world - live physics-world handle.
camera - live camera entity used to project the overlay.
flags - combined bits from the table below; default 7 retains joints, contacts and bounds. Zero draws nothing. Negative values and unknown bits cause a runtime error.
| Bit | Layer |
|---|---|
| 1 | Native joint geometry, with available limits and extra geometry. |
| 2 | Contact crosses, normals and signed separation labels in centimetres. |
| 4 | Native fat shape AABBs, including broad-phase margin; not exact body bounds. |
| 8 | Collider wire geometry: sphere circles, capsule rings/arcs, hull edges and solid mesh/terrain triangle edges. |
| 16 | Dynamic-body centre-of-mass axes and mass labels in kilograms. |
| 32 | Both joint attachment frames, including joints with shapeless or disabled bodies. |
| 64 | Approximate native normal-force arrows and labels. |
| 128 | Independent approximate native friction-force arrows and magnitude labels. |
| 256 | Native hexadecimal contact feature labels; these are not Blitz handles. |
| 512 | Native island bounds. |
| 1024 | Contact crosses coloured by solver graph assignment. |
| 4096 | Managed scene colliders, movement roles and body identities. Requires the shared world returned by GetWorldPhysics. |
Draws a world's diagnostics through renderer-owned 2D lines and text. Call after RenderWorld and before Flip. Add distinct flags or combine with Or; for example 24 shows collider geometry, centres of mass and mass labels. Default 7 does not include the new layers.
The overlay is not depth-tested. It respects camera viewport offsets, near/far clipping, perspective or orthographic projection, current 2D Viewport and buffer bounds. It ignores the current Origin while aligning to the camera, then restores Origin and Viewport and leaves Color white. A camera with projection mode 0 draws nothing. Text uses the current font; crowded or combined labels can overlap.
Shape, COM and island selection uses enabled native shape proxies. Sleeping enabled shapes remain visible; disabled shapes do not. Contact layers show awake solver pairs, including speculative contacts, rather than sensor overlaps or retained event history. Native joint geometry requires at least one participating body selected through a shape proxy. Flag 32 separately visits all public joints. Frame axes are red X, green Y and blue Z, each 0.3 metres long.
Force arrows are native solver estimates, not calibrated physical reaction-force measurements. The display uses half the stored impulse times the last step's inverse timestep. Normal estimates use totalNormalImpulse, which includes repeated bias/relaxation solver passes; friction uses the manifold friction impulse. Arrow length is 0.05 metres per displayed estimate unit. Do not treat changes between substep settings as physical force changes. Use the documented joint reaction getters for joint loads.
Drawing neither steps nor wakes bodies, consumes no events and records no mutations. Geometry replacement updates the next draw without another step. Sphere/capsule curves are sampled wire outlines; hull edges and mesh/terrain solid triangle edges use native cooked geometry, scale and transforms. Terrain holes are omitted.
For entity physics, keep the handle from GetWorldPhysics and use PhysicsDebugDraw world,camera,4096 after RenderWorld. This optional layer draws committed canonical geometry in the body's role colour: grey for static, blue for kinematic, green for free dynamic and purple for Collisions-controlled. Compatibility geometry uses short orange segments, leaving canonical edges visible when the shapes coincide. Each label shows the same body handle returned by EntityPhysicsBody, its movement role, and C/K counts for canonical/compatibility colliders. Counts describe actual colliders; they do not imply additional bodies or mass.
Layer 4096 includes sleeping enabled bodies and omits disabled bodies. It replaces layer 8 when both are selected; other diagnostic layers can be combined normally. It reads committed native geometry without applying pending entity edits, cooking colliders or stepping. Use UpdateWorld to apply edits, or GetWorldPhysics to explicitly reconcile without stepping. A previously obtained world handle can inspect the retained scene after a rejected edit. The layer is unavailable for standalone physics worlds or replay players. Bit 2048 remains unsupported by this command; PhysicsPlayerDebugDraw uses it for replayed queries.
Each world retains at most 262,144 lines and 8,192 labels (127 bytes per label). Exceeding either bound raises a runtime error before any primitives from that capture are drawn. Storage is reused until world teardown. Large scenes and many labels can be expensive; enable only the layers needed.
Optional native logging: set environment variable B3D_BOX3D_LOG to 1, including with SetEnv. Native messages then enter the existing bounded diagnostics store at Information level, category physics, source Box3D. Unset or other values preserve console logging. Ordinary steps may emit no messages. This is process-wide, thread-safe diagnostic routing; native workers never call Blitz functions. Native assertions retain their existing handler and fail-fast behaviour.
See also: StepPhysics, PhysicsBodyCenterX, PhysicsJointForceX, CapturePhysicsProfile, CountDiagnostics, DiagnosticMessage, ClearDiagnostics.
; Native physics overlays.
Graphics3D 1120,820,0,2
SetBuffer BackBuffer()
AppTitle "Blitz3D+ - physics debug layers"
Global world=CreatePhysicsWorld(),flags=8,overlay=True,paused=False,show_meshes=True,ortho=False
Global ball=CreateSphere(20),ball_body,ball_shape,ball_size#=1
Global block=CreateCube(),block_body,hinge,enabled=True
Dim layer_bits(11): Dim layer_keys(11): Dim layer_names$(11)
For i=1 To 11: layer_bits(i)=1 Shl (i-1): layer_keys(i)=i+1: Next
layer_keys(11)=34
layer_names(1)="1 Joint geometry / limits"
layer_names(2)="2 Contact points / normals"
layer_names(3)="3 Fat shape bounds"
layer_names(4)="4 Collider wire geometry"
layer_names(5)="5 Dynamic COM / mass"
layer_names(6)="6 Joint attachment frames"
layer_names(7)="7 Approx. normal forces"
layer_names(8)="8 Approx. friction forces"
layer_names(9)="9 Contact feature labels"
layer_names(10)="0 Island bounds"
layer_names(11)="G Solver graph colours"
camera=CreateCamera(): CameraViewport camera,340,130,760,660
CameraClsColor camera,16,23,36: PositionEntity camera,13,13,-20: CameraZoom camera,2.8
target=CreatePivot(): PositionEntity target,0,1,0: PointEntity camera,target
light=CreateLight(): RotateEntity light,45,-30,0: AmbientLight 110,120,140
floor=CreateCube(): ScaleEntity floor,5,.25,4: PositionEntity floor,0,-.25,0: EntityColor floor,45,55,70
floor_body=CreatePhysicsBody(world,floor,0): fs=PhysicsBodyBox(floor_body,10,.5,8)
ScaleEntity ball,.5,.5,.5: EntityColor ball,240,155,65
ball_body=CreatePhysicsBody(world,ball,2): ball_shape=PhysicsBodySphere(ball_body,1): PhysicsShapeRestitution ball_shape,.5
PhysicsBodySleeping ball_body,False: PhysicsBodyDamping ball_body,.3,.2
ScaleEntity block,1,.3,.6: EntityColor block,70,175,235
block_body=CreatePhysicsBody(world,block,2): bs=PhysicsBodyBox(block_body,2,.6,1.2)
; A shaped fixed anchor keeps native joint geometry visible.
anchor=CreatePivot(): PositionEntity anchor,1,2,0
anchor_body=CreatePhysicsBody(world,anchor,0): aps=PhysicsBodySphere(anchor_body,.15,0)
ResetPhysicsBody block_body,2,2,0,0,0,0
hinge=CreatePhysicsHingeJoint(anchor_body,block_body,1,2,0,0,0,1)
PhysicsJointLimits hinge,True,-.7,.7
PhysicsJointMotor hinge,True,1.2,20
capsule=CreatePivot(): PositionEntity capsule,-3,1.25,2
cap_body=CreatePhysicsBody(world,capsule,0): cs=PhysicsBodyCapsule(cap_body,1,2.5)
cap_mid=CreateCylinder(20,True,capsule): ScaleEntity cap_mid,.5,.75,.5: EntityColor cap_mid,100,210,150
For side=-1 To 1 Step 2
cap_end=CreateSphere(16,capsule): ScaleEntity cap_end,.5,.5,.5: PositionEntity cap_end,0,.75*side,0: EntityColor cap_end,100,210,150
Next
; A cooked hull and a triangle ramp expose their actual edges.
hull=CreateCone(8): ScaleEntity hull,.8,1,.8: PositionEntity hull,3,1,2: EntityColor hull,180,120,225
hull_body=CreatePhysicsBody(world,hull,0): hs=PhysicsBodyHull(hull_body,hull)
ramp=CreateMesh(): surf=CreateSurface(ramp)
AddVertex surf,-1,0,-1: AddVertex surf,1,0,-1: AddVertex surf,1,.8,1: AddVertex surf,-1,.8,1
AddTriangle surf,0,2,1: AddTriangle surf,0,3,2: UpdateNormals ramp
PositionEntity ramp,-1,0,2: EntityColor ramp,200,190,100: EntityFX ramp,16
ramp_body=CreatePhysicsBody(world,ramp,0): rs=PhysicsBodyMesh(ramp_body,ramp)
ResetDebugLoads()
timer=CreateTimer(60)
While Not KeyDown(1)
For i=1 To 11
If KeyHit(layer_keys(i)) Then flags=flags Xor layer_bits(i)
Next
If KeyHit(57) Then overlay=Not overlay
If KeyHit(25) Then paused=Not paused
If KeyHit(50)
show_meshes=Not show_meshes
If show_meshes
ShowEntity ball: ShowEntity block: ShowEntity capsule: ShowEntity hull: ShowEntity ramp: ShowEntity floor
Else
HideEntity ball: HideEntity block: HideEntity capsule: HideEntity hull: HideEntity ramp: HideEntity floor
EndIf
EndIf
If KeyHit(24)
ortho=Not ortho
If ortho Then CameraProjMode camera,2: CameraZoom camera,.09 Else CameraProjMode camera,1: CameraZoom camera,2.8
EndIf
If KeyHit(18) Then enabled=Not enabled: PhysicsBodyActive ball_body,enabled
If KeyHit(44)
ball_size=2.5-ball_size
PhysicsShapeSphere ball_shape,ball_size,0,0,0
ScaleEntity ball,ball_size/2,ball_size/2,ball_size/2
EndIf
If KeyHit(19) Then ResetDebugLoads()
ticks=WaitTimer(timer): If ticks>6 Then ticks=6
If paused=False
For tick=1 To ticks
If enabled Then PhysicsBodyForce ball_body,.5,0,0
StepPhysics world,1.0/60,4
Next
If EntityY(ball)<-4 Then ResetPhysicsBody ball_body,-2,4,-1,0,0,0
EndIf
RenderWorld
If overlay Then PhysicsDebugDraw world,camera,flags
Color 235,240,250
Text 18,18,"PHYSICS DEBUG LAYERS"
Text 18,49,"Native collider geometry, contacts and constraints"
Text 18,82,"Space: overlay P: pause M: meshes O: projection E: enable ball Z: resize ball R: reset Esc: exit"
For i=1 To 11
Color 115,130,150: state$="off"
If flags And layer_bits(i) Then Color 115,230,170: state="ON"
Text 18,146+(i-1)*29,layer_names(i)
Text 298,146+(i-1)*29,state
Next
Color 235,240,250
Text 18,488,"Flags: "+flags+" Overlay: "+overlay
Text 18,514,"Paused: "+paused+" Meshes: "+show_meshes
Text 18,540,"Ortho: "+ortho+" Ball active: "+PhysicsBodyIsEnabled(ball_body)
Text 18,566,"Ball diameter: "+ball_size+" m"
Text 18,610,"Frames: red X / green Y / blue Z"
Text 18,636,"Mass labels: kilograms"
Text 18,662,"Forces: native diagnostic estimates"
Text 18,688,"Contact layers show awake pairs."
Text 18,714,"Combine layers; crowded labels overlap."
Text 18,750,"M hides meshes so wire edges stand out."
Text 18,776,"R resets poses; layer choices stay set."
Flip
Wend
FreeTimer timer
FreePhysicsWorld world
ClearWorld
End
Function ResetDebugLoads()
ResetPhysicsBody ball_body,-2,4,-1,0,0,0
ResetPhysicsBody block_body,2,2,0,0,0,0
PhysicsBodyAwake block_body,True
End Function