Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
8d92d57
added first test of new HUD that sends trigger effects to dualsense c…
kamih Jan 25, 2025
7f69c46
embed ds5w_x64.dll
RiddleTime Jan 25, 2025
1615691
Update DualSenseJob.cs
RiddleTime Jan 25, 2025
09c796c
Update DualSenseOverlay.cs
RiddleTime Jan 25, 2025
ad4f4c8
Update TriggerHaptics.cs
RiddleTime Jan 25, 2025
3615968
Update DualSenseConfiguration.cs
RiddleTime Jan 25, 2025
7231caf
Update TriggerHaptics.cs
RiddleTime Jan 25, 2025
23b6b8f
Update TriggerHaptics.cs
RiddleTime Jan 25, 2025
d7ad4ad
revert tripperhaptics to vibration only
RiddleTime Jan 25, 2025
135b34d
added ds5w_set_trigger_effect_feedback and ds5w_set_trigger_effect_we…
kamih Jan 25, 2025
6b02396
Update TriggerHaptics.cs
RiddleTime Jan 25, 2025
a660d5d
added new math utility functions to FloatExtensions, different trigge…
kamih Jan 26, 2025
721282d
added ds5w_batch_begin, ds5w_batch_end, and ds5w_set_rumble API funct…
kamih Jan 27, 2025
8d83193
added suspensionTravel and kerb/slip/g/abs vibration telemetry data t…
kamih Jan 27, 2025
457dcd7
added rumble settings to DualSense tab, set left rumble from curb vib…
kamih Jan 27, 2025
861bfb2
hide Rumble config group for RaceRoom and AC1
kamih Jan 27, 2025
1c1fe67
rename: FreqAtMinSlipRatio -> LowSlipFrequency, FreqAtMaxSlipRatio ->…
kamih Jan 27, 2025
e986f44
update lastPhysicsPacketId in AssettoCorsaEvoDataProvider::Update
kamih Jan 29, 2025
b4b2bcf
added SlipRatioBias to ThrottleSlipHaptics and BrakeSlipHaptics
kamih Jan 29, 2025
9d0532f
added damage, abs, gear shift -> left rumble motor, rpm -> right rumb…
kamih Feb 3, 2025
57c1ea5
Merge branch 'dev' into dualsense_v2
RiddleTime Feb 3, 2025
c4b0e8d
Merge branch 'dev' into dualsense_v2
RiddleTime Feb 4, 2025
0dfcf6e
Merge branch 'dev' into dualsense_v2
RiddleTime Feb 6, 2025
6acea23
Merge branch 'dev' into dualsense_v2
RiddleTime Mar 7, 2025
9d06ec7
Merge branch 'dev' into dualsense_v2
RiddleTime Mar 14, 2025
0664d95
Merge branch 'dev' into dualsense_v2
RiddleTime Mar 24, 2025
1e907f7
Merge branch 'dev' into dualsense_v2
RiddleTime Jul 31, 2025
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
18 changes: 18 additions & 0 deletions Race Element.Data/Common/SimulatorData/LocalCar/LocalCarData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,24 @@ public sealed record PhysicsData
/// The speed of the car in kilometers per hour.
/// </summary>
public float Velocity { get; internal set; }

/// <summary>Wheel suspension travel.</summary>
public float[] SuspensionTravel { get; internal set; } = new float[4];

/// <summary>Damage to each side.</summary>
public float[] Damage { get; internal set; } = new float[5];

/// <summary>Vibrations generated from driving on curbs and off track.</summary>
public float KerbVibration;

/// <summary>Vibrations generated from tire slip.</summary>
public float SlipVibrations;

/// <summary>Vibrations generated from g-forces.</summary>
public float Gvibrations;

/// <summary>Vibrations generated from brake ABS.</summary>
public float AbsVibrations;
}
public sealed record InputsData
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ internal sealed class AssettoCorsaEvoDataProvider : AbstractSimDataProvider
public sealed override void Update(ref LocalCarData localCar, ref SessionData sessionData, ref GameData gameData)
{
var physicsPage = AcEvoSharedMemory.Instance.ReadPhysicsPageFile();
// no need to remap the physics page if packet is the same
if (lastPhysicsPacketId == physicsPage.PacketId) // no need to remap the physics page if packet is the same
{
lastPhysicsPacketId = physicsPage.PacketId;
Expand All @@ -34,7 +35,7 @@ public sealed override void Update(ref LocalCarData localCar, ref SessionData se
lastPhysicsPacketId = physicsPage.PacketId;
SimDataProvider.GameData.IsGamePaused = false;
}

LocalCarMapper.AddPhysics(ref physicsPage, ref localCar, ref sessionData);

gameData.Name = GameName;
Expand All @@ -49,6 +50,7 @@ public sealed override void Update(ref LocalCarData localCar, ref SessionData se
//SessionData.Instance.PlayerCarIndex = graphicsPage.PlayerCarID;
//SimDataProvider.LocalCar.CarModel.CarClass = dummyCarClass;

lastPhysicsPacketId = physicsPage.PacketId;
}

//private LogFileJob _logFileJob;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,17 @@ internal static partial class LocalCarMapper
{
internal static void AddPhysics(ref SPageFilePhysics pagePhysics, ref LocalCarData commonData, ref SessionData sessionData)
{
commonData.Physics.SuspensionTravel = pagePhysics.SuspensionTravel;
commonData.Physics.KerbVibration = pagePhysics.KerbVibration;
commonData.Physics.SlipVibrations = pagePhysics.SlipVibrations;
commonData.Physics.Gvibrations = pagePhysics.Gvibrations;
commonData.Physics.AbsVibrations = pagePhysics.AbsVibrations;
commonData.Physics.Acceleration = new(pagePhysics.AccG[0], pagePhysics.AccG[1], pagePhysics.AccG[2]);
commonData.Physics.Damage = pagePhysics.CarDamage;

commonData.Engine.IsPitLimiterOn = pagePhysics.PitLimiterOn;
commonData.Engine.MaxRpm = pagePhysics.CurrentMaxRpm;
commonData.Engine.Rpm = pagePhysics.Rpms;

commonData.Engine.IsRunning = commonData.Engine.Rpm > 0;

commonData.Inputs.Steering = pagePhysics.SteerAngle;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -691,16 +691,16 @@ public sealed class SPageFilePhysics
/// <summary>Engine running?</summary>
[MarshalAs(UnmanagedType.Bool)] public bool IsEngineRunning;

/// <summary>Vibrations sent to the FFB, could be used for motion rigs</summary>
/// <summary>Vibrations generated from driving on curbs and off track.</summary>
public float KerbVibration;

/// <summary>Vibrations sent to the FFB, could be used for motion rigs</summary>
/// <summary>Vibrations generated from tire slip.</summary>
public float SlipVibrations;

/// <summary>Vibrations sent to the FFB, could be used for motion rigs</summary>
/// <summary>Vibrations generated from g-forces.</summary>
public float Gvibrations;

/// <summary>Vibrations sent to the FFB, could be used for motion rigs</summary>
/// <summary>Vibrations generated from brake ABS.</summary>
public float AbsVibrations;

public static readonly int Size = Marshal.SizeOf(typeof(SPageFilePhysics));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
using RaceElement.HUD.Overlay.Configuration;
using RaceElement.Data.Games;

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;
}

[HideForGame(Game.RaceRoom | Game.AssettoCorsa1)]
[ConfigGrouping("Rumble", "Adjust the rumble effects.")]
public RumbleParams Rumble { get; init; } = new();
public sealed class RumbleParams
{
/// <summary>
/// Kerb rumble coef
/// </summary>
[ToolTip("Kerb rumble coef.")]
[FloatRange(0.0f, 2.0f, 0.01f, 2)]
public float KerbCoef { get; init; } = 1.2f;

/// <summary>
/// ABS rumble coef
/// </summary>
[ToolTip("ABS rumble coef.")]
[FloatRange(0.0f, 2.0f, 0.01f, 2)]
public float ABSCoef { get; init; } = 1f;

/// <summary>
/// Damage rumble coef
/// </summary>
[ToolTip("Damage rumble coef.")]
[FloatRange(0.0f, 100.0f, 0.01f, 2)]
public float DamageCoef { get; init; } = 60f;

/// <summary>
/// Gear shift rumble coef
/// </summary>
[ToolTip("Gear shift rumble coef.")]
[FloatRange(0.0f, 100.0f, 0.01f, 2)]
public float GearCoef { get; init; } = 32f;

/// <summary>
/// How slowly rumble accum decays (low = fast)
/// </summary>
[ToolTip("How fast rumble accum decays. (low = fast)")]
[FloatRange(0.5f, 0.9f, 0.01f, 2)]
public float AccumDecay { get; init; } = 0.7f;

/// <summary>
/// RPM % at which rumble will start
/// </summary>
[ToolTip("RPM % at which right rumble will start.")]
[FloatRange(0.0f, 1.0f, 0.01f, 2)]
public float RPMStart { get; init; } = 0.9f;

/// <summary>
/// RPM right rumble coef
/// </summary>
[ToolTip("RPM right rumble coef.")]
[FloatRange(0.0f, 1.0f, 0.01f, 2)]
public float RPMCoef { get; init; } = 0.5f;
}

[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;

[ToolTip("Front/back slip ratio balance to feed into effect. Front[0..1]Back")]
[FloatRange(0.0f, 1.0f, 0.002f, 3)]
public float SlipRatioBias { get; init; } = 0.5f;

[ToolTip("Minimum slip ratio at which the effect will start.")]
[FloatRange(0.0f, 1.0f, 0.002f, 3)]
public float MinSlipRatio { get; init; } = 0.240f;

[ToolTip("Maximum slip ratio after which frequency of effect will be clamped.")]
[FloatRange(1.0f, 10.0f, 0.002f, 3)]
public float MaxSlipRatio { get; init; } = 2.0f;

[ToolTip("Sets the frequency of the trigger vibration effect when slip ratio is <= MinSlipRatio.")]
[FloatRange(10.0f, 255.0f, 0.5f, 2)]
public float MinSlipFrequency { get; init; } = 150.0f;

[ToolTip("Sets the frequency of the trigger vibration effect when slip ratio is >= MaxSlipRatio.")]
[FloatRange(10.0f, 255.0f, 0.5f, 2)]
public float MaxSlipFrequency { get; init; } = 20.0f;

[ToolTip("Gain for the effect amplitude as SlipRatio increases.")]
[FloatRange(1.0f, 10.0f, 0.1f, 1)]
public float AmpGain { get; init; } = 2.8f;

[ToolTip("Maximum effect amplitude.")]
[IntRange(1, 8, 1)]
public int MaxAmp { get; init; } = 3;
}

[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("Front/back slip ratio balance to feed into effect. Front[0..1]Back")]
[FloatRange(0.0f, 1.0f, 0.002f, 3)]
public float SlipRatioBias { get; init; } = 0.5f;

[ToolTip("Minimum slip ratio at which the effect will start.")]
[FloatRange(0.0f, 1.0f, 0.002f, 3)]
public float MinSlipRatio { get; init; } = 0.5f;

[ToolTip("Maximum slip ratio after which frequency of effect will be clamped.")]
[FloatRange(1.0f, 10.0f, 0.002f, 3)]
public float MaxSlipRatio { get; init; } = 2.5f;

[ToolTip("Sets the frequency of the trigger vibration effect when slip ratio is <= MinSlip.")]
[FloatRange(10.0f, 255.0f, 0.5f, 2)]
public float MinSlipFrequency { get; init; } = 165.0f;

[ToolTip("Sets the frequency of the trigger vibration effect when slip ratio is >= MaxSlip.")]
[FloatRange(10.0f, 255.0f, 0.5f, 2)]
public float MaxSlipFrequency { get; init; } = 20.0f;

[ToolTip("Gain for the effect amplitude as SlipRatio increases.")]
[FloatRange(1.0f, 10.0f, 0.1f, 1)]
public float AmpGain { get; init; } = 2.5f;

[ToolTip("Maximum effect amplitude.")]
[IntRange(1, 8, 1)]
public int MaxAmp { get; init; } = 3;
}

}
28 changes: 28 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,28 @@
using RaceElement.Core.Jobs.Loop;
using RaceElement.Data.Common;
using RaceElement.Data.Games;

using static RaceElement.HUD.Common.Overlays.Driving.DualSense.DS5W;
using static RaceElement.HUD.Common.Overlays.Driving.DualSense.Util;

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

internal sealed class DualSenseJob(DualSenseOverlay overlay) : AbstractLoopJob
{
private TriggerHaptics _triggerHaptics = new();
public sealed override void RunAction()
{
//if (!overlay.ShouldRender())
// return;
ds5w_batch_begin();
_triggerHaptics.HandleAcceleration(overlay._config.ThrottleSlip);
_triggerHaptics.HandleBraking(overlay._config.BrakeSlip);
if (!GameManager.CurrentGame.HasFlag(Game.RaceRoom) &&
!GameManager.CurrentGame.HasFlag(Game.AssettoCorsa1))
_triggerHaptics.HandleRumble(overlay._config.Rumble);
ds5w_batch_end();
}
public override void AfterCancel()
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
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.DS5W;

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 DualSenseJob _DualSenseJob;

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();
_DualSenseJob = new DualSenseJob(this) { IntervalMillis = 1000 / 200 };
_DualSenseJob.Run();
}
public sealed override void BeforeStop()
{
if (IsPreviewing) return;

_DualSenseJob?.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) { }

}
44 changes: 44 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,44 @@
using System.Diagnostics;
using System.Runtime.InteropServices;

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

internal class Util
{
public static void DebugOut(string msg)
{
StackTrace st = new StackTrace(false);
string caller = st.GetFrame(1).GetMethod().Name;
Debug.WriteLine(caller + ": " + msg);
}
}

internal static partial class DS5W
{
[LibraryImport("ds5w_x64.dll")]
public static partial int ds5w_init();

[LibraryImport("ds5w_x64.dll")]
public static partial void ds5w_shutdown();

[LibraryImport("ds5w_x64.dll")]
public static partial void ds5w_batch_begin();

[LibraryImport("ds5w_x64.dll")]
public static partial int ds5w_batch_end();

[LibraryImport("ds5w_x64.dll")]
public static partial int ds5w_set_rumble(int left, int strength);

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

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

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

[LibraryImport("ds5w_x64.dll")]
public static partial int ds5w_set_trigger_effect_weapon(int left, int start, int end, int strength);
}
Loading