Repository navigation
Conversation
…nd headers Included third party libraries
Must be adapted for 26.3 updates
Refactor PhysX module includes to new server paths (servers/physics_server_3d.h -> servers/physics_3d/physics_server_3d.h) and update rendering server includes to servers/rendering/rendering_server.h across multiple headers and source files. Adjusted register_types to use physics_3d wrapper. Added two thirdparty PhysX vehicle steering headers (PxVehicleSteeringFunctions.h, PxVehicleSteeringParams.h) to provide steering-related declarations. These changes align with server subtree reorganization and supply missing vehicle steering definitions.
…tact + teardown fixes Features: - SoftBody3D: full simulation via GPU PxDeformableVolume (FEM, conforming tetrahedron cook) with a CPU XPBD fallback; per-body Auto/CPU/GPU resolution through physics/physx_3d/soft_body/mode or the physx_soft_mode node metadata - PhysXGranular3D: granular volume (sand/gravel/snow) with a real angle of repose (MLS-MPM Drucker-Prager grains, optional PBD-granular mode) - PhysXParticleFluid3D: MLS-MPM compute-shader solver mode (no CUDA required) with analytic colliders, rigid-body reaction impulses and GPU isosurface; GPU fluid gains granular mode and isosurface tuning/clip-warning fixes - Tests: node-level smoke test in GDScript and C#; soft-body behavioral tests un-skipped; suite 332/332 green; shaders moved to particles/shaders/ - README: refresh the PhysX section (5.10 SDK zips, D3D12 build steps, feature/status tables, milestone checklist) [Fix - Critical]: PhysXSpace3D: size the GPU deformable contact buffers (maxDeformableSurfaceContacts / maxDeformableVolumeContacts) — with the budgets at 0 the GPU pipeline silently dropped every deformable contact, so GPU soft bodies fell through the world [Fix - Critical]: physx_filter_shader: exclude deformable pairs from contact modification — the GPU deformable pipeline cannot contact-modify, and any pair carrying the flag was dropped entirely (second half of the soft-body fall-through) [Fix - Critical]: worldscape_3d editor: remove queue_free-from-destructor patterns across 11 destructors that crashed the editor at shutdown [Fix - Critical]: PhysXSpace3D: detach registered soft bodies on space destruction — a soft body outliving its space (RID never freed) crashed the engine with a use-after-free at shutdown [Fix - High]: joints: skip Joint3D's transient single-node configuration quietly (a static-vs-world joint is invalid in PhysX) and cache params/flags until the joint exists — removes error spam from every static+dynamic joint setup [Fix - High]: joints: clamp cone-twist and 6DOF swing/twist limits into PhysX's valid range — degenerate Godot values (0 span = locked, the stock 0/0 g6dof ranges) were rejected as "limit invalid", breaking ragdoll and bridge joints [Fix - Medium]: bodies: skip the PhysX-rejected setWakeCounter on kinematic dynamics (PhysX error on every CharacterBody3D spawn)
…fer size error [Critical] KinematicCollision3D::get_collider index error - body_test_motion returned hit || recovered — it reported a hit with an empty collision list whenever initial-penetration recovery occurred, even without recovery_as_collision. Godot's reference implementation only returns true when actual contacts were recorded (result_count > 0). CharacterBody3D.move_and_slide stores every returned result as a slide collision, so walking around with tiny floor penetration handed the demo a slide collision with zero contacts → get_collider(0) spammed the error every frame. - Fix (physx_direct_space_state_3d.cpp): return hit || (recovery_as_collision && recovered && collision_count > 0), and removed the lines that zeroed collision_count/collision_depth after the contact pass. Also updated the PHYSX-MOVE-004 C# test, which had enshrined the buggy behavior — it now sets RecoveryAsCollision = true (exactly what CharacterBody3D does) and additionally asserts the reported collision has contacts. [Critical] MultiMesh buffer size: set_particle_count() / set_foam_particle_count() only stored the new value. The PBD path sizes its MultiMesh and the server-side capacity at creation, so changing the count on a live fluid — the sand demo's 1–3 keys (40k/70k/100k) — made the render pass write a new_count × 12 buffer into a MultiMesh allocated for the old count → p_buffer.size() != instances * stride. Fix (physx_particle_fluid_3d.cpp): both setters now rebuild the live PBD fluid so capacity and MultiMesh match the new count (MPM path already reconfigures on the next spawn). Verified: changing count on a running fluid no longer errors and re-spawning works.
This change preserves simulation collision for convex shapes when a body is rebuilt or re-enabled, while keeping concave shapes query-only on non-kinematic dynamic actors. It also adds regression tests covering static↔rigid round-trips and disable/re-enable behavior for convex and concave shapes. modules/physx/objects/physx_body_3d.cpp — companion guard. set_shape_disabled() unconditionally restored eSIMULATION_SHAPE on re-enable, which would push that forbidden flag combination onto a concave shape attached to a non-kinematic dynamic body. Re-enable now restores the simulation flag only when the shape is convex (or the actor is static/kinematic); eSCENE_QUERY_SHAPE is always restored so the shape stays raycastable. modules/physx/test/project/tests/csharp/BodyTests.cs — two new tests. PHYSX-BODY-056: static→rigid→static→rigid round-trip with a convex box, dropped onto a static floor after each switch; asserts it comes to rest at the half-extent height each time. PHYSX-BODY-057: disable/re-enable cycle — convex shape restores collision (body lands on the floor again); a concave mesh on a dynamic body stays query-only (ray still hits it, body never becomes solid).
Add ray-pickability support and return extra cast rest info. - Introduce virtual is_ray_pickable() in PhysXObject3D (default true) and mark Body/Area/SoftBody overrides as override. Make Area raw-server default pickable (Area3D node overrides on ready). - Add pick_ray flag to PhysXQueryFilterCallback and enforce it in preFilter; set pick_ray=true for intersect_ray so raycasts skip non-pickable objects while other queries ignore the flag. - Populate optional rest info for blocking hits in cast_motion (point, normal, shape, collider rid/id, linear_velocity). - Update C# tests: adjust RayPickableToggle expectations and add RayPickableMixed test verifying ray-only enforcement and that point/shape queries ignore pickability.
- Add signed/mirror-capable scaling for convex polygon shapes. - Dispatch geometry scaling via _shape_geometry_scale_for so convex shapes use a signed scale path; introduce _shape_geometry_scale_signed. ConvexPolygonShape3D now cooks a point-reflected (mirrored) convex mesh and stores convex_mesh_mirrored, uses abs(scale) when creating PxConvexMeshGeometry, and updates ensure/release helpers. - Query path (_build_query_shape) uses signed scale for convex queries when transform determinant != 0. - Add GPU-note comment and C# tests verifying mirrored convex behavior (ray, rest, query). Tests added (ShapeTests.cs, shapes 75/75) - PHYSX-SHAPE-MIR-001 — dynamic convex cube with an X-mirrored shape transform drops and rests at its half-height without NaN/crash. - PHYSX-SHAPE-MIR-002 — the decisive geometry check: an asymmetric wedge hull attached mirrored; a raycast at the mirrored slope position hits at the mirrored height (0.75) with the mirrored normal (+X tilt), while an unmirrored control body confirms the base geometry. - PHYSX-SHAPE-MIR-003 — rest_info with a mirrored query shape overlapping the floor returns an up normal and a floor-surface point, agreeing with the unmirrored control (query-vs-simulation agreement).
spaces/physx_direct_space_state_3d.cpp — one new static helper wired into both sweep paths: - _sweep_hit_opposing_normal(hit, dir, out normal): for sweep hits on PxTriangleMeshGeometry/PxHeightFieldGeometry (with a valid faceIndex), it re-derives the true world-space face normal via PxMeshQuery::getTriangle + PxTriangle::normal, keeping the orientation that agrees with the reported hit normal (the side the mover approached from; the SDK itself flips for negative-determinant mesh scales). It returns false — treat as non-blocking — when the resulting normal does not oppose the sweep direction (dot ≤ −0.001, the ancestor's proven threshold). Primitive-geometry hits keep their sweep normal but still get the opposition test, since a genuine forward block must oppose the motion. - _body_motion_cast (body_test_motion): positive-distance hits now go through the helper — edge-derived or contradictory-normal hits are rejected as grazing artifacts, and recorded hits use the re-derived face normal (feeding _fill_collision_from_sweep, so CharacterBody3D slide-collision normals are face-correct). The distance ≤ 0 eMTD/initial-contact branch (REG-0012) is deliberately untouched — its MTD normal is a push-out direction, exactly what Godot consumes there. - cast_motion: the same check runs in the forward-hit branch; a rejected grazing hit reports an unobstructed motion (safe/unsafe = 1), and the rest-info normal (from Phase 2) now uses the face-corrected normal.
Articulations are now fully usable from GDScript/C#, links have real collision shapes and filtering, and the full suites pass: C# 346/346 (articulations 5/5) and GDScript 47/47 (including the new binding test).
Module changes:
objects/physx_articulation_3d.{h,cpp} — the link list became a LinkRecord vector (PhysX link, its collision shape, layer/mask, density), and four capabilities were added:
- set_link_shape(index, PhysXShape3D*, transform): detaches the old shape, creates a per-link PxShape from the shared blueprint via the established create_shape + attachShape + release refcount pattern, composed with the shape's alignment pose. It reuses the Phase-4 scale dispatch (signed scale for convex polygons — mirrors preserved; abs for everything else) and applies REG-0014 semantics (concave shapes degrade to query-only on links, which simulate). Mass properties are recomputed at the link's stored density.
- set_link_collision_layer/mask (+ getters): per-link filter data written to the shape's word0/word1, so the module's filter shader and pair-callback semantics apply to links unchanged. Default layer/mask is 1/1, matching bodies.
- get_link_velocity(index): { "linear": Vector3, "angular": Vector3 } — PxArticulationLink inherits these from PxRigidBody.
- Crash guard found by the binding test: set_drive/set_limit on the base link (no inbound joint) dereferenced a null joint pointer and crashed the engine natively. Both now fail with a clear error. The GDScript binding test caught this on its first run — exactly what that suite is for.
physx_server.{h,cpp} — six new methods (link shape, layer/mask set/get ×2, velocity) plus ClassDB bindings for the entire articulation API (all 17 methods), following the vehicle block's pattern with the same inner-server caveat documented. The add_link box default is unchanged, so existing lab code keeps working.
doc_classes/PhysXServer3D.xml — full method documentation for the articulation API (enums, axis/joint-type tables, ordering constraint that links must be added before articulation_set_space).
Tests
- ArticulationTests.cs (new category, 5 tests): a revolute ACCELERATION drive holds a 0.5 rad target on a fixed-base two-link chain (angle + twist-axis checks); layer+mask 0 makes a link non-solid (sphere falls through to the floor); set_link_shape replacement is collision-bearing (sphere rests on the new shape); link velocity matches the finite difference of the transform under free fall; layer/mask round-trip.
- GDScript bridge + binding test: physx_articulation_bridge.gd (path-loaded, no class_name so it doesn't depend on the stale global class cache) and a GDBIND-ART-001 block exercising every bound method through GDScript → ClassDB → inner server, including multi-frame simulation.
- ArticulationApi.cs mirrors VehicleApi's reflection pattern with the per-call bridge fallback — needed because the generated C# glue predates the new bindings.
GPU soft-body collision exceptions are implemented and verified working. CPU soft bodies gained reference-parity area gravity overrides, the query-invisibility limitation is now explicit.
The three sub-goals:
1. GPU-path collision exceptions — implemented. The investigation went better than the plan's fallback branch anticipated. An empirical probe (GPU PxDeformableVolume soft bodies over a control platform vs. a platform whose layer/mask kills the shader pair) returned SHADER_KILLS_DEFORMABLE: the filter shader's eKILL fully controls GPU deformable-vs-rigid pairs in this SDK. That made the registry design viable:
PxFilterData.word2 (previously reserved-unused) now carries an exception slot on shapes whose owner participates in at least one soft-body exception. Slots are allocated lazily on PhysXObject3D (bodies, areas, soft bodies alike) and are monotonic — scenes without soft-body exceptions never write word2 and never touch the registry, so the shader cost is zero there.
PhysXSoftExceptionRegistry (soft slot → set of body slots) lives in physx_filter_shader.{h,cpp} with the documented thread contract: written only between steps on the server thread, read-only during simulate() — same happens-before argument as the existing actor userData floats.
The shader kills a deformable-vs-body pair whose two slots match a registry entry (both orientations handled). soft_body_add/remove_collision_exception and the soft-body free() branch maintain the registry and push slots into the deformable's collision shape (via a new PhysXSoftVolume3D::set_exception_slot, plus the slot is baked into Params so a volume built after the exception still gets it). Bodies propagate word2 through the existing update_shapes_collision_filter. The CPU XPBD path is untouched — it already enforced exceptions via the per-vertex query exclude list.
2. Area overrides — reference behavior verified, CPU parity implemented. The reference backend (GodotSoftBody3D::predict_motion) does resolve area gravity overrides (priority-ordered, override modes, default-area fallback) — contradicting the plan's expectation. The module's CPU path now does the same via PhysXSpace3D::_resolve_soft_body_gravity (AABB overlap approximation of the reference's broadphase pairs), with the override resolver and point-gravity helper extracted from physx_body_3d.cpp into shared helpers in physx_area_3d.{h,cpp} so body and soft-body resolution can't drift. GPU deformables keep scene gravity — the solver has no per-body gravity injection; that's documented rather than worked around. Area wind on soft bodies (also in the reference) is deferred and documented.
3. Queryability — made explicit. physx_query_filter_callback.cpp now documents that actors without PhysXActorUserData (GPU deformables, articulation links, particle systems) are invisible to scene queries by design, and that soft_body_set_ray_pickable is inert — with the reason (Godot's reference resolves soft bodies through its own pair system) and what query support would require. The same limitations block is in physx_soft_body_3d.h.
- Avoids expensive mass/inertia re-computation for non-mass body parameters by splitting surface-only updates from full actor re-application. - Exposes collision pair counts via ProcessInfo and documents island count as 0 because PhysX does not expose an island counter. Added C# coverage for the new stats behavior.
- Map Godot's SPACE_PARAM_CONTACT_MAX_ALLOWED_PENETRATION into PhysX by storing a per-space half-rest offset and applying it to newly created PxShapes. - Add physx_apply_space_rest_offset (declared in physx_shaped_object_3d.h) and call it when creating/attaching shapes (PhysXShapedObject3D, PhysXArea3D). - Cache unmapped contact-related SpaceParameters so SpaceGetParam round-trips for all eight parameters. - Remove unused PhysXVehicleSceneContext stub files and references. Add C# tests for space param round-trip and penetration/rest behavior.
The mechanism (adapted from the ancestor, matched to this module's structure): with more than one fluid in a space, PhysXSpace3D::step sets a per-step deferred flag before simulate(). Each fluid's onPostSolve then kicks only the smoothing (+anisotropy) kernels on the CUDA stream and stashes the PxGpuParticleSystem/stream/count — PhysX invokes each fluid's callback in turn during the solve, so inlining the sync there made system 2's kernel wait for system 1's callback to finish blocking (N serial GPU-idle gaps). After fetchResults, a new finish_isosurface_extraction() pass runs once per fluid: the first sync absorbs the whole batch's in-flight GPU work, then the outlier clamp, extraction and foam kicks proceed per fluid. Single-fluid spaces keep the inline path byte-identical to before (flag off → same code path), and the mode flip self-heals (a pending from a batched step is dropped if the count drops to 1). Publication timing and the one-30 Hz-tick latency are unchanged — publish still happens in onPostSolve after at least one full fetchResults between kick and read, same safety margin as before. Measurement (the phase's completion criterion): my first 2-fluid probe was noise-bound (±0.3 ms run-to-run vs ~0.13 ms expected win), so I amplified to 8 fluids (~24k particles each) and A/B'd by temporarily forcing the mode off — flag-off is the honest BEFORE since the inline branch is unchanged code. Five samples each: Inline (BEFORE): 22.97 / 21.97 / 22.31 / 22.13 / 23.67 ms avg frame — median 22.31 Batched (AFTER): 21.82 / 21.64 / 21.80 / 21.29 / 21.68 — median 21.80
Optional async stepping ships flag-gated (default off), and the completion criteria are met: the extended suite passes in both modes (354/354 async off, 354/354 async on, determinism 5/5 in async mode).
Engine's actual phase order:
tick N+1: sync() → flush_queries() → scripts (_physics_process) → end_sync() → step()
sync() runs at the start of each tick, fetching the solve that step() kicked at the end of the previous one. That means with async stepping, scripts still always run after a fetch — they read the same data as before, and the solve overlaps only the tail of the tick (deferred-call flush, node destruction, rendering). The behavioral shift is much smaller than the plan feared, and the plan's threading note ("the risk is ordering, not concurrency") turned out to be accurate for this reason.
Implementations:
1. Flag + split (physx_project_settings.{h,cpp}, physx_space_3d.{h,cpp}, physx_server.{h,cpp}):
- New physics/physx_3d/simulation/async_step (default off), read live per server step so it can be toggled at runtime — this is what makes both-mode validation and mid-flight flip-safety possible.
- step(): pre-step hooks, vehicle update, isosurface batching flag, simulate() — then returns if async. sync(): fetchResults(true) + the whole post-solve pipeline, extracted into a shared _finish_step() (queued mutations → GPU readbacks/CPU soft bodies → body post-step → vehicle post-step → active-actor count). With the flag off, step fetches inline and sync() no-ops — byte-identical to the historic path. end_sync() stays a marker.
- INFO_ACTIVE_OBJECTS moved to per-space counts taken right after each fetch (in async mode it's read one phase later — equivalent semantics, documented).
- step() called with a solve in flight now fetches defensively instead of erroring (covers mid-flight flag flips).
2. In-flight window hazards (the plan's ownership mandate): the window between step and sync is real — deferred engine calls and node destruction land there (I confirmed via main.cpp: message_queue->flush() runs right after step). All actor mutations now route through the space: add_actor/remove_actor queue into a pending-ops list applied after the fetch, and a new release_actor transfers PxActor ownership so a mid-flight destroy releases the actor only after the solve that still references it completes. Six sites were routed: body _destroy_actor, area destructor, and the GPU fluid/cloth/soft-volume destroy paths (plus their addActor sites). _terminate_scene fetches before releasing the scene if the space died mid-flight.
3. Scene queries: a query against a running scene is a hard PhysX error, so every PhysicsDirectSpaceState3D entry point force-fetches a pending solve first (_ensure_space_synced). Frames whose scripts query collapse the async overlap — that's the documented cost; frames that only read body state keep it.
Validation
1. Toggle mechanism: PHYSX_ASYNC_STEP=1 env var → TestMain sets the setting at startup (verified the banner appears and the flag is live in the run).
2. Extended suite × 2: async off 354/354, async on 354/354 (3,207 assertions) — including determinism 5/5 (same-solve results unaffected by host scheduling), stress 4/4, vehicles, articulations, soft bodies, and all query categories under the lazy-sync path.
3. PHYSX-LIFE-016 (new dedicated lifecycle test): a body freed via the message queue — landing mid-flight in async mode — leaves the space simulating cleanly (canary falls); in sync mode it's harmless by construction. Uses a GDScript bridge + CallDeferred since MessageQueue isn't C#-exposed.
The contact offset for PhysX shapes was being reset when margins changed, dropping the space-mapped rest offset and leaving restOffset >= contactOffset. This commit reapplies the space rest offset after shape updates and corrects the stepping flag timing so _finish_step runs after the scene is marked idle, preventing re-entrant sync/fetch recursion during post-solve queries.
1. Map per-axis 6DOF spring equilibria into a PxD6Joint drive target (setDrivePosition) and make spring drives unlimited (forceLimit=PX_MAX_F32). 2. Refactor GPU particle-fluid isosurface extraction to clamp outlier particles (clamp_outliers) to avoid infinite mesh stretching. 3. Add PhysXServer3D.soft_body_is_gpu binding, clarify soft-body query-invisibility (reverted userData wiring), adjust query filter for soft bodies, remove an unused variable, apply style/indent fixes, 4. update README and add related C# tests.
Aligns PhysX stepping with the async/sync flow by removing the premature scene simulation call, avoids stale wheel steer-angle data before the first write, and fixes wheel attachment pose conversion from the chassis body frame to the COM frame so suspension raycasts land at the expected positions.
Fixed a bug where negative (right) steer commands produced second-quadrant wheel angles via atan2, causing the vehicle to spin. The steer math now operates on |delta|, uses atan with a clamped positive denominator, and re-applies the sign to the final target. Adds a regression test (PHYSX-VEHI-019) verifying per-wheel angles and that a right steer yaws the chassis toward +X. Changes: modules/physx/vehicles/physx_vehicle_3d.cpp, modules/physx/test/project/tests/csharp/VehicleTests.cs.
Every scene-mutation path that could run between step() and sync() in async mode is now either routed through the existing deferred-op queue or forced to fetch the in-flight solve first. All 7 edited translation units pass a full MSVC C++20 syntax check (/Zs) against the vendored PhysX SDK and the Redot core headers.
Fix A — Vehicle surface_frictions dangling material (F-02). The grip table (Vehicle2State::surface_pairs) now has a parallel surface_pair_bodies RID table recording which ground body each entry was resolved from. apply_wheel_params writes both; new PhysXVehicle3D::invalidate_surface_pairs_for_body() compacts matching entries (order-stable, per-wheel nbMaterialFrictions kept consistent); new PhysXServer3D::invalidate_vehicles_surface_pairs() walks all surviving vehicles and is called from free()'s body branch before memdelete(b) releases the body's material. Fix C — Fluid set_capacity isosurface desync (F-05). set_capacity now destroys and rebuilds the isosurface stack when it exists (same pattern as set_foam_size), so the extractor's device/host scratch always matches the new capacity that onPostSolve feeds it. Fix D — PBD spawn int overflow (F-06). spawn() now computes per-axis cell counts and their product in int64_t, per-axis clamped to particle_count; a degenerate cap clamps to 0 instead of a negative resize. Fix E — Chunk emitter (F-19). Continuous emission normalizes emission_direction once per tick (zero falls back to up, matching spawn_at), so a non-unit direction no longer silently scales launch speed. Chunks carry a stable monotonic id; ChunkBody.index → ChunkBody.id; apply_chunk_impulse resolves by id (freeing a chunk mid-call is now a no-op, never an impulse aimed at the wrong chunk). The MPM fluid coupling was updated to carry ids.
- Integrates NVIDIA Blast authoring and runtime into the PhysX module. Adds PhysXBlastAsset, PhysXBlastAuthoring (mesh fracture), and PhysXDestructible3D (runtime damage/fracture/split/spawn). - Wires Windows-only NvBlast libs/DLLs in SCsub with a `blast=no` switch to disable. Provides editor UX: fracture dialog, inspector preview, context-menu entries, gizmo, icon, and preview builder. Registers new classes and doc XML, and includes a headless smoke test (gdscript) covering authoring → resource round-trip → destructible lifecycle. - Non-Windows platforms currently build without library wiring (prints a notice).
- Hinge axis now matches godot_physics_3d joint_make_hinge (transform variant) composes a Z→X frame rotation into both joint frames: godot_physics_3d constrains hinges about the frame's local Z; PhysX revolute joints rotate about local X. I initially used +90° about Y, which maps the PhysX axis onto Godot's −Z — the test run caught it immediately (wz=-1.44 for a +4 motor target), confirming the reference's -90° constant is the sign-preserving one. joint_make_hinge_simple is untouched (its frames already put the axis on X directly). - Joint RIDs survive body free / mode changes PhysXJoint3D now references the PhysXBody3D wrappers (not raw actors) plus cached frames, and bodies keep a joint list: Freeing a body notifies body_removed() — the PxJoint is dropped (fetch-safe via the Phase 1 gate, connected dynamics woken) but the wrapper, RID, kind and full parameter cache survive; no more "RID not found" errors and no more silently deleted joints. Actor recreation (static↔dynamic set_mode, scene entry) notifies rebuild() via _add_to_scene() — a dormant joint resurrects against the new actor with its entire configuration restored. release_joints_for_actor (the destructive path) is removed from the server and header. - Parameters are cached before anything else The wrapper's cache is now the single source of truth: every setter stores first, then pushes to the live PxJoint (config applied on creation/rebuild via _apply_params()). Parameters set during a dormant/transient configuration survive to resurrection — regression-tested. Two subtle state tracks were needed for faithful rebuilds: the slider's "limit was ever configured" bit (fresh sliders must stay unlimited) and the cone-twist "limits were ever set" bit (fresh cone limits are wide, not hair-thin-locked). A side effect of the redesign: the 6DOF ENABLE_MOTOR flag now caches the X-axis (twist) velocity correctly — the old code silently skipped it.
- Captured node scale on Body transform and Area transform changes and re-bake attached shapes against that scale. - Added PhysXShapedObject3D::refresh_shape_scaling to rebuild PxGeometry and local poses (scale origin, preserve rotation), update contact offsets, wake dynamic bodies, and recompute inertia for bodies. - Adjusted shape placement when adding shapes to account for body_scale. - Added C# tests for scaled static box queries, scaled sphere rest, mirrored convex collision, and scaled area gravity override to verify behavior.
1. Recovery margin slack + 0.4-scaled passes _body_motion_recover now implements the godot_physics contract exactly, with the constants taken from godot_space_3d.cpp (margin floored at 1e-4, min_contact_depth = margin × 0.05, 0.4 × remaining MTD per pass, 4 passes): penetration shallower than the slack counts as rest separation and is left alone, and each pass applies 40% of what remains — one call recovers ~87% of a deep embedment instead of snapping the full MTD. p_margin, previously unused, is now the knob driving the slack. The MTD/collision-reporting structure is untouched. 2. Sync callbacks gated on actual activity _finish_step no longer iterates every body. The sync set is now: active actors (from the getActiveActors query that already ran for INFO_ACTIVE_OBJECTS) ∪ all kinematics (Godot fires their state callback every tick — a stopped AnimatableBody3D platform must decay its carry-velocity to zero) ∪ newly-asleep bodies (in the previous active set but no longer — one final sync delivers the resting pose). Continuously sleeping bodies cost zero callbacks; a RigidBody3D's node sync resumes when it wakes. The unregister_body hook also clears the previous-active list so freed bodies never leave dangling pointers. 3. Moving areas stop waking the world The per-move bounds-read + overlap-query wake pass in PhysXArea3D::set_transform is now gated on the area actually imposing something (has_gravity_override() || has_linear_damp_override() || has_angular_damp_override()). Pure monitoring areas stop paying a query per move and stop keeping sleeping stacks awake. Wind is unaffected by design — sleeping bodies receive it from their own pre-step, and the trigger pairs driving overlap lists are re-evaluated by the kinematic move itself.
- Correct the per-vertex mass scaling for point impulses so soft-body pushes match the solver's even mass distribution. - Also fix contact-plane generation for sphere collisions by using the actual surface point/normal contract, preventing resting vertices from hovering and sticking in deep overlaps.
- Cloth solver - scale cached rest_volume by p_scale^3 to keep volume constraint consistent and distribute damping per substep to avoid compounding; - GPU particle fluid - increment mesh_version when clearing to avoid stale surfaces; soft volume - update PxShape simulation/query filter data when params change so deformables respect runtime layers/masks/exceptions; custom geometry callbacks - support per-scale callback instances, refresh params and proper cleanup; cone/cylinder adapt to new API; - Heightmap - use full signed 16-bit quantization range and add Y offset to center heights, reducing quantization artifacts.
- Tighten PhysX build gating to supported platforms only, - Fixes default gravity initialization to honor project settings, and avoids redundant actor flag writes in the pre-step path. - Cache repeated StringName lookups for gravity and async stepping, updates soft-body filter comments, and corrects the heightfield scale floor to prevent invalid raycast magnitudes on flat terrains. - These changes reduce unnecessary runtime churn and fix mismatches between project gravity settings and PhysX behavior.
Add coverage for overlap exits after body removal, joint preservation across body mode changes, async mid-solve frees, and freeing an area during monitor dispatch. Include the lifecycle suite in the default test run and add a minimal RID-level diagnostic probe.
Delete the temporary GDScript diagnostic, reformat run_suite.py, and remove an unused timing variable.
Bind is_gpu_dynamics_enabled() to ClassDB and add get_gpu_device_name() (returning the CUDA device name from the context, empty when GPU dynamics is unavailable -- CPU-only build, no usable device, or enhanced_determinism forcing the CPU path). Ports the reference module's PhysX.is_gpu_available()/get_gpu_device_name() queries the example project tests against; both documented in doc_classes.
…ls, flow diagnostics Solid flow colliders now use the reference coupling idiom: zero injection with full-channel coupling (velocity + divergence at 100, post-pressure apply) and allocationScale 0 -- the previous isPhysicsCollision-only form let gas blow straight through walls (flame_thrower passed through instead of deflecting). PhysXFlowSimulation3D gains scattering controls: light_scattering gates the gas's light-shaft/self-shadow pass, and scattering_receive_shadows (0 = off, nonzero = on at shadow_steps quality) gates shadow sampling. get_stats() is now script-facing (available, backend, active_blocks, step_ms, render_calls, timing) for debug HUDs. Emitter and collider NodePaths that fail to resolve now warn once instead of failing silently as "no flames".
|
Important Review skippedToo many files! This PR contains 893 files, which is 593 over the limit of 300. To get a review, reduce the PR to 300 files or fewer by splitting it into smaller PRs or changing its base branch. Usage-priced reviews support at most 300 files. ⚙️ Run configuration
⛔ Files ignored due to path filters: 66
📒 Files selected for processing: 893
You can disable this status message by setting the
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
@Vlado-VG Could you please run |
rest_info()'s penetrating-query fallback fabricated the surface point as a half-penetration midpoint (query_pose.p - mtd * depth * 0.5). That point sits on the query's side of the vertex, so the sd = (pos - point) . normal plane test in every per-vertex depenetration consumer (PhysXCloth3D::_collide(), the CPU soft-body contact refresh) read "in front of the surface" for any penetrating query and never resolved: a forced-CPU cloth fell straight through static bodies and floors (the cloth_ball scene's sheet ignored the ball entirely). Report the MTD exit point instead (query_pose.p + mtd *depth), which makes the plane test read -depth for a penetrating query -- the standard push-out contract. The GPU deformable-surface path is unchanged; the CPU cloth now drapes over the ball as it does on the GodotPhysics backend. Add a behavioral regression suite for the CPU cloth path (cloth_cpu_collision_test.gd, new blocking "cloth" suite in modules/physx/test): a forced-SIM_CPU sheet dropped onto a static ball must catch, drape, and never fall through. The existing gpu suite only exercised the GPU deformable path on CUDA machines, which is why the CPU fallback had no coverage and this bug survived. Apply the pre-commit style battery over the module (the failing "Static checks / Code style, file formatting, and docs" CI job): clang-format (C++/GLSL), dotnet-format (C# test suite), codespell, file-format, and header-guards fixes; seven headers carried a duplicated second copyright banner (blast asset/authoring, five editor plugin headers), reduced to the canonical "banner -> #pragma once" layout. No behavior changes in the style pass; engine build and the full test suites (gdscript, smoke, blast, vehicle, async, lifecycle, runtime, cloth, gpu, flow, water) are green. Fixes: CPU fallback cloth and soft-body collision depenetration against static/dynamic bodies.
|
Hello there, If you would like to test the PhysX module and help me squash some bugs, you are welcome to do so. I have provided a release-build as well as a ready-ported physx project example which Wild-Ox uses to showcase his module, but keep in mind not everything is 100% functional. You may discover some broken demos, which I'd be happy if you can report about. Thank you for your time... |
… shadows Implemented light scattering (FlowScatterRD): After Flow composites the gas, a quarter-resolution compute pass marches each pixel's view ray through Flow's sparse density and accumulates what nearby point/spot lights scatter toward the camera -- Godot's own distance and spot falloff, Henyey-Greenstein anisotropy, dimmed by scene objects (shadow atlas, omni dual-paraboloid + spot projected) and by the gas between the point and the light. An upsample pass blends it into the frame, trusting texels less the further their scene distance diverges. PhysXFlowSimulation3D: FlowScatterSource captured from the grid render data each frame; _gather_scatter_lights/_fill_scatter_shadows ported (top lights by volumetric-fog energy near the gas, shadows via LightStorage); new script properties scattering_strength / scattering_anisotropy / scattering_max_lights / scattering_shadow_steps / scattering_shadow_reach, joining light_scattering + scattering_receive_shadows. Also: FlowContextRD gains transient_buffer_rid() (the sparse table's RID, mirroring transient_texture_rid); solid flow colliders now deflect gas (reference coupling idiom: full-channel couple rates, post-pressure apply, no allocation) instead of passing through; unresolved emitter/collider NodePaths warn once; get_stats() bound with a step_ms diagnostic.
Add Redot copyright headers, correct the rendering include directives, clarify the resting-position variable in the penetration test, and allow `lineary` in the spell-check ignore list.
Move the separator line to the top of the Redot Engine copyright notice in both flow scatter files.
…2.9.2 physx_sdk_builds.yml: manual-dispatch Linux x86_64 build of the ovphysx-0.7.0 SDK in checked + release, with the CUDA GPU projects enabled (shared libPhysXGpu_64.so runtime) and the OmniPVD runtime; clang 18/gcc on ubuntu-24.04 with CUDA Toolkit 12.9.2 from the NVIDIA apt repo. Vendor preset carried from the Windows vc17win64-rd preset minus the MSVC-only PX_CMAKE_SUPPRESS_REGENERATION. PhysX only -- Flow and Blast pipelines are separate and Windows libs are produced locally. collect_physx_libs.py: also collect libPVDRuntime_static_64.a (linked by the module's Linux SCsub), the shared libPhysX*.so runtimes (RUNPATH=$ORIGIN via patchelf), and the SDK headers; skip the engine/ checkout tree so vendored previous-version libs can't shadow the fresh build; fix the expected-library check's lib-prefix mismatch.
|
I am well aware my repo is a couple commits behind. A large build update is on its way. Peace out ;) |
thirdparty/physx: Windows checked/release static libs and PhysXGpu_64.dll replaced with the 5.12.0 build; headers re-synced. PhysX 5.12 removed the PhysXCooking library (its functions are exported from PhysXCommon now), so PhysXCooking_static_64 drops out of the vendored set and both SCsub link lists; 5.12's NpOmniPvd also references the OmniPVD writer entry points, which pulls PVDRuntime_static_64 into the Windows link line (Linux already linked it). Module fixes (required by 5.12): - PxArticulationDrive takes a PxPerformanceEnvelope instead of a maxJointVelocity float; the articulation drive now constructs the envelope with PX_MAX_F32 effort and velocity, preserving the previous unlimited-drive semantics (the new maxEffort default of 0 disables the drive). - The 5.11-era gpuDynamicsConfig overrides (4M found/lost pairs, 4M contacts, 256 MB heap and collision stack) are replaced by the 5.12 defaults. 5.12 allocates the pinned-host heap, collision stack and broadphase mirrors eagerly at those sizes; the old values asked for single 256 MB pinned allocations, which Windows refuses (CUDA_ERROR_OUT_OF_MEMORY), after which 5.12 latches the CUDA context into a permanent out-of-memory state and every later GPU scene in the process aborts. With the defaults the whole C# suite passes. Test infrastructure: the physx_ci.csproj now restores the Redot.NET.Sdk packages from a repository-local source (modules/mono/nuget, filled by build_assemblies.py with the push nupkgs local option and gitignored) via a new nuget.config; the previously missing file made the C# suite build against the published SDK, which does not contain this fork's PhysX classes. The SDK pin moves to 26.3.0, matching what the script produces for this engine line.
Replace the 5.11 Linux static libraries with the 5.12 checked/release builds from the physx-sdk-builds workflow artifact: the eight core static libs plus PVDRuntime, the GPU static set (ArticulationGpu, BroadphaseGpu, CommonGpu, NarrowphaseGpu, SimulationControllerGpu, SolverGpu, CudaContextManager), and the 5.12 libPhysXGpu_64.so shared runtime. PhysXCooking_static_64 drops out -- 5.12 removed the cooking library and folds its functions into PhysXCommon, which the module already links last.
PhysXRuntimeManager gains the install helpers: manifest parsing (validated against the Windows bundle schema), streaming SHA-256, and bundle extraction with per-file checksum verification (with rollback on failure). The editor plugin's "Download and Restart" confirm now drives the real flow: fetch the manifest, download the bundle with progress, verify the zip and each extracted file, install beside the editor executable, then restart. The trigger fires at project open and live when the physics engine selection changes mid-session (via ProjectSettings settings_changed). SCsub: delay-load NvBlast.dll/NvBlastExtAuthoring.dll so the editor starts without the runtime DLLs; PhysXGpu_64.dll needs no flag (5.12 dispatches the GPU module through a runtime-set function pointer).
…the Blast runtime The editor executable statically imported NvBlastExtShaders.dll (the module references its shader symbols), which in turn statically imports NvBlast.dll and NvBlastGlobals.dll -- so removing any of the three from the editor's directory killed the process in the OS loader before main, too early for the runtime-missing dialog to help. Delay-loading ExtShaders removes the last static Blast dependency: the editor now starts without any PhysX runtime DLLs, the module detects the gap, and the download-and-restart flow installs the full set. NvBlastGlobals.dll stays off the /DELAYLOAD list: no engine object imports it directly (the linker warns "no imports found" under /WX); it loads as a dependency of the other Blast DLLs at install time.
…runtime support - SCsub: suppress LNK4199 (template_release's optimized link resolves NvBlast.dll imports only through the other Blast DLLs, so the delay-load flag legitimately matched nothing and /WX failed the link). - PhysXRuntimeManager: platform-aware required-files and manifest parsing (Linux needs only libPhysXGpu_64.so), bundle extraction creates its target directory, and the releases-page URL is exposed for manual-install fallback messages. - Editor plugin: install failures now show the manual-install instructions (bundle download + placement next to the executable) in the dialog and console. - release.yml: the Windows editor build packages the PhysX/Blast/Flow runtime DLLs as a separate physx-runtime-windows artifact for the redot-physx-runtime repository.
This module adds a new 3D physics backend with NVIDIA PhysX 5.11 behind Redot's standard PhysicsServer3D interface, bringing GPU-accelerated rigid body dynamics (CUDA) to Windows and Linux platforms along with a suite of high-fidelity physics subsystems, such as volumetric fluids, fracturing/destruction, advanced vehicles, and interactive water. It is implemented as a standard engine module (modules/physx) with a script-facing PhysXServer3D singleton, ready-to-use Node3D-based node wrappers, editor integration (gizmos, configuration warnings), full class documentation, and a regression test suite. The module has passed CI and builds on Windows (MSVC & MinGW) and Linux (CPU and CUDA GPU physics), with per-platform SDK libraries vendored and staged automatically. MACOS & Android are currently not yet supported.
is_gpu_dynamics_enabled(),get_gpu_device_name()).PhysXFlowSimulation3D/PhysXFlowEmitter3D/PhysXFlowCollider3Dfor real-time smoke, fire, and gas on the RenderingDevice backend, with preset colormaps, light scattering/self-shadowing, solid-collider coupling, per-sim stats, and Windows + Linux support.PhysXDestructible3D,PhysXBlastAsset,PhysXBlastAuthoringfor runtime fracturing, plus a Flow↔Blast bridge (PhysXFlowBlastBridge3D) that ties destruction debris into volumetric effects.PhysXVehicle3D/PhysXVehicleWheel3D(4W wheeled solver with Ackermann steering), plus ready-madePhysXMotorcycle3D,PhysXTank3D, and PhysXBoat3D variants.PhysXWaterSurface3Dwith caustics, boat/ship wakes (PhysXWaterWake3D), spray (PhysXWaterSpray3D), and genericPhysXBuoyancy3D.PhysXGranular3D), gas (PhysXGas3D+ emitters), particle fluids, soft bodies/volumes, and articulations - each with documented nodes and parameters.