From 04d95204ea2f38824353c072000fa19569a27d7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CE=B6eh=20Matt?= <5415177+ZehMatt@users.noreply.github.com> Date: Tue, 13 Oct 2026 02:08:41 +0300 Subject: [PATCH 1/3] Port SolidSetDefaults --- Game.Shared/PhysicsShared.cs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/Game.Shared/PhysicsShared.cs b/Game.Shared/PhysicsShared.cs index ad3d597d..b40be4fe 100644 --- a/Game.Shared/PhysicsShared.cs +++ b/Game.Shared/PhysicsShared.cs @@ -4,6 +4,7 @@ using Source; using Source.Common; using Source.Common.Filesystem; +using Source.Common.Formats.BSP; using Source.Common.Formats.Keyvalues; using Source.Common.Mathematics; using Source.Common.Physics; @@ -79,6 +80,25 @@ public void Clear() { } #endif +public class SolidSetDefaults : IVPhysicsKeyHandler +{ + Contents ContentsMask; + + public void ParseKeyValue(ref T data, ReadOnlySpan key, ReadOnlySpan value) { + if (stricmp(key, "contents") == 0) + ContentsMask = (Contents)atoi(value); + } + + public void SetDefaults(ref T data) { + Assert(typeof(T) == typeof(Solid)); + ref Solid solid = ref Unsafe.As(ref data); + solid.Params = g_PhysDefaultObjectParams; + ContentsMask = Contents.Solid; + } + + public Contents GetContentsMask() => ContentsMask; +} + [EngineComponent] public static class PhysicsSharedGlobals { @@ -116,6 +136,8 @@ public static class PhysicsSharedGlobals EnableCollisions = true }; + public static readonly SolidSetDefaults g_SolidSetup = new(); + public static void AddSurfacepropFile(ReadOnlySpan filename, IPhysicsSurfaceProps props, IFileSystem fileSystem) { using IFileHandle? file = fileSystem.Open(filename, FileOpenOptions.Read | FileOpenOptions.Binary, "GAME"); if (file != null) { From c08ff9d685a92b8c1fb26b11a3b2f5118a3a12e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CE=B6eh=20Matt?= <5415177+ZehMatt@users.noreply.github.com> Date: Tue, 13 Oct 2026 02:11:26 +0300 Subject: [PATCH 2/3] Port static prop VPhysics creation --- Game.Client/Physics.cs | 4 +- Game.Server/Physics.cs | 4 +- Source.Common/Engine/IStaticPropMgr.cs | 2 +- Source.Engine/StaticPropMgr.cs | 67 ++++++++++++++++++++++++-- 4 files changed, 71 insertions(+), 6 deletions(-) diff --git a/Game.Client/Physics.cs b/Game.Client/Physics.cs index 06a91dd7..74c4824b 100644 --- a/Game.Client/Physics.cs +++ b/Game.Client/Physics.cs @@ -12,6 +12,8 @@ using System.Collections.Generic; using System.Text; +using static Source.Engine.StaticPropMgrGlobals; + namespace Game.Client; [EngineComponent] @@ -82,6 +84,6 @@ private void PhysicsLevelInit() { g_PhysWorldObject = PhysCreateWorld_Shared(C_World.GetClientWorldEntity(), modelinfo.GetVCollide(1), g_PhysDefaultObjectParams)!; - // TODO: staticpropmgr.CreateVPhysicsRepresentations(physenv, g_SolidSetup, NULL); + g_StaticPropMgr.CreateVPhysicsRepresentations(physenv, g_SolidSetup, null); } } diff --git a/Game.Server/Physics.cs b/Game.Server/Physics.cs index 6cb4d475..56c79361 100644 --- a/Game.Server/Physics.cs +++ b/Game.Server/Physics.cs @@ -20,6 +20,8 @@ using System.Runtime.CompilerServices; using System.Text; +using static Source.Engine.StaticPropMgrGlobals; + namespace Game.Server; [EngineComponent] @@ -380,7 +382,7 @@ public override void LevelInitPreEntity() { } public static IPhysicsObject? PhysCreateWorld(BaseEntity world){ - // todo staticpropmgr + g_StaticPropMgr.CreateVPhysicsRepresentations(physenv, g_SolidSetup, world); VCollide? worldCollide = modelinfo.GetVCollide(1); return PhysCreateWorld_Shared(world, worldCollide, g_PhysDefaultObjectParams); } diff --git a/Source.Common/Engine/IStaticPropMgr.cs b/Source.Common/Engine/IStaticPropMgr.cs index b0a79ad2..e3a43752 100644 --- a/Source.Common/Engine/IStaticPropMgr.cs +++ b/Source.Common/Engine/IStaticPropMgr.cs @@ -7,7 +7,7 @@ public interface IStaticPropMgr { - void CreateVPhysicsRepresentations(IPhysicsEnvironment physenv, IVPhysicsKeyHandler defaults, object gameData); + void CreateVPhysicsRepresentations(IPhysicsEnvironment physenv, IVPhysicsKeyHandler defaults, object? gameData); void TraceRayAgainstStaticProp(Ray ray, int staticPropIndex, Trace tr); diff --git a/Source.Engine/StaticPropMgr.cs b/Source.Engine/StaticPropMgr.cs index 9d98e78a..af71fd46 100644 --- a/Source.Engine/StaticPropMgr.cs +++ b/Source.Engine/StaticPropMgr.cs @@ -242,8 +242,10 @@ public void GetStaticPropMaterialColorAndLighting(Trace trace, int staticPropInd throw new NotImplementedException(); } - public void CreateVPhysicsRepresentations(IPhysicsEnvironment physenv, IVPhysicsKeyHandler defaults, object gameData) { - throw new NotImplementedException(); + public void CreateVPhysicsRepresentations(IPhysicsEnvironment physenv, IVPhysicsKeyHandler defaults, object? gameData) { + int count = StaticProps.Count; + for (int i = count; --i >= 0;) + StaticProps[i].CreateVPhysics(physenv, defaults, gameData); } public void GetAllStaticProps(List output) { @@ -575,6 +577,20 @@ void ChangeRenderGroup(StaticProp prop) { public class StaticProp : IClientUnknown, IClientRenderable, ICollideable { + static readonly ObjectParams g_PhysDefaultObjectParams = new() { + MassCenterOverrideFn = null, + Mass = 1.0f, + Inertia = 1.0f, + Damping = 0.1f, + RotDamping = 0.1f, + RotInertiaLimit = 0.05f, + Name = "DEFAULT", + GameData = null, + Volume = 0f, + DragCoefficient = 1.0f, + EnableCollisions = true + }; + Vector3 Origin; QAngle Angles; Model? Model; @@ -872,7 +888,52 @@ public void CleanUpRenderHandle() { public ClientRenderHandle_t GetRenderHandle() => renderHandle; public void SetAlpha(byte alpha) => Alpha = alpha; - public void CreateVPhysics(IPhysicsEnvironment physenv, IVPhysicsKeyHandler defaults, object gameData) => throw new NotImplementedException(); + public void CreateVPhysics(IPhysicsEnvironment physenv, IVPhysicsKeyHandler defaults, object? gameData) { + if (Solid == (byte)SolidType.None) + return; + + VCollide? vcollide = null; + Solid solid = default; + PhysCollide? physCollide; + + if (Model != null && Solid == (byte)SolidType.VPhysics) + vcollide = CM.VCollideForModel(-1, Model); + + if (vcollide != null) { + physCollide = vcollide.Solids![0]; + + IVPhysicsKeyParser parse = physcollision.VPhysicsKeyParserCreate(vcollide.KeyValues!); + while (!parse.Finished()) { + ReadOnlySpan block = parse.GetCurrentBlockName(); + if (strcmpi(block, "solid") == 0) { + parse.ParseSolid(ref solid, defaults); + break; + } + else + parse.SkipBlock(); + } + physcollision.VPhysicsKeyParserDestroy(parse); + } + else { + if (Solid != (byte)SolidType.BBox) { + ReadOnlySpan model = Model != null ? modelLoader.GetName(Model) : "unknown model"; + Warning($"Map Error: Static prop with bogus solid type {Solid}! ({model})\n"); + Solid = (byte)SolidType.None; + return; + } + + physCollide = physcollision.BBoxToCollide(Model!.Mins, Model.Maxs); + solid.Params = g_PhysDefaultObjectParams; + } + + Assert(physCollide != null); + solid.Params.EnableCollisions = true; + solid.Params.GameData = gameData; + solid.Params.Name = "prop_static"; + + int surfaceData = (int)physprop.GetSurfaceIndex(((ReadOnlySpan)solid.SurfaceProp).SliceNullTerminatedString()); + physenv.CreatePolyObjectStatic(physCollide!, surfaceData, Origin, Angles, ref solid.Params); + } public float Radius() => radius; public int Flags() => flags; From 1c086446e5e3597c98b15f4736373f4325a86d4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CE=B6eh=20Matt?= <5415177+ZehMatt@users.noreply.github.com> Date: Tue, 13 Oct 2026 02:13:09 +0300 Subject: [PATCH 3/3] Fix model and world solid parsing not using g_SolidSetup --- Game.Shared/PhysicsShared.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Game.Shared/PhysicsShared.cs b/Game.Shared/PhysicsShared.cs index b40be4fe..25b79981 100644 --- a/Game.Shared/PhysicsShared.cs +++ b/Game.Shared/PhysicsShared.cs @@ -389,7 +389,7 @@ public static bool PhysModelParseSolidByIndex(ref Solid solid, BaseEntity entity Solid tmpSolid = default; tmpSolid.Params = g_PhysDefaultObjectParams; - parse.ParseSolid(ref tmpSolid, null); + parse.ParseSolid(ref tmpSolid, g_SolidSetup); if (solidIndex < 0 || tmpSolid.Index == solidIndex) { parsed = true; @@ -463,7 +463,7 @@ public static bool PhysModelParseSolidByIndex(ref Solid solid, BaseEntity entity if (strcmpi(block, "solid") == 0 || strcmpi(block, "staticsolid") == 0) { solid = default; solid.Params = defaultParams; - parse.ParseSolid(ref solid, null); + parse.ParseSolid(ref solid, g_SolidSetup); solid.Params.EnableCollisions = true; solid.Params.GameData = world; solid.Params.Name = "world"; @@ -479,6 +479,9 @@ public static bool PhysModelParseSolidByIndex(ref Solid solid, BaseEntity entity if (obj == null) continue; + Assert(g_SolidSetup.GetContentsMask() != 0); + obj.SetContents((uint)g_SolidSetup.GetContentsMask()); + worldPhysics ??= obj; } else if (strcmpi(block, "fluid") == 0) {