Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
using RaceElement.HUD.Overlay.Configuration;

namespace RaceElement.HUD.Common.Overlays.Driving.DualSense;

internal sealed class DualSenseConfiguration : OverlayConfiguration
{
public DualSenseConfiguration()
{
GenericConfiguration.AlwaysOnTop = false;
GenericConfiguration.Window = false;
GenericConfiguration.Opacity = 1.0f;
GenericConfiguration.AllowRescale = false;
}


[ConfigGrouping("Brake Slip", "Adjust the slip effect whilst applying the brakes.")]
public BrakeSlipHaptics BrakeSlip { get; init; } = new();
public sealed class BrakeSlipHaptics
{
/// <summary>
/// The brake in percentage (divide by 100f if you want 0-1 value)
/// </summary>
[ToolTip("The minimum brake percentage before any effects are applied. See this like a deadzone.")]
[FloatRange(0.1f, 99f, 0.1f, 1)]
public float BrakeThreshold { get; init; } = 3f;

[FloatRange(0.05f, 6f, 0.002f, 3)]
public float FrontSlipThreshold { get; init; } = 0.25f;

[FloatRange(0.05f, 6f, 0.002f, 3)]
public float RearSlipThreshold { get; init; } = 0.25f;

[ToolTip("Higher is stronger dynamic feedback.")]
[IntRange(1, 8, 1)]
public int FeedbackStrength { get; init; } = 3;

[ToolTip("Sets the min frequency of the vibration effect in the trigger.")]
[IntRange(1, 10, 1)]
public int MinFrequency { get; init; } = 6;

[ToolTip("Sets the max frequency of the vibration effect in the trigger.")]
[IntRange(20, 150, 1)]
public int MaxFrequency { get; init; } = 100;

[ToolTip("Change the amplitude(strength) of the vibration effect in the trigger.")]
[IntRange(1, 8, 1)]
public int Amplitude { get; init; } = 6;
}

[ConfigGrouping("Throttle Slip", "Adjust the slip effect whilst applying the throttle.\nModify the threshold to increase or decrease sensitivity in different situations.")]
public ThrottleSlipHaptics ThrottleSlip { get; init; } = new();
public sealed class ThrottleSlipHaptics
{
/// <summary>
/// The throttle in percentage (divide by 100f if you want 0-1 value)
/// </summary>
[ToolTip("The minimum throttle percentage before any effects are applied. See this like a deadzone.")]
[FloatRange(0.1f, 99f, 0.1f, 1)]
public float ThrottleThreshold { get; init; } = 3f;

[ToolTip("Decrease this treshold to increase the sensitivity when the front wheels slip (understeer).")]
[FloatRange(0.05f, 6f, 0.002f, 3)]
public float FrontSlipThreshold { get; init; } = 0.35f;

[ToolTip("Decrease this treshold to increase the sensitivity when the rear wheels slip (oversteer).")]
[FloatRange(0.05f, 10f, 0.002f, 3)]
public float RearSlipThreshold { get; init; } = 0.25f;

[ToolTip("Higher is stronger dynamic feedback.")]
[IntRange(1, 8, 1)]
public int FeedbackStrength { get; init; } = 2;

[ToolTip("Sets the min frequency of the vibration effect in the trigger.")]
[IntRange(1, 10, 1)]
public int MinFrequency { get; init; } = 6;

[ToolTip("Sets the max frequency of the vibration effect in the trigger.")]
[IntRange(20, 150, 1)]
public int MaxFrequency { get; init; } = 100;

[ToolTip("Change the amplitude(strength) of the vibration effect in the trigger.")]
[IntRange(1, 8, 1)]
public int Amplitude { get; init; } = 7;
}

}
17 changes: 17 additions & 0 deletions Race Element.HUD.Common/Overlays/Driving/DualSense/DualSenseJob.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using RaceElement.Core.Jobs.Loop;
using static RaceElement.HUD.Common.Overlays.Driving.DualSense.Resources;

namespace RaceElement.HUD.Common.Overlays.Driving.DualSense;

internal sealed class HapticsJob(DualSenseOverlay overlay) : AbstractLoopJob
{
public sealed override void RunAction()
{
if (!overlay.ShouldRender())
return;

TriggerHaptics.HandleAcceleration(overlay._config);
TriggerHaptics.HandleBraking(overlay._config);
}
public override void AfterCancel() { ds5w_set_trigger_effect_off(0); ds5w_set_trigger_effect_off(1); }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
using RaceElement.Data.Games;
using RaceElement.HUD.Overlay.Internal;
using System.Diagnostics;
using System.Drawing;
using System.Reflection;
using System.Text;

using static RaceElement.HUD.Common.Overlays.Driving.DualSense.Resources;

namespace RaceElement.HUD.Common.Overlays.Driving.DualSense;

[Overlay(Name = "DualSense",
Description = "Adds trigger effects to the DualSense Controller.\n See Guide in the Discord of Race Element for instructions.",
OverlayCategory = OverlayCategory.Inputs,
OverlayType = OverlayType.Drive,
Game = Game.RaceRoom | Game.AssettoCorsa1 | Game.AssettoCorsaEvo,
Authors = ["Reinier Klarenberg", "Guillaume Stordeur"]
)]
internal sealed class DualSenseOverlay : CommonAbstractOverlay
{
internal readonly DualSenseConfiguration _config = new();
private HapticsJob _hapticsJob;

public DualSenseOverlay(Rectangle rectangle) : base(rectangle, "DualSense")
{
Width = 1; Height = 1;
RefreshRateHz = 1;
AllowReposition = false;
}

public sealed override void BeforeStart()
{
if (IsPreviewing) return;

ExtractDs5ApiDll();

ds5w_init();
_hapticsJob = new HapticsJob(this) { IntervalMillis = 1000 / 200 };
_hapticsJob.Run();
}
public sealed override void BeforeStop()
{
if (IsPreviewing) return;

_hapticsJob?.CancelJoin();

ds5w_shutdown();
}

/// <summary>
/// Extracts the embbed dll in this namespace folder, to the executing assembly folder.
/// only extracts if it does not exists. Does not check for version!!!
/// </summary>
private static void ExtractDs5ApiDll()
{
string dllPath = Path.Combine(AppContext.BaseDirectory, Path.GetFileName("ds5w_x64.dll"));
FileInfo dllFile = new(dllPath);
if (dllFile.Exists) return;

string resourceName = "RaceElement.HUD.Common.Overlays.Driving.DualSense.ds5w_x64.dll";
var assembly = Assembly.GetExecutingAssembly();
using (var stream = assembly.GetManifestResourceStream(resourceName))
{
if (stream == null)
throw new FileNotFoundException($"Could not find resource: {resourceName}");

using var fileStream = dllFile.Open(FileMode.Create, FileAccess.Write);
stream.CopyTo(fileStream);
stream.Close();
fileStream.Close();
}
}

public sealed override bool ShouldRender() => DefaultShouldRender() && !IsPreviewing;

public sealed override void Render(Graphics g) { }

}
24 changes: 24 additions & 0 deletions Race Element.HUD.Common/Overlays/Driving/DualSense/Resources.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System.Runtime.InteropServices;

namespace RaceElement.HUD.Common.Overlays.Driving.DualSense;

internal static partial class Resources
{
[LibraryImport("ds5w_x64.dll", SetLastError = true)]
public static partial int ds5w_init();

[LibraryImport("ds5w_x64.dll", SetLastError = true)]
public static partial void ds5w_shutdown();

[LibraryImport("ds5w_x64.dll", SetLastError = true)]
public static partial int ds5w_set_trigger_effect_off(int left);

[LibraryImport("ds5w_x64.dll", SetLastError = true)]
public static partial int ds5w_set_trigger_effect_vibration(int left, int pos, int amp, int freq);

[LibraryImport("ds5w_x64.dll", SetLastError = true)]
public static partial int ds5w_set_trigger_effect_feedback(int left, int pos, int strength);

[LibraryImport("ds5w_x64.dll", SetLastError = true)]
public static partial int ds5w_set_trigger_effect_weapon(int left, int start, int end, int strength);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
using RaceElement.Data.Common;
using RaceElement.Util.SystemExtensions;
using static RaceElement.HUD.Common.Overlays.Driving.DualSense.Resources;

namespace RaceElement.HUD.Common.Overlays.Driving.DualSense;

internal static class TriggerHaptics
{
public static void HandleBraking(DualSenseConfiguration config)
{
// TODO: add either an option to threshold it on brake input or based on some curve?
if (SimDataProvider.LocalCar.Inputs.Brake <= config.BrakeSlip.BrakeThreshold / 100f)
{
ds5w_set_trigger_effect_off(1);
return;
}
float[] slipRatios = SimDataProvider.LocalCar.Tyres.SlipRatio;
if (slipRatios.Length != 4)
{
ds5w_set_trigger_effect_off(1);
return;
}

float slipRatioFront = Math.Max(slipRatios[0], slipRatios[1]);
float slipRatioRear = Math.Max(slipRatios[2], slipRatios[3]);

// TODO: add option for front and rear ratio threshold.
if (slipRatioFront > config.BrakeSlip.FrontSlipThreshold || slipRatioRear > config.BrakeSlip.RearSlipThreshold)
{
float frontslipCoefecient = slipRatioFront * 4f;
frontslipCoefecient.ClipMax(7.5f);

float rearSlipCoefecient = slipRatioFront * 2f;
rearSlipCoefecient.ClipMax(5f);

float magicValue = frontslipCoefecient + rearSlipCoefecient;
float percentage = magicValue * 1.0f / 12.5f;

if (percentage >= 0.05f)
{
ds5w_set_trigger_effect_feedback(1, 0, (int)(config.BrakeSlip.FeedbackStrength * percentage));
Thread.Sleep((int)(1000f / 250));
ds5w_set_trigger_effect_off(1);
}

int freq = (int)(config.BrakeSlip.MaxFrequency * percentage);
freq.ClipMin(config.BrakeSlip.MinFrequency);
ds5w_set_trigger_effect_vibration(1, 0, config.BrakeSlip.Amplitude, freq);
Thread.Sleep((int)(1000f / 250 * 4));
}
else
ds5w_set_trigger_effect_off(1);
}

public static void HandleAcceleration(DualSenseConfiguration config)
{
if (SimDataProvider.LocalCar.Inputs.Throttle <= config.ThrottleSlip.ThrottleThreshold / 100f)
{
ds5w_set_trigger_effect_off(0);
return;
}
float[] slipRatios = SimDataProvider.LocalCar.Tyres.SlipRatio;
if (slipRatios.Length != 4)
{
ds5w_set_trigger_effect_off(0);
return;
}

float slipRatioFront = Math.Max(slipRatios[0], slipRatios[1]);
float slipRatioRear = Math.Max(slipRatios[2], slipRatios[3]);

if (slipRatioFront > config.ThrottleSlip.FrontSlipThreshold || slipRatioRear > config.ThrottleSlip.RearSlipThreshold)
{
float frontslipCoefecient = slipRatioFront * 3f;
frontslipCoefecient.ClipMax(5);
float rearSlipCoefecient = slipRatioFront * 5f;
rearSlipCoefecient.ClipMax(7.5f);

float magicValue = frontslipCoefecient + rearSlipCoefecient;
float percentage = magicValue * 1.0f / 12.5f;
if (percentage >= 0.05f)
{
ds5w_set_trigger_effect_feedback(0, 0, (int)(config.ThrottleSlip.FeedbackStrength * percentage));
Thread.Sleep((int)(1000f / 250));
ds5w_set_trigger_effect_off(0);
}

int freq = (int)(config.ThrottleSlip.MaxFrequency * percentage);
freq.ClipMin(config.ThrottleSlip.MinFrequency);
ds5w_set_trigger_effect_vibration(0, 0, config.ThrottleSlip.Amplitude, freq);
Thread.Sleep((int)(1000f / 250 * 4));
}
else
ds5w_set_trigger_effect_off(0);
}
}
Binary file not shown.
9 changes: 7 additions & 2 deletions Race Element.HUD.Common/Race Element.HUD.Common.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@
<IsTrimmable>True</IsTrimmable>
<IsAotCompatible>True</IsAotCompatible>
</PropertyGroup>
<ItemGroup>
<None Remove="Overlays\Driving\DualSense\ds5w_x64.dll" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Overlays\Driving\DualSense\ds5w_x64.dll" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="EdCanHack.Unglide" Version="2.0.0" />
Expand All @@ -36,7 +42,6 @@
</ItemGroup>

<ItemGroup>
<Folder Include="Overlays\System\" />
<Folder Include="Overlays\System\" />
</ItemGroup>

</Project>