clone

an endless marble-race generator in the browser; the url is the seed, so every track can be shared

main
marbles / docs / physics.md
physics.md 4.4K source

Marble physics research + design (M11)

Research findings

Box3D meshes (docs/collision.md, sample_mesh.cpp, pinned commit):

  • Trimesh via b3MeshDef → b3CreateMesh → b3CreateMeshShape (static only).
  • identifyEdges=true suppresses internal-edge (ghost) collisions — the modern equivalent of Marble Blast's smooth convex-brush interiors.
  • Docs recommend weldVertices=true (tolerance ~1mm). Samples pass a degenerate-triangle capacity; b3CreateMesh can fail → NEVER ignore a null return (our first ribbon dropped whole strips silently = holes).
  • Per-triangle materialIndices + b3ShapeDef materials = native per-surface friction/restitution → the future icy/boost surface hook.
  • Character mover doc: roundness beats soup — capsules/spheres slide because they're convex; keep the marble a dynamic sphere.

Marble Blast recipe (MBHaxe src/Marble.hx — "99% identical physics"):

  • gravity 20 (marble r≈0.2 → ~our world scale): gravity-DOMINANT feel.
  • Restitution 0.5 with minBounceVel 0.1 / minBounceSpeed 3 (thresholded — no micro-bounce); shock absorber 0.01, super bounce 0.9 (timed override).
  • Rolling: accelerate HARD toward a target velocity (maxRoll 15, angular accel 75 ≈ linear 15) — responsiveness comes from authority, not speed.
  • Friction: kinetic 0.7, static 1.1, bounce-kinetic 0.2. Air control 5.
  • Powerups are cheap parameter/impulse mutations with timers (helicopter = gravity ×0.25). Gravity pads rotate the gravity vector.

Our design mapping

  • Geometry: ONE welded ribbon trimesh per strip (identifyEdges), cups for drop shafts, boxes for islands. Mesh creation failures are LOUD.
  • Feel: gravity −20; assist = strong acceleration (≈8) toward target cruise, climb carry sized by rail lookahead, soft brake above max; restitution ~0.35 on the marble.
  • Per-tile modifiers (next): rail stations inherit def-family tags → speed/gravity multipliers; per-triangle materials for icy later.
  • Powerup pattern (later): timed overrides {gravityScale, restitution, impulse} — Marble Blast's proven cheap trick. Island gravity rotation = rotating the world gravity vector while anchored.

Ribbon debug state (next session)

  • Shim mesh path WORKS: 2-triangle repro (scratchpad mesh-repro.mjs) collides with winding [0,2,1] (one-sided; CW in (x,z) viewed from +y = up-facing collision). ph_add_mesh is loud on failure now.
  • The FULL ribbon holds the marble in NEITHER winding — the discrepancy is unexplained. NEXT: isolated repro — build ribbonOf() output for a synthetic straight 10-unit path, drop a marble in node, inspect. Suspects: vertex ordering per ring vs winding assumption, weld collapsing rings, triangle indices crossing ring boundaries wrongly, or scale (verts are fine floats).
  • USE_RIBBON=false gates it in proxies.ts; slab law is live and proven (65/65). Adopt Marble Blast constants (g=20, accel 8, restitution 0.35 thresholded) TOGETHER WITH the ribbon once it holds — recorded values in the section above.
  • Then: per-tile modifiers via rail-station def tags (speed/gravity) and per-triangle materials (icy); Marble Blast powerup pattern; island gravity rotation.

Ribbon session 2 findings (isolated repro chain — major progress)

  • FIXED: upV cross-product order (right×t hung gutters/walls BELOW the floor; straights masked it — flat middle sits at up=0 either way).
  • FIXED: winding is layout-handed (profile runs along right = up×t = −z for +x travel → original winding was correct; the square repro misled because its layout mirrored).
  • FIXED: strips now extrapolate to true boundary ports (stations are mid-samples; the half-step gap at every segment boundary dropped marbles at speed). Shaft-adjacent ends never extend.
  • Isolated straight AND arc ribbons now hold a rolling marble perfectly.
  • REMAINING: full-stream falls at chute VALLEYS — the pitch crease where a steep chute cubic meets flat acts as a kicker (marble arrives vy≈−11, reflects off the sharp V, launches over the 0.66 wall laterally; slabs' overlapping geometry incidentally rounded this). NEXT: round the valley — insert interpolated rings across part-boundary pitch jumps (bound Δpitch per ring like MAX_TURN bounds Δyaw), and/or raise walls through high-|dv| creases. Repro: phys-deep cua-seed-1 → FELL at s=27.3.
  • Ribbon stays gated (default slabs) until the valley fix lands green.