using System.ComponentModel; using System.Runtime.InteropServices; namespace Sandbox; partial class PhysicsWorld { internal Scene Scene { get; set; } private DebugOverlaySystem DebugOverlay => Scene?.DebugOverlay; /// /// A SceneWorld where debug SceneObjects exist. /// [EditorBrowsable( EditorBrowsableState.Never )] public SceneWorld DebugSceneWorld { get => native.GetDebugScene(); set => native.SetDebugScene( value ); } /// /// Updates all the SceneObjects in the , call once per tick or frame. /// [EditorBrowsable( EditorBrowsableState.Never )] public unsafe void DebugDraw() { if ( onDebugDrawFunctionPointer == DelegateFunctionPointer.Null ) onDebugDrawFunctionPointer = DelegateFunctionPointer.Get( OnDebugDrawInternal ); native.Draw( onDebugDrawFunctionPointer ); } DelegateFunctionPointer onDebugDrawFunctionPointer; internal unsafe struct DebugDrawArgs { public DrawStringArgs* String; public DrawSegmentArgs* Segment; public DrawPointArgs* Point; public DrawBoundsArgs* Bounds; public DrawTransformArgs* Transform; } internal unsafe struct DrawStringArgs { public Vector3 Position; public sbyte* String; public Color Color; } internal unsafe struct DrawSegmentArgs { public Vector3 P1; public Vector3 P2; public Color Color; } internal unsafe struct DrawPointArgs { public Vector3 Position; public float Size; public Color Color; } internal unsafe struct DrawBoundsArgs { public BBox Box; public Color Color; } internal unsafe struct DrawTransformArgs { public Vector3 Position; public Rotation Rotation; } [UnmanagedFunctionPointer( CallingConvention.StdCall )] unsafe delegate void DebugDrawDelegate_t( DebugDrawArgs* args ); private unsafe void OnDebugDrawInternal( DebugDrawArgs* args ) { if ( args == null ) return; if ( args->String != null ) OnDrawStringInternal( args->String ); else if ( args->Segment != null ) OnDrawSegmentInternal( args->Segment ); else if ( args->Point != null ) OnDrawPointInternal( args->Point ); else if ( args->Bounds != null ) OnDrawBoundsInternal( args->Bounds ); else if ( args->Transform != null ) OnDrawTransformInternal( args->Transform ); } private unsafe void OnDrawStringInternal( DrawStringArgs* args ) { if ( args == null ) return; if ( DebugOverlay == null ) return; var position = args->Position; var color = args->Color; var text = Marshal.PtrToStringAnsi( (IntPtr)args->String ); var scope = new TextRendering.Scope( text, color, 11, "Roboto Mono", 600 ) { Outline = new TextRendering.Outline { Color = Color.Black, Size = 3, Enabled = true } }; DebugOverlay.ScreenText( position, scope ); } private unsafe void OnDrawSegmentInternal( DrawSegmentArgs* args ) { if ( args == null ) return; if ( DebugOverlay == null ) return; var p1 = args->P1; var p2 = args->P2; var color = args->Color; DebugOverlay.Line( p1, p2, color ); } private unsafe void OnDrawPointInternal( DrawPointArgs* args ) { if ( args == null ) return; if ( DebugOverlay == null ) return; var position = args->Position; var size = args->Size; var color = args->Color; DebugOverlay.Point( position, size * 0.1f, color, 0, true ); } private unsafe void OnDrawBoundsInternal( DrawBoundsArgs* args ) { if ( args == null ) return; if ( DebugOverlay == null ) return; var box = args->Box; var color = args->Color; DebugOverlay.Box( box, color ); } private unsafe void OnDrawTransformInternal( DrawTransformArgs* args ) { if ( args == null ) return; if ( DebugOverlay == null ) return; var position = args->Position; var rotation = args->Rotation; const float axisLength = 5.0f; DebugOverlay.Line( position, position + rotation.Forward * axisLength, Color.Red, overlay: true ); DebugOverlay.Line( position, position + rotation.Right * axisLength, Color.Green, overlay: true ); DebugOverlay.Line( position, position + rotation.Up * axisLength, Color.Blue, overlay: true ); } }