Refactor CRayTrace

This commit is contained in:
SlynxCZ 2026-04-14 22:01:27 +02:00
parent 12585b4161
commit 51afd30a0a
5 changed files with 459 additions and 481 deletions

View file

@ -11,164 +11,199 @@ namespace RayTraceAPI
{ {
#region Native Structs (matching C++ layout exactly) #region Native Structs (matching C++ layout exactly)
[Flags] [Flags]
public enum InteractionLayers: ulong public enum InteractionLayers : ulong
{ {
None = 0, None = 0,
Solid = 0x1, Solid = 0x1,
Hitboxes = 0x2, Hitboxes = 0x2,
Trigger = 0x4, Trigger = 0x4,
Sky = 0x8, Sky = 0x8,
PlayerClip = 0x10, PlayerClip = 0x10,
NPCClip = 0x20, NPCClip = 0x20,
BlockLOS = 0x40, BlockLOS = 0x40,
BlockLight = 0x80, BlockLight = 0x80,
Ladder = 0x100, Ladder = 0x100,
Pickup = 0x200, Pickup = 0x200,
BlockSound = 0x400, BlockSound = 0x400,
NoDraw = 0x800, NoDraw = 0x800,
Window = 0x1000, Window = 0x1000,
PassBullets = 0x2000, PassBullets = 0x2000,
WorldGeometry = 0x4000, WorldGeometry = 0x4000,
Water = 0x8000, Water = 0x8000,
Slime = 0x10000, Slime = 0x10000,
TouchAll = 0x20000, TouchAll = 0x20000,
Player = 0x40000, Player = 0x40000,
NPC = 0x80000, NPC = 0x80000,
Debris = 0x100000, Debris = 0x100000,
Physics_Prop = 0x200000, Physics_Prop = 0x200000,
NavIgnore = 0x400000, NavIgnore = 0x400000,
NavLocalIgnore = 0x800000, NavLocalIgnore = 0x800000,
PostProcessingVolume = 0x1000000, PostProcessingVolume = 0x1000000,
UnusedLayer3 = 0x2000000, UnusedLayer3 = 0x2000000,
CarriedObject = 0x4000000, CarriedObject = 0x4000000,
PushAway = 0x8000000, PushAway = 0x8000000,
ServerEntityOnClient = 0x10000000, ServerEntityOnClient = 0x10000000,
CarriedWeapon = 0x20000000, CarriedWeapon = 0x20000000,
StaticLevel = 0x40000000, StaticLevel = 0x40000000,
csgo_team1 = 0x80000000, csgo_team1 = 0x80000000,
csgo_team2 = 0x100000000, csgo_team2 = 0x100000000,
csgo_grenadeclip = 0x200000000, csgo_grenadeclip = 0x200000000,
csgo_droneclip = 0x400000000, csgo_droneclip = 0x400000000,
csgo_moveable = 0x800000000, csgo_moveable = 0x800000000,
csgo_opaque = 0x1000000000, csgo_opaque = 0x1000000000,
csgo_monster = 0x2000000000, csgo_monster = 0x2000000000,
csgo_thrown_grenade = 0x8000000000, csgo_thrown_grenade = 0x8000000000,
MASK_SHOT_PHYSICS = Solid | PlayerClip | Window | PassBullets | Player | NPC | Physics_Prop, MASK_SHOT_PHYSICS = Solid | PlayerClip | Window | PassBullets | Player | NPC | Physics_Prop,
MASK_SHOT_HITBOX = Hitboxes | Player | NPC, MASK_SHOT_HITBOX = Hitboxes | Player | NPC,
MASK_SHOT_FULL = MASK_SHOT_PHYSICS | Hitboxes, MASK_SHOT_FULL = MASK_SHOT_PHYSICS | Hitboxes,
MASK_WORLD_ONLY = Solid | Window | PassBullets, MASK_WORLD_ONLY = Solid | Window | PassBullets,
MASK_GRENADE = Solid | Window | Physics_Prop | PassBullets, MASK_GRENADE = Solid | Window | Physics_Prop | PassBullets,
MASK_BRUSH_ONLY = Solid | Window, MASK_BRUSH_ONLY = Solid | Window,
MASK_PLAYER_MOVE = Solid | Window | PlayerClip | PassBullets, MASK_PLAYER_MOVE = Solid | Window | PlayerClip | PassBullets,
MASK_NPC_MOVE = Solid | Window | NPCClip | PassBullets MASK_NPC_MOVE = Solid | Window | NPCClip | PassBullets
} }
[StructLayout(LayoutKind.Explicit, Size = 32)] [StructLayout(LayoutKind.Sequential, Pack = 8)]
public unsafe struct TraceOptions public struct TraceOptions
{ {
[FieldOffset(0)] public ulong InteractsAs; public ulong InteractsAs;
[FieldOffset(8)] public ulong InteractsWith; public ulong InteractsWith;
[FieldOffset(16)] public ulong InteractsExclude; public ulong InteractsExclude;
[FieldOffset(24)] public int DrawBeam; public int DrawBeam;
public TraceOptions() private int _pad;
{
InteractsAs = 0;
InteractsWith = (ulong)InteractionLayers.MASK_SHOT_PHYSICS;
InteractsExclude = 0;
DrawBeam = 0;
}
public TraceOptions(InteractionLayers interactsAs, InteractionLayers interactsWith, InteractionLayers interactsExclude = 0, bool drawBeam = false) public TraceOptions()
{ {
InteractsAs = (ulong)interactsAs; InteractsAs = 0;
InteractsWith = (ulong)interactsWith; InteractsWith = (ulong)InteractionLayers.MASK_SHOT_PHYSICS;
InteractsExclude = (ulong)interactsExclude; InteractsExclude = 0;
DrawBeam = drawBeam ? 1 : 0; DrawBeam = 0;
} }
}
[StructLayout(LayoutKind.Explicit, Size = 136)] public TraceOptions(InteractionLayers interactsAs, InteractionLayers interactsWith,
public unsafe struct TraceResult InteractionLayers interactsExclude = 0, bool drawBeam = false)
{ {
[FieldOffset(0)] public float StartPosX; InteractsAs = (ulong)interactsAs;
[FieldOffset(4)] public float StartPosY; InteractsWith = (ulong)interactsWith;
[FieldOffset(8)] public float StartPosZ; InteractsExclude = (ulong)interactsExclude;
DrawBeam = drawBeam ? 1 : 0;
}
}
[FieldOffset(12)] public float EndPosX; [StructLayout(LayoutKind.Sequential)]
[FieldOffset(16)] public float EndPosY; public unsafe struct TraceResult
[FieldOffset(20)] public float EndPosZ; {
public Vector3 StartPos;
public Vector3 EndPos;
public Vector3 HitPoint;
public Vector3 Normal;
[FieldOffset(24)] public float HitPointX; public float Fraction;
[FieldOffset(28)] public float HitPointY; public float HitOffset;
[FieldOffset(32)] public float HitPointZ;
[FieldOffset(36)] public float NormalX; public int TriangleIndex;
[FieldOffset(40)] public float NormalY; public short HitboxBoneIndex;
[FieldOffset(44)] public float NormalZ; private short _pad;
[FieldOffset(48)] public float Fraction; public int Contents;
[FieldOffset(52)] public float HitOffset; public int RayType;
[FieldOffset(56)] public int TriangleIndex; public bool StartInSolid;
[FieldOffset(60)] public int HitboxBoneIndex; public bool ExactHitPoint;
[FieldOffset(64)] public int Contents; public nint HitEntity;
[FieldOffset(68)] public int RayType; public nint Hitbox;
public nint Surface;
public nint Body;
public nint Shape;
[FieldOffset(72)] public int AllSolid; public nint BodyTransform;
[FieldOffset(76)] public int ExactHitPoint; public nint ShapeAttributes;
// pointer section public readonly bool DidHit() => Fraction < 1.0f;
[FieldOffset(80)] public nint HitEntityPtr; public readonly bool IsAllSolid() => StartInSolid;
[FieldOffset(88)] public nint HitboxPtr; public readonly bool HasExactHit() => ExactHitPoint;
[FieldOffset(96)] public nint SurfacePropsPtr;
[FieldOffset(104)] public nint BodyHandle;
[FieldOffset(112)] public nint ShapeHandle;
// thread safe heap pointer section public readonly int GetTriangleIndex() => TriangleIndex;
[FieldOffset(120)] public nint BodyTransformPtr; public readonly int GetHitboxBoneIndex() => HitboxBoneIndex;
[FieldOffset(128)] public nint ShapeAttributesPtr; public readonly int GetContents() => Contents;
public readonly int GetRayType() => RayType;
public unsafe ref T GetRef<T>(nint ptr) where T : unmanaged public readonly Vector3 GetStartPos() => StartPos;
{ public readonly Vector3 GetEndPos() => EndPos;
if (ptr == 0) public readonly Vector3 GetHitPoint() => HitPoint;
throw new NullReferenceException(); public readonly Vector3 GetNormal() => Normal;
return ref *(T*)ptr; public readonly float GetFraction() => Fraction;
} public readonly float GetHitOffset() => HitOffset;
public T? GetObject<T>(nint ptr) where T : class public readonly T* GetPtr<T>(nint ptr) where T : unmanaged
{ {
if (ptr == 0) return (T*)ptr;
return null; }
return (T)Activator.CreateInstance(typeof(T), ptr)!; public readonly ref T GetRef<T>(nint ptr) where T : unmanaged
} {
if (ptr == 0)
throw new NullReferenceException();
public Vector3 StartPos => new(StartPosX, StartPosY, StartPosZ); return ref *(T*)ptr;
public Vector3 EndPos => new(EndPosX, EndPosY, EndPosZ); }
public Vector3 HitPoint => new(HitPointX, HitPointY, HitPointZ);
public Vector3 Normal => new(NormalX, NormalY, NormalZ);
public bool DidHit => Fraction < 1.0f; public readonly bool TryGetRef<T>(nint ptr, out T* value) where T : unmanaged
public bool IsAllSolid => AllSolid != 0; {
public bool HasExactHit => ExactHitPoint != 0; if (ptr == 0)
{
value = null;
return false;
}
public CEntityInstance? HitEntity => GetObject<CEntityInstance>(HitEntityPtr); value = (T*)ptr;
} return true;
}
public readonly CEntityInstance HitEntityPtr() => new(HitEntity);
public readonly CTransform BodyTransformPtr() => new(BodyTransform);
}
[StructLayout(LayoutKind.Sequential)]
public struct QAngle
{
public float X;
public float Y;
public float Z;
}
#endregion #endregion
public interface CRayTraceInterface public interface CRayTraceInterface
{ {
public unsafe bool TraceShape(Vector origin, QAngle angles, CBaseEntity? ignoreEntity, TraceOptions options, TraceResult TraceShape(
out TraceResult result); in Vector3 start,
in QAngle angles,
CEntityInstance ignoreEntity,
in TraceOptions options);
public unsafe bool TraceEndShape(Vector origin, Vector endOrigin, CBaseEntity? ignoreEntity, TraceResult TraceEndShape(
TraceOptions options, out TraceResult result); in Vector3 start,
in Vector3 end,
CEntityInstance ignoreEntity,
in TraceOptions options);
public unsafe bool TraceHullShape(Vector vecStart, Vector vecEnd, Vector hullMins, Vector hullMaxs, TraceResult TraceHullShape(
CBaseEntity? ignoreEntity, TraceOptions options, out TraceResult result); in Vector3 start,
in Vector3 end,
in Vector3 mins,
in Vector3 maxs,
CEntityInstance ignoreEntity,
in TraceOptions options);
TraceResult TraceShapeEx(
in Vector3 start,
in Vector3 end,
nint traceFilter,
nint ray);
} }
} }

View file

@ -3,7 +3,9 @@
// Copyright (c) 2025 slynxcz. All rights reserved. // Copyright (c) 2025 slynxcz. All rights reserved.
// //
using System.Buffers; using System.Buffers;
using System.Numerics;
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Loader; using System.Runtime.Loader;
using System.Text; using System.Text;
@ -17,6 +19,8 @@ using CounterStrikeSharp.API.Modules.Commands;
using CounterStrikeSharp.API.Modules.Memory; using CounterStrikeSharp.API.Modules.Memory;
using CounterStrikeSharp.API.Modules.Utils; using CounterStrikeSharp.API.Modules.Utils;
using RayTraceAPI; using RayTraceAPI;
using QAngle = RayTraceAPI.QAngle;
using Vector = CounterStrikeSharp.API.Modules.Utils.Vector;
namespace RayTraceImpl; namespace RayTraceImpl;
@ -54,112 +58,122 @@ public class RayTraceImpl : BasePlugin
public class CRayTrace : CRayTraceInterface public class CRayTrace : CRayTraceInterface
{ {
public unsafe bool TraceShape(Vector origin, QAngle angles, CBaseEntity? ignoreEntity, TraceOptions options, out TraceResult result) public TraceResult TraceShape(in Vector3 start, in QAngle angles, CEntityInstance ignore, in TraceOptions options)
{ {
result = default; unsafe
{
if (!NativeBridge.Loaded)
return default;
if (!NativeBridge.m_bRayTraceLoaded || NativeBridge.m_pRayTraceHandle == nint.Zero) return NativeBridge.TraceShape!(
return false; NativeBridge.Handle,
start,
TraceResult resultBuffer = default; angles,
TraceOptions optionsBuffer = options; ignore.Handle,
(nint)Unsafe.AsPointer(ref Unsafe.AsRef(in options))
bool success = NativeBridge._traceShape!(NativeBridge.m_pRayTraceHandle, );
origin.Handle, }
angles.Handle,
ignoreEntity?.Handle ?? nint.Zero,
(nint)(&optionsBuffer),
(nint)(&resultBuffer));
result = resultBuffer;
return success;
} }
public unsafe bool TraceEndShape(Vector origin, Vector endOrigin, CBaseEntity? ignoreEntity, TraceOptions options, out TraceResult result) public TraceResult TraceEndShape(in Vector3 start, in Vector3 end, CEntityInstance ignore, in TraceOptions options)
{ {
result = default; unsafe
{
if (!NativeBridge.Loaded)
return default;
if (!NativeBridge.m_bRayTraceLoaded || NativeBridge.m_pRayTraceHandle == nint.Zero) return NativeBridge.TraceEndShape!(
return false; NativeBridge.Handle,
start,
TraceResult resultBuffer = default; end,
TraceOptions optionsBuffer = options; ignore.Handle,
(nint)Unsafe.AsPointer(ref Unsafe.AsRef(in options))
bool success = NativeBridge._traceEndShape!(NativeBridge.m_pRayTraceHandle, );
origin.Handle, }
endOrigin.Handle,
ignoreEntity?.Handle ?? nint.Zero,
(nint)(&optionsBuffer),
(nint)(&resultBuffer));
result = resultBuffer;
return success;
} }
public unsafe bool TraceHullShape(Vector vecStart, Vector vecEnd, Vector hullMins, Vector hullMaxs, CBaseEntity? ignoreEntity, TraceOptions options, out TraceResult result) public TraceResult TraceHullShape(in Vector3 start, in Vector3 end, in Vector3 mins, in Vector3 maxs, CEntityInstance ignore, in TraceOptions options)
{ {
result = default; unsafe
{
if (!NativeBridge.Loaded)
return default;
if (!NativeBridge.m_bRayTraceLoaded || NativeBridge.m_pRayTraceHandle == nint.Zero) return NativeBridge.TraceHullShape!(
return false; NativeBridge.Handle,
start,
end,
mins,
maxs,
ignore.Handle,
(nint)Unsafe.AsPointer(ref Unsafe.AsRef(in options))
);
}
}
TraceResult resultBuffer = default; public TraceResult TraceShapeEx(in Vector3 start, in Vector3 end, nint filter, nint ray)
TraceOptions optionsBuffer = options; {
unsafe
{
if (!NativeBridge.Loaded)
return default;
bool success = NativeBridge._traceHullShape!(NativeBridge.m_pRayTraceHandle, return NativeBridge.TraceShapeEx!(
vecStart.Handle, NativeBridge.Handle,
vecEnd.Handle, start,
hullMins.Handle, end,
hullMaxs.Handle, filter,
ignoreEntity?.Handle ?? nint.Zero, ray
(nint)(&optionsBuffer), );
(nint)(&resultBuffer)); }
result = resultBuffer;
return success;
} }
} }
public static class NativeBridge public static class NativeBridge
{ {
public static nint m_pRayTraceHandle = nint.Zero; public static nint Handle;
public static bool m_bRayTraceLoaded = false; public static bool Loaded;
public static Func<nint, nint, nint, nint, nint, nint, bool>? _traceShape; public static Func<nint, Vector3, QAngle, nint, nint, TraceResult>? TraceShape;
public static Func<nint, nint, nint, nint, nint, nint, bool>? _traceEndShape; public static Func<nint, Vector3, Vector3, nint, nint, TraceResult>? TraceEndShape;
public static Func<nint, nint, nint, nint, nint, nint, nint, nint, bool>? _traceHullShape; public static Func<nint, Vector3, Vector3, Vector3, Vector3, nint, nint, TraceResult>? TraceHullShape;
public static Func<nint, Vector3, Vector3, nint, nint, TraceResult>? TraceShapeEx;
public static bool Initialize() public static bool Initialize()
{ {
m_pRayTraceHandle = (nint)Utilities.MetaFactory("CRayTraceInterface001")!; Handle = (nint)Utilities.MetaFactory("CRayTraceInterface001")!;
if (m_pRayTraceHandle == nint.Zero) if (Handle == 0)
{
Prints.ServerLog("[RayTraceImpl] Failed to get Ray-Trace interface handle. Is Ray-Trace MetaMod module loaded?", ConsoleColor.Red);
return false; return false;
}
Bind(); Bind();
m_bRayTraceLoaded = true; Loaded = true;
return true; return true;
} }
private static void Bind() private static void Bind()
{ {
int traceShapeIndex = 2; int shape, end, hull, ex;
int traceEndShapeIndex = 3;
int traceHullShapeIndex = 4;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{ {
traceShapeIndex = 1; shape = 1;
traceEndShapeIndex = 2; end = 2;
traceHullShapeIndex = 3; hull = 3;
ex = 4;
}
else
{
shape = 2;
end = 3;
hull = 4;
ex = 5;
} }
_traceShape = VirtualFunction.Create<nint, nint, nint, nint, nint, nint, bool>(m_pRayTraceHandle, traceShapeIndex); TraceShape = VirtualFunction.Create<nint, Vector3, QAngle, nint, nint, TraceResult>(Handle, shape);
_traceEndShape = VirtualFunction.Create<nint, nint, nint, nint, nint, nint, bool>(m_pRayTraceHandle, traceEndShapeIndex); TraceEndShape = VirtualFunction.Create<nint, Vector3, Vector3, nint, nint, TraceResult>(Handle, end);
_traceHullShape = VirtualFunction.Create<nint, nint, nint, nint, nint, nint, nint, nint, bool>(m_pRayTraceHandle, traceHullShapeIndex); TraceHullShape = VirtualFunction.Create<nint, Vector3, Vector3, Vector3, Vector3, nint, nint, TraceResult>(Handle, hull);
TraceShapeEx = VirtualFunction.Create<nint, Vector3, Vector3, nint, nint, TraceResult>(Handle, ex);
} }
} }

View file

@ -144,36 +144,106 @@ struct TraceOptions
struct TraceResult struct TraceResult
{ {
float StartPosX; TraceResult()
float StartPosY; {
float StartPosZ; m_flFraction = 1.0f;
float EndPosX; m_bStartInSolid = false;
float EndPosY; m_bExactHitPoint = false;
float EndPosZ; m_pEnt = nullptr;
float HitPointX; m_pHitbox = nullptr;
float HitPointY; m_pSurfaceProperties = nullptr;
float HitPointZ; }
float NormalX;
float NormalY;
float NormalZ;
float Fraction;
float HitOffset;
int TriangleIndex; explicit TraceResult(const CGameTrace* pTrace, bool bResult)
int HitboxBoneIndex; {
int Contents; m_bTraceShapeResult = bResult;
int RayType; m_pSurfaceProperties = pTrace->m_pSurfaceProperties;
int AllSolid; m_pEnt = pTrace->m_pEnt;
int ExactHitPoint; m_pHitbox = pTrace->m_pHitbox;
m_hBody = pTrace->m_hBody;
m_hShape = pTrace->m_hShape;
m_nContents = pTrace->m_nContents;
m_BodyTransform = pTrace->m_BodyTransform;
m_ShapeAttributes = pTrace->m_ShapeAttributes;
m_vStartPos = pTrace->m_vStartPos;
m_vEndPos = pTrace->m_vEndPos;
m_vHitNormal = pTrace->m_vHitNormal;
m_vHitPoint = pTrace->m_vHitPoint;
m_flHitOffset = pTrace->m_flHitOffset;
m_flFraction = pTrace->m_flFraction;
m_nTriangle = pTrace->m_nTriangle;
m_nHitboxBoneIndex = pTrace->m_nHitboxBoneIndex;
m_eRayType = pTrace->m_eRayType;
m_bStartInSolid = pTrace->m_bStartInSolid;
m_bExactHitPoint = pTrace->m_bExactHitPoint;
}
CEntityInstance *HitEntity; TraceResult(const TraceResult&) = delete;
CHitBox *Hitbox; TraceResult& operator=(const TraceResult&) = delete;
CPhysSurfaceProperties *SurfaceProps;
IPhysicsBody *BodyHandle;
IPhysicsShape *ShapeHandle;
CTransform *BodyTransform; TraceResult(TraceResult&&) = default;
RnCollisionAttr_t *ShapeAttributes; TraceResult& operator=(TraceResult&&) = default;
const Vector& StartPos() const { return m_vStartPos; }
const Vector& EndPos() const { return m_vEndPos; }
const Vector& HitPoint() const { return m_vHitPoint; }
const Vector& Normal() const { return m_vHitNormal; }
float Fraction() const { return m_flFraction; }
float HitOffset() const { return m_flHitOffset; }
bool DidHit() const { return m_flFraction < 1.0f; }
bool IsAllSolid() const { return m_bStartInSolid; }
bool HasExactHit() const { return m_bExactHitPoint; }
CEntityInstance* HitEntity() const { return m_pEnt; }
int TriangleIndex() const { return m_nTriangle; }
int HitboxBoneIndex() const { return m_nHitboxBoneIndex; }
CHitBox* Hitbox() const { return const_cast<CHitBox*>(m_pHitbox); }
int Contents() const { return m_nContents; }
RayType_t RayType() const { return m_eRayType; }
IPhysicsBody* Body() const { return m_hBody; }
IPhysicsShape* Shape() const { return m_hShape; }
const CTransform& BodyTransform() const { return m_BodyTransform; }
const RnCollisionAttr_t& ShapeAttributes() const { return m_ShapeAttributes; }
CPhysSurfaceProperties* Surface() const { return const_cast<CPhysSurfaceProperties*>(m_pSurfaceProperties); }
private:
bool m_bTraceShapeResult; // return value from RayTrace::TraceShapeEx
const CPhysSurfaceProperties *m_pSurfaceProperties;
CEntityInstance *m_pEnt;
const CHitBox *m_pHitbox;
HPhysicsBody m_hBody;
HPhysicsShape m_hShape;
uint64 m_nContents; // contents on other side of surface hit
CTransform m_BodyTransform;
RnCollisionAttr_t m_ShapeAttributes;
Vector m_vStartPos; // start position
Vector m_vEndPos; // final position
Vector m_vHitNormal; // surface normal at impact
Vector m_vHitPoint; // exact hit point if m_bExactHitPoint is true, otherwise equal to m_vEndPos
float m_flHitOffset; // surface normal hit offset
float m_flFraction; // time completed, 1.0 = didn't hit anything
int32 m_nTriangle; // the index of the triangle that was hit
int16 m_nHitboxBoneIndex; // the index of the hitbox bone that was hit
RayType_t m_eRayType;
bool m_bStartInSolid; // if true, the initial point was in a solid area
bool m_bExactHitPoint; // if true, then m_vHitPoint is the exact hit point of the query and the shape
}; };
class CRayTraceInterface class CRayTraceInterface
@ -181,37 +251,25 @@ class CRayTraceInterface
public: public:
virtual ~CRayTraceInterface() = default; virtual ~CRayTraceInterface() = default;
virtual bool TraceShape( virtual TraceResult TraceShape(const Vector& vecStart,
const Vector* origin, const QAngle& angAngles,
const QAngle* viewangles, CEntityInstance* pIgnoreEntity,
CEntityInstance* ignoreEntity, TraceOptions* pTraceOptions) = 0;
const TraceOptions* opts,
TraceResult* outResult
) = 0;
virtual bool TraceEndShape( virtual TraceResult TraceEndShape(const Vector& vecStart,
const Vector* origin, const Vector& vecEnd,
const Vector* endOrigin, CEntityInstance* pIgnoreEntity,
CEntityInstance* ignoreEntity, TraceOptions* pTraceOptions) = 0;
const TraceOptions* opts,
TraceResult* outResult
) = 0;
virtual bool TraceHullShape( virtual TraceResult TraceHullShape(const Vector& vecStart,
const Vector* vecStart, const Vector& vecEnd,
const Vector* vecEnd, const Vector& vecMins,
const Vector* hullMins, const Vector& vecMaxs,
const Vector* hullMaxs, CEntityInstance* pIgnoreEntity,
CEntityInstance* ignoreEntity, TraceOptions* pTraceOptions) = 0;
const TraceOptions* opts,
TraceResult* outResult
) = 0;
virtual bool TraceShapeEx( virtual TraceResult TraceShapeEx(const Vector& vecStart,
const Vector* vecStart, const Vector& vecEnd,
const Vector* vecEnd, CTraceFilter* pTraceFilter,
CTraceFilter* filterInc, Ray_t* pRay) = 0;
Ray_t* rayInc,
TraceResult* outResult
) = 0;
}; };

View file

@ -15,27 +15,16 @@ namespace RayTracePlugin::RayTrace
{ {
CRayTrace g_CRayTrace; CRayTrace g_CRayTrace;
using TraceShapeFn = bool(*)(void* pThis, bool CRayTrace::Initialize()
Ray_t& ray,
Vector& start,
Vector& end,
CTraceFilter* filter,
CGameTrace* trace);
static TraceShapeFn s_TraceShape = nullptr;
bool Initialize()
{ {
DynLibUtils::CModule libserver = DynLibUtils::CModule(shared::g_pServer); m_pCNavPhysicsInterfaceVTable = DynLibUtils::CModule(shared::g_pServer).GetVirtualTableByName("CNavPhysicsInterface").RCast<void**>();
void** pVTable = libserver.GetVirtualTableByName("CNavPhysicsInterface").RCast<void**>(); if (!m_pCNavPhysicsInterfaceVTable)
if (!pVTable)
{ {
FP_ERROR("Failed to find CNavPhysicsInterface VTable!"); FP_WARN("Tried getting virtual function from a null vtable.");
return false; return false;
} }
s_TraceShape = DynLibUtils::CMemory(pVTable[shared::g_pGameConfig->GetOffset("CNavPhysicsInterface_TraceShape")]).RCast<TraceShapeFn>(); m_pCNavPhysicsInterface_TraceShape = m_pCNavPhysicsInterfaceVTable[shared::g_pGameConfig->GetOffset("CNavPhysicsInterface_TraceShape")];
return true; return true;
} }
@ -54,167 +43,129 @@ namespace RayTracePlugin::RayTrace
beam->DispatchSpawn(); beam->DispatchSpawn();
} }
std::optional<TraceResult> TraceShapeEx( TraceResult CRayTrace::TraceShape(const Vector& vecStart, const QAngle& angAngles, CEntityInstance* pIgnoreEntity, TraceOptions* pTraceOptions)
const Vector& start,
const Vector& end,
CTraceFilter& filterInc,
Ray_t rayInc)
{ {
if (!s_TraceShape) { CTraceFilterEx filter = pIgnoreEntity ? CTraceFilterEx(static_cast<CBaseEntity*>(pIgnoreEntity)) : CTraceFilterEx();
FP_ERROR("CNavPhysicsInterface::TraceShape is not bound!");
return std::nullopt; filter.m_nInteractsAs = 0;
filter.m_nInteractsWith = static_cast<uint64_t>(MASK_SHOT_PHYSICS);
filter.m_nInteractsExclude = 0;
if (pTraceOptions)
{
if (pTraceOptions->InteractsAs != 0)
filter.m_nInteractsAs = pTraceOptions->InteractsAs;
if (pTraceOptions->InteractsWith != static_cast<uint64_t>(MASK_SHOT_PHYSICS))
filter.m_nInteractsWith = pTraceOptions->InteractsWith;
if (pTraceOptions->InteractsExclude != 0)
filter.m_nInteractsExclude = pTraceOptions->InteractsExclude;
} }
thread_local CGameTrace tr{};
Vector startCopy = start;
Vector endCopy = end;
s_TraceShape(nullptr, rayInc, startCopy, endCopy,
&filterInc, &tr);
TraceResult r{};
r.StartPosX = tr.m_vStartPos.x;
r.StartPosY = tr.m_vStartPos.y;
r.StartPosZ = tr.m_vStartPos.z;
r.EndPosX = tr.m_vEndPos.x;
r.EndPosY = tr.m_vEndPos.y;
r.EndPosZ = tr.m_vEndPos.z;
r.HitPointX = tr.m_vHitPoint.x;
r.HitPointY = tr.m_vHitPoint.y;
r.HitPointZ = tr.m_vHitPoint.z;
r.NormalX = tr.m_vHitNormal.x;
r.NormalY = tr.m_vHitNormal.y;
r.NormalZ = tr.m_vHitNormal.z;
r.Fraction = tr.m_flFraction;
r.HitOffset = tr.m_flHitOffset;
r.TriangleIndex = tr.m_nTriangle;
r.HitboxBoneIndex = tr.m_nHitboxBoneIndex;
r.Contents = tr.m_nContents;
r.RayType = (int)tr.m_eRayType;
r.AllSolid = tr.m_bStartInSolid;
r.ExactHitPoint = tr.m_bExactHitPoint;
r.HitEntity = (CEntityInstance*)tr.m_pEnt;
r.Hitbox = (CHitBox*)tr.m_pHitbox;
r.SurfaceProps = (CPhysSurfaceProperties*)tr.m_pSurfaceProperties;
r.BodyHandle = (IPhysicsBody*)tr.m_hBody;
r.ShapeHandle = (IPhysicsShape*)tr.m_hShape;
r.BodyTransform = &tr.m_BodyTransform;
r.ShapeAttributes = &tr.m_ShapeAttributes;
return r;
}
std::optional<TraceResult> TraceShape(
const Vector& origin,
const QAngle& viewangles,
CEntityInstance* ignoreEntity,
const TraceOptions* opts)
{
Vector forward; Vector forward;
AngleVectors(viewangles, &forward); AngleVectors(angAngles, &forward);
Vector endOrigin{ Vector vecEnd{
origin.x + forward.x * 8192.f, vecStart.x + forward.x * 8192.f,
origin.y + forward.y * 8192.f, vecStart.y + forward.y * 8192.f,
origin.z + forward.z * 8192.f vecStart.z + forward.z * 8192.f
}; };
CTraceFilterEx filter = ignoreEntity ? CTraceFilterEx(static_cast<CBaseEntity*>(ignoreEntity)) : CTraceFilterEx();
filter.m_nInteractsAs = 0;
filter.m_nInteractsWith = static_cast<uint64_t>(MASK_SHOT_PHYSICS);
filter.m_nInteractsExclude = 0;
if (opts)
{
if (opts->InteractsAs != 0)
filter.m_nInteractsAs = opts->InteractsAs;
if (opts->InteractsWith != static_cast<uint64_t>(MASK_SHOT_PHYSICS))
filter.m_nInteractsWith = opts->InteractsWith;
if (opts->InteractsExclude != 0)
filter.m_nInteractsExclude = opts->InteractsExclude;
}
Ray_t ray; Ray_t ray;
auto res = TraceShapeEx(origin, endOrigin, filter, ray); auto res = TraceShapeEx(vecStart, vecEnd, &filter, &ray);
if (opts && opts->DrawBeam) if (pTraceOptions && pTraceOptions->DrawBeam)
{ {
Color col = res.has_value() ? colors::Red().ToValveColor() : colors::Green().ToValveColor(); Color col = res.DidHit() ? colors::Red().ToValveColor() : colors::Green().ToValveColor();
DrawBeam(origin, res ? Vector(res->EndPosX, res->EndPosY, res->EndPosZ) : endOrigin, col); DrawBeam(vecStart, res.DidHit() ? res.HitPoint() : vecEnd, col);
} }
return res; return res;
} }
std::optional<TraceResult> TraceEndShape( TraceResult CRayTrace::TraceEndShape(const Vector& vecStart, const Vector& vecEnd, CEntityInstance* pIgnoreEntity, TraceOptions* pTraceOptions)
const Vector& origin,
const Vector& endOrigin,
CEntityInstance* ignoreEntity,
const TraceOptions* opts)
{ {
CTraceFilterEx filter = ignoreEntity ? CTraceFilterEx(static_cast<CBaseEntity*>(ignoreEntity)) : CTraceFilterEx(); CTraceFilterEx filter = pIgnoreEntity ? CTraceFilterEx(static_cast<CBaseEntity*>(pIgnoreEntity)) : CTraceFilterEx();
filter.m_nInteractsAs = 0; filter.m_nInteractsAs = 0;
filter.m_nInteractsWith = static_cast<uint64_t>(MASK_SHOT_PHYSICS); filter.m_nInteractsWith = static_cast<uint64_t>(MASK_SHOT_PHYSICS);
filter.m_nInteractsExclude = 0; filter.m_nInteractsExclude = 0;
if (opts) if (pTraceOptions)
{ {
if (opts->InteractsAs != 0) if (pTraceOptions->InteractsAs != 0)
filter.m_nInteractsAs = opts->InteractsAs; filter.m_nInteractsAs = pTraceOptions->InteractsAs;
if (opts->InteractsWith != static_cast<uint64_t>(MASK_SHOT_PHYSICS)) if (pTraceOptions->InteractsWith != static_cast<uint64_t>(MASK_SHOT_PHYSICS))
filter.m_nInteractsWith = opts->InteractsWith; filter.m_nInteractsWith = pTraceOptions->InteractsWith;
if (opts->InteractsExclude != 0) if (pTraceOptions->InteractsExclude != 0)
filter.m_nInteractsExclude = opts->InteractsExclude; filter.m_nInteractsExclude = pTraceOptions->InteractsExclude;
} }
Ray_t ray; Ray_t ray;
auto res = TraceShapeEx(origin, endOrigin, filter, ray); auto res = TraceShapeEx(vecStart, vecEnd, &filter, &ray);
if (opts && opts->DrawBeam) if (pTraceOptions && pTraceOptions->DrawBeam)
{ {
Color col = res.has_value() ? colors::Red().ToValveColor() : colors::Green().ToValveColor(); Color col = res.DidHit() ? colors::Red().ToValveColor() : colors::Green().ToValveColor();
DrawBeam(origin, res ? Vector(res->EndPosX, res->EndPosY, res->EndPosZ) : endOrigin, col); DrawBeam(vecStart, res.DidHit() ? res.HitPoint() : vecEnd, col);
} }
return res; return res;
} }
std::optional<TraceResult> TraceHullShape(const Vector &vecStart, const Vector &vecEnd, const Vector &hullMins, TraceResult CRayTrace::TraceHullShape(const Vector& vecStart, const Vector& vecEnd, const Vector& vecMins,
const Vector &hullMaxs, CEntityInstance *ignoreEntity, const TraceOptions *opts) { const Vector& vecMaxs, CEntityInstance* pIgnoreEntity, TraceOptions* pTraceOptions)
CTraceFilterEx filter = ignoreEntity ? CTraceFilterEx(static_cast<CBaseEntity*>(ignoreEntity)) : CTraceFilterEx(); {
CTraceFilterEx filter = pIgnoreEntity ? CTraceFilterEx(static_cast<CBaseEntity*>(pIgnoreEntity)) : CTraceFilterEx();
filter.m_nInteractsAs = 0; filter.m_nInteractsAs = 0;
filter.m_nInteractsWith = static_cast<uint64_t>(MASK_SHOT_PHYSICS); filter.m_nInteractsWith = static_cast<uint64_t>(MASK_SHOT_PHYSICS);
filter.m_nInteractsExclude = 0; filter.m_nInteractsExclude = 0;
if (opts) if (pTraceOptions)
{ {
if (opts->InteractsAs != 0) if (pTraceOptions->InteractsAs != 0)
filter.m_nInteractsAs = opts->InteractsAs; filter.m_nInteractsAs = pTraceOptions->InteractsAs;
if (opts->InteractsWith != static_cast<uint64_t>(MASK_SHOT_PHYSICS)) if (pTraceOptions->InteractsWith != static_cast<uint64_t>(MASK_SHOT_PHYSICS))
filter.m_nInteractsWith = opts->InteractsWith; filter.m_nInteractsWith = pTraceOptions->InteractsWith;
if (opts->InteractsExclude != 0) if (pTraceOptions->InteractsExclude != 0)
filter.m_nInteractsExclude = opts->InteractsExclude; filter.m_nInteractsExclude = pTraceOptions->InteractsExclude;
} }
Ray_t ray; Ray_t ray;
ray.Init(hullMins, hullMaxs); ray.Init(vecMins, vecMaxs);
auto res = TraceShapeEx(vecStart, vecEnd, filter, ray);
if (opts && opts->DrawBeam) auto res = TraceShapeEx(vecStart, vecEnd, &filter, &ray);
if (pTraceOptions && pTraceOptions->DrawBeam)
{ {
Color col = res.has_value() ? colors::Red().ToValveColor() : colors::Green().ToValveColor(); Color col = res.DidHit() ? colors::Red().ToValveColor() : colors::Green().ToValveColor();
DrawBeam(vecStart, res ? Vector(res->EndPosX, res->EndPosY, res->EndPosZ) : vecEnd, col); DrawBeam(vecStart, res.DidHit() ? res.HitPoint() : vecEnd, col);
} }
return res; return res;
} }
TraceResult CRayTrace::TraceShapeEx(const Vector& vecStart, const Vector& vecEnd, CTraceFilter* pTraceFilter, Ray_t* pRay)
{
if (!m_pCNavPhysicsInterface_TraceShape)
{
FP_ERROR("CNavPhysicsInterface::TraceShape is not bound!");
return TraceResult();
}
Vector vecStartCopy = vecStart;
Vector vecEndCopy = vecEnd;
CGameTrace trace;
bool bResult = m_pCNavPhysicsInterface_TraceShape.RCast<
bool (*)(void*, Ray_t&, Vector&, Vector&, CTraceFilter*, CGameTrace*)>()(
nullptr, *pRay, vecStartCopy, vecEndCopy, pTraceFilter, &trace);
return TraceResult(&trace, bResult);
}
} }

View file

@ -27,98 +27,18 @@ namespace RayTracePlugin::RayTrace
} }
}; };
bool Initialize(); class CRayTrace : public CRayTraceInterface {
std::optional<TraceResult> TraceShape(
const Vector& origin,
const QAngle& viewangles,
CEntityInstance* ignoreEntity = nullptr,
const TraceOptions* opts = nullptr);
std::optional<TraceResult> TraceEndShape(
const Vector& origin,
const Vector& endOrigin,
CEntityInstance* ignoreEntity = nullptr,
const TraceOptions* opts = nullptr);
std::optional<TraceResult> TraceHullShape(
const Vector& vecStart,
const Vector& vecEnd,
const Vector& hullMins,
const Vector& hullMaxs,
CEntityInstance* ignoreEntity = nullptr,
const TraceOptions* opts = nullptr);
std::optional<TraceResult> TraceShapeEx(
const Vector& vecStart,
const Vector& vecEnd,
CTraceFilter& filterInc,
Ray_t rayInc);
class CRayTrace : public CRayTraceInterface
{
public: public:
bool TraceShape( bool Initialize();
const Vector* origin, public:
const QAngle* viewangles, TraceResult TraceShape(const Vector& vecStart, const QAngle& angAngles, CEntityInstance* pIgnoreEntity, TraceOptions* pTraceOptions) override;
CEntityInstance* ignoreEntity, TraceResult TraceEndShape(const Vector& vecStart, const Vector& vecEnd, CEntityInstance* pIgnoreEntity, TraceOptions* pTraceOptions) override;
const TraceOptions* opts, TraceResult TraceHullShape(const Vector& vecStart, const Vector& vecEnd, const Vector& vecMins, const Vector& vecMaxs, CEntityInstance* pIgnoreEntity, TraceOptions* pTraceOptions) override;
TraceResult* outResult) override TraceResult TraceShapeEx(const Vector& vecStart, const Vector& vecEnd, CTraceFilter* pTraceFilter, Ray_t* pRay) override;
{ protected:
auto result = RayTrace::TraceShape(*origin, *viewangles, ignoreEntity, opts); DynLibUtils::CMemory m_pCNavPhysicsInterface_TraceShape;
if (!result.has_value()) protected:
return false; void** m_pCNavPhysicsInterfaceVTable;
*outResult = result.value();
return true;
}
bool TraceEndShape(
const Vector* origin,
const Vector* endOrigin,
CEntityInstance* ignoreEntity,
const TraceOptions* opts,
TraceResult* outResult) override
{
auto result = RayTrace::TraceEndShape(*origin, *endOrigin, ignoreEntity, opts);
if (!result.has_value())
return false;
*outResult = result.value();
return true;
}
bool TraceHullShape(
const Vector* vecStart,
const Vector* vecEnd,
const Vector* hullMins,
const Vector* hullMaxs,
CEntityInstance* ignoreEntity,
const TraceOptions* opts,
TraceResult* outResult) override
{
auto result = RayTrace::TraceHullShape(*vecStart, *vecEnd, *hullMins, *hullMaxs, ignoreEntity, opts);
if (!result.has_value())
return false;
*outResult = result.value();
return true;
}
bool TraceShapeEx(
const Vector* vecStart,
const Vector* vecEnd,
CTraceFilter* filterInc,
Ray_t* rayInc,
TraceResult* outResult) override
{
auto result = RayTrace::TraceShapeEx(*vecStart, *vecEnd, *filterInc, *rayInc);
if (!result.has_value())
return false;
*outResult = result.value();
return true;
}
}; };
extern CRayTrace g_CRayTrace; extern CRayTrace g_CRayTrace;