Repository navigation
feat(world): a blast inside a shape pushes it toward its center of mass - #418
Merged
Merged
Conversation
siracozmen01
commented
Nov 5, 2026
siracozmen01
left a comment
Collaborator
Author
There was a problem hiding this comment.
Review at 6f52206: ready. No findings. Read, not run.
- Inside matches the sentence.
inside = !(out.distance > 1e-6f && surface > 1e-6f): the blast is in the core (distance about 0), or within the rounding (surface = distance - radiusabout 0 or below). That is "inside it, its rounding included", with the 1e-6 m tolerance the old branch already used. - The push is the sentence's.
BlastToCentertakesbody position + rotated localCenter - blast, the vector from the blast to the body's center of mass, and normalizes it.m3ApplyLinearImpulseInternalapplies it with no lever arm, so it spins nothing.- Its size still uses
ShapeProjectedAreaalong that direction, brought into the shape's frame withm3InvRotateVec3(xf.q, ...), andFalloffScaleis the old arithmetic moved out unchanged. - "At the center of mass itself" is a 1e-5 m ball (
dot(d, d) <= 1e-10). The body says so, and that is a fair reading of "itself" for a float position. - The body is woken before the branch, so even the no-push case wakes it, as "every body the blast reaches wakes" says.
- Multi-shape bodies:
PushShapestill runs once per shape and pushes that shape's body once.- A shape the blast is inside pushes toward the body's center of mass.
- A shape it is outside of pushes from its nearest point exactly as before (the
!insidebranch is the old code). - So a blast inside one shape of a two-shape body gives one center-of-mass push plus one ordinary push. A blast at the center of mass inside one shape gives that shape nothing and the other its ordinary push.
- The
M3_SOLVER_REVreasoning holds for what the rev guards. Its comment says it is in the snapshot config hash, so that a snapshot does not silently diverge. A snapshot holds state, not a pending explosion, and the step itself is unchanged, so every snapshot steps to the same bits. A journal has its own version and no config hash, so a tape recorded before this change replays an explosion with the new push. The body states that, and thefeatchangelog entry is where a user sees it.
Merged
siracozmen01
pushed a commit
that referenced
this pull request
Nov 8, 2026
🤖 I have created a release *beep* *boop* --- ## [1.1.0](v1.0.5...v1.1.0) (2026-11-05) ### Changed - **1.1.0 refuses snapshots, tapes and containers from 1.0.x.** The snapshot keeps format 6, the journal tape format 7 and the replay container format 4, but they carry `M3_ABI_VERSION`, which now names 1.1, so 1.1.0 refuses a snapshot, tape or container that 1.0.x wrote with `m3_errorVersion`. `m3CreateWorld` refuses a def from 1.0 headers the same way: rebuild against the 1.1 headers. The shared library's `SOVERSION` is `1.1`. - **A blast inside a shape pushes its body toward its centre of mass.** When the blast of `m3World_Explode` lay inside a shape, its rounding included, the body was pushed from the blast toward a point of that shape, with the impulse applied there. That point was a sphere's centre, the middle of a capsule's segment, or the average of a hull's vertices. A lopsided hull, or a body with several shapes, therefore spun for no physical reason. A blast exactly at that point pushed along the shape's own x axis. Now the push runs from the blast to the body's centre of mass, as maul2d's does, so it spins nothing, and a blast at the centre of mass itself pushes nothing; the body still wakes. `m3World_Explode`, and a replay of a journaled explosion, can give a different velocity to a body one of whose shapes holds the blast. A shape the blast is outside of is pushed as before. ([#418](#418)) ### Fixed - **A mesh create no longer takes seconds on a fan.** The edge flags a mesh bakes on create found each edge's neighbor by scanning every triangle around one of its vertices, so a fan of triangles around one vertex took time quadratic in its size: about 5.4 s for 65533 triangles. The bake now takes time linear in the mesh, about 0.025 s for the same fan, and gives the same flags byte for byte, so no contact or result changes. The same holds for a mesh create that `m3World_ReplayJournal` replays. ([#413](#413)) - **Restore refuses a voxel chunk whose cells disagree.** An occupied cell always has a fill of 1 to 255, and an empty cell has fill 0 and payload 0. `m3World_Restore` refused neither an occupied cell with fill 0 nor an empty cell with a fill or a payload, and `m3World_ReplayJournal` took the same states from a chunk create record. Both now refuse such a chunk with `m3_errorFormat`, and restore leaves the world as it was. No chunk the library writes is refused. ([#409](#409)) - **Restore refuses a wheel direction that is not unit length.** A vehicle create takes a wheel direction whose squared length lies between 0.81 and 1.21 and stores it normalized, but restore took any stored direction in that band, so a restored wheel could push along a scaled direction. Restore now requires the squared length within 1e-5 of 1, about 24 times what normalizing leaves, and refuses anything else with `m3_errorFormat`. ([#415](#415)) - **Restore refuses mesh edge flags the mesh would not bake.** A mesh's edge flags are derived from its geometry, and contact welding reads them, but restore took them as stored. Restore now bakes each mesh's flags again and refuses a snapshot whose flags differ, with `m3_errorFormat`, or `m3_errorCapacity` when it cannot allocate the bake's scratch. No snapshot the library writes is refused. On the largest mesh the limits allow, 65522 triangles, a restore took about 26 to 29 ms with the check against 22 ms without it. ([#416](#416))
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
When the blast of
m3World_Explodesits inside a shape, its rounding included,PushShapeinsrc/destruction.cpushed the body from the blast toward a point of the shape and applied the impulse there. That point was a sphere's centre, the middle of a capsule's segment, or the average of a hull's vertices (#417). It is not the body's centre of mass when a hull is lopsided or the body has more than one shape, so the push spun the body for a reason with no physical meaning. A blast exactly at that point pushed along the shape's local x axis, a direction nothing about the blast picks.docs/user/destruction.mdsaid the shape "is pushed out through its centroid", which matched neither case. maul2d pushes such a body toward its centre of mass, and the family should behave the same.Change
A shape with the blast inside it, its rounding included, now pushes its body in the direction from the blast to the body's centre of mass. The impulse is linear, along a line through the centre of mass, so that push spins nothing. A blast inside it at the centre of mass itself, within 1e-5 m, pushes nothing; the body still wakes. The impulse's size is computed as before:
impulsePerAreatimes the area the shape shows along the push.What changes for which calls:
m3World_Explode, and a replay of a journaled explosion, can give a different velocity to a body one of whose shapes holds the blast. A shape the blast is outside of is pushed as before.test/hashes.txt.M3_SOLVER_REVstays at 21: an explosion is an API call that acts at once, so a snapshot written before this change still steps to the same bits.docs/user/destruction.mdnow says: "A shape with the blast inside it, its rounding included, is pushed in the direction from the blast to its body's centre of mass, so that push spins nothing; a blast inside it at the centre of mass itself pushes nothing."Verification
TestBlastInsideintest/test_explosion.chas three cases:src/destruction.c, all three fail.test_golden,test_warmholdandtest_benchfrom a gcc-14 Release build give all 20 lines oftest/hashes.txtunchanged, before the two helpers were split out ofPushShape; the split moves no arithmetic, and the golden tests pass on this head.tools/check.sh prpasses in the main checkout on this head: 78 and 76 tests, and fuzz with no finding.Closes #417