Skip to content

Refactor capsule creation to support dynamic shape selection#597

Open
tracygardner wants to merge 3 commits intomainfrom
claude/fix-airplane-physics-shape-MmnbC
Open

Refactor capsule creation to support dynamic shape selection#597
tracygardner wants to merge 3 commits intomainfrom
claude/fix-airplane-physics-shape-MmnbC

Conversation

@tracygardner
Copy link
Copy Markdown
Contributor

@tracygardner tracygardner commented May 1, 2026

Summary

Renamed createCapsuleFromBoundingBox to createShapeFromBoundingBox and enhanced it to dynamically select between box and capsule shapes based on mesh dimensions. This improves physics shape accuracy for meshes where height is less than twice the radius.

Key Changes

  • Renamed flockMesh.createCapsuleFromBoundingBox() to createShapeFromBoundingBox() across all call sites (mesh.js, physics.js, animate.js, blockmesh.js)
  • Added conditional logic to return a PhysicsShapeBox when mesh height is less than 2× radius, preventing degenerate capsule shapes
  • Reorganized code in the shape creation function to check dimensions before calculating capsule-specific parameters
  • Updated all references to use the new function name (4 files, 5 call sites)

Implementation Details

The function now performs a dimension check early in the process:

  • If height < 2 * radius: creates a box shape with dimensions matching the bounding box
  • Otherwise: creates a capsule shape as before with the existing shrink logic

This prevents invalid capsule geometries and ensures appropriate physics behavior for all mesh proportions.

https://claude.ai/code/session_01VZjZFU3AZyeseyRxtLuEwW

Summary by CodeRabbit

  • Bug Fixes
    • Refined physics collision-shape generation to intelligently select between capsule and box shapes based on mesh dimensions, improving collision accuracy for vertical animations and edge-case proportions.
    • Enhanced degenerate dimension handling in collision-shape construction for more accurate physical behavior.

claude added 3 commits May 1, 2026 06:21
Multi-object GLB models (e.g. airplane) have a vertex-less root mesh.
PhysicsShapeMesh on empty geometry produces a degenerate Havok shape
(a sphere), causing the model to float above the ground. Fall back to
PhysicsShapeBox derived from the hierarchical bounding box instead.

https://claude.ai/code/session_01VZjZFU3AZyeseyRxtLuEwW
In setupMesh, createCapsuleFromBoundingBox is called for all loaded
models. For wide/flat models like the airplane the capsule radius
exceeds half the height, producing a sphere (cylinderHeight=0) centred
on the bounding box rather than the base, which floats the model above
the ground. Fall back to PhysicsShapeBox when height < 2*capsuleRadius.

https://claude.ai/code/session_01VZjZFU3AZyeseyRxtLuEwW
Rename createCapsuleFromBoundingBox to createShapeFromBoundingBox and
make it return a PhysicsShapeBox when the bounding box is wider than
it is tall (height < 2*radius), which would otherwise produce a sphere
that floats the model above the ground. Tall models continue to get a
capsule as before.

https://claude.ai/code/session_01VZjZFU3AZyeseyRxtLuEwW
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 1, 2026

📝 Walkthrough

Walkthrough

The PR refactors physics shape creation by renaming createCapsuleFromBoundingBox to createShapeFromBoundingBox in api/mesh.js and implementing conditional logic that returns either a box or capsule based on bounding-box dimensions. All call sites across multiple files are updated to use the renamed function.

Changes

Cohort / File(s) Summary
Physics Shape Factory Definition
api/mesh.js
Renamed function from createCapsuleFromBoundingBox to createShapeFromBoundingBox. Added conditional logic to return PhysicsShapeBox when capsule height would be insufficient (height < 2 * radius), otherwise constructs capsule without prior Math.max(0, ...) clamping on cylinder segment height.
Physics Shape Factory Call Sites
api/animate.js, api/physics.js, ui/blockmesh.js
Updated function calls to use the renamed createShapeFromBoundingBox in fallback physics shape regeneration, capsule construction, and block mesh sizing logic respectively.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 A capsule became a box so wise,
When dimensions grew too tight—
The rabbit hops through shapes with glee,
Refactored physics, what a delight! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: renaming the capsule creation function and adding dynamic shape selection logic to choose between box and capsule shapes based on mesh dimensions.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/fix-airplane-physics-shape-MmnbC

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.

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 60 minutes.

Comment @coderabbitai help to get the list of available commands and usage tips.

@cloudflare-workers-and-pages
Copy link
Copy Markdown

Deploying flockxr with  Cloudflare Pages  Cloudflare Pages

Latest commit: faf074d
Status: ✅  Deploy successful!
Preview URL: https://165d37fa.flockxr.pages.dev
Branch Preview URL: https://claude-fix-airplane-physics.flockxr.pages.dev

View logs

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
api/animate.js (1)

18-29: ⚠️ Potential issue | 🟠 Major

Allow animation collider swaps to start from the box fallback.

When the "vertical" animation path creates a PhysicsShapeBox via createShapeFromBoundingBox (api/animate.js:67), the next animation change exits early because the guard at lines 19-27 only accepts PhysicsShapeCapsule instances. This prevents wide/flat models from ever transitioning to horizontal-fly, horizontal-fall, or sitting colliders. The guard must also accept box shapes as valid starting states.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@api/animate.js` around lines 18 - 29, The guard in
updateCapsuleShapeForAnimation prevents transitions when the current collider is
a PhysicsShapeBox created by createShapeFromBoundingBox; update the early-return
condition to accept both flock.BABYLON.PhysicsShapeCapsule and
flock.BABYLON.PhysicsShapeBox as valid starting types. Specifically, in
updateCapsuleShapeForAnimation check physicsMesh.physics.shape instanceof
flock.BABYLON.PhysicsShapeCapsule || physicsMesh.physics.shape instanceof
flock.BABYLON.PhysicsShapeBox (or equivalent) so box-shaped colliders can be
swapped into the horizontal/sitting capsule paths.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@api/mesh.js`:
- Around line 28-39: Compute the adjustedHeight (using shrinkAmount) before
deciding the fallback and use that adjustedHeight when checking against 2 *
radius so you don't produce a negative cylinderHeight; specifically, move or add
adjustedHeight = Math.max(0, height - shrinkAmount) above the conditional and
change the test from if (height < 2 * radius) to if (adjustedHeight < 2 *
radius) so the fallback box path and the cylinderHeight calculation
(cylinderHeight = adjustedHeight - 2 * radius) use the same threshold.

In `@api/physics.js`:
- Around line 58-62: The created collider's actual type isn't being saved: in
createPhysicsShape when you call flock.createShapeFromBoundingBox(mesh,
flock.scene) (and similarly where setupMesh seeds bodies), capture the returned
shape object, call getShapeTypeFromPhysics(returnedShape) to derive the real
type, and assign that result to physicsShapeType (instead of the requested
"CAPSULE" literal). Update any mesh/body seeding code that previously set
physicsShapeType implicitly to instead inspect the created shape and persist the
derived type so boxes created by createShapeFromBoundingBox are stored
correctly.

---

Outside diff comments:
In `@api/animate.js`:
- Around line 18-29: The guard in updateCapsuleShapeForAnimation prevents
transitions when the current collider is a PhysicsShapeBox created by
createShapeFromBoundingBox; update the early-return condition to accept both
flock.BABYLON.PhysicsShapeCapsule and flock.BABYLON.PhysicsShapeBox as valid
starting types. Specifically, in updateCapsuleShapeForAnimation check
physicsMesh.physics.shape instanceof flock.BABYLON.PhysicsShapeCapsule ||
physicsMesh.physics.shape instanceof flock.BABYLON.PhysicsShapeBox (or
equivalent) so box-shaped colliders can be swapped into the horizontal/sitting
capsule paths.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d1c06f2a-4042-48cb-a8fc-17cb8c08771e

📥 Commits

Reviewing files that changed from the base of the PR and between 43d5784 and faf074d.

📒 Files selected for processing (4)
  • api/animate.js
  • api/mesh.js
  • api/physics.js
  • ui/blockmesh.js

Comment thread api/mesh.js
Comment on lines +28 to +39
if (height < 2 * radius) {
return new flock.BABYLON.PhysicsShapeBox(
localCenter,
flock.BABYLON.Quaternion.Identity(),
new flock.BABYLON.Vector3(width, height, depth),
scene,
);
}

const shrinkAmount = 0.01;
const adjustedHeight = Math.max(0, height - shrinkAmount);
const cylinderHeight = adjustedHeight - 2 * radius;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use the same threshold for the fallback and the shrink step.

height < 2 * radius is checked before subtracting shrinkAmount, so meshes at height === 2 * radius — or within 0.01 above it — still go down the capsule path and end up with a negative cylinderHeight. That reintroduces the degenerate geometry this change is trying to avoid.

Suggested fix
-    if (height < 2 * radius) {
+    const shrinkAmount = 0.01;
+    const adjustedHeight = Math.max(0, height - shrinkAmount);
+    if (adjustedHeight <= 2 * radius) {
       return new flock.BABYLON.PhysicsShapeBox(
         localCenter,
         flock.BABYLON.Quaternion.Identity(),
         new flock.BABYLON.Vector3(width, height, depth),
         scene,
       );
     }
 
-    const shrinkAmount = 0.01;
-    const adjustedHeight = Math.max(0, height - shrinkAmount);
-    const cylinderHeight = adjustedHeight - 2 * radius;
+    const cylinderHeight = Math.max(0, adjustedHeight - 2 * radius);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (height < 2 * radius) {
return new flock.BABYLON.PhysicsShapeBox(
localCenter,
flock.BABYLON.Quaternion.Identity(),
new flock.BABYLON.Vector3(width, height, depth),
scene,
);
}
const shrinkAmount = 0.01;
const adjustedHeight = Math.max(0, height - shrinkAmount);
const cylinderHeight = adjustedHeight - 2 * radius;
const shrinkAmount = 0.01;
const adjustedHeight = Math.max(0, height - shrinkAmount);
if (adjustedHeight <= 2 * radius) {
return new flock.BABYLON.PhysicsShapeBox(
localCenter,
flock.BABYLON.Quaternion.Identity(),
new flock.BABYLON.Vector3(width, height, depth),
scene,
);
}
const cylinderHeight = Math.max(0, adjustedHeight - 2 * radius);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@api/mesh.js` around lines 28 - 39, Compute the adjustedHeight (using
shrinkAmount) before deciding the fallback and use that adjustedHeight when
checking against 2 * radius so you don't produce a negative cylinderHeight;
specifically, move or add adjustedHeight = Math.max(0, height - shrinkAmount)
above the conditional and change the test from if (height < 2 * radius) to if
(adjustedHeight < 2 * radius) so the fallback box path and the cylinderHeight
calculation (cylinderHeight = adjustedHeight - 2 * radius) use the same
threshold.

Comment thread api/physics.js
Comment on lines 58 to 62
const createPhysicsShape = (mesh, shapeType) => {
if (shapeType === "CAPSULE") {
mesh.computeWorldMatrix(true);
return flock.createCapsuleFromBoundingBox(mesh, flock.scene);
return flock.createShapeFromBoundingBox(mesh, flock.scene);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n -C3 'getShapeTypeFromPhysics|PhysicsShapeBox|createShapeFromBoundingBox|physicsShapeType|physicsCache|resolvedShapeType|setupMesh' api/physics.js api/mesh.js

Repository: flipcomputing/flock

Length of output: 6511


🏁 Script executed:

sed -n '3,17p' api/physics.js

Repository: flipcomputing/flock

Length of output: 448


Persist the actual collider type returned by createShapeFromBoundingBox().

getShapeTypeFromPhysics() only recognizes PhysicsShapeCapsule and PhysicsShapeMesh (returns null for any other type). However, createShapeFromBoundingBox() can return PhysicsShapeBox based on dimensions (api/mesh.js:29). When called at lines 61 and 328–331, the actual returned shape type is never captured—only the requested "CAPSULE" intent is stored in physicsShapeType. This causes boxes created here to be misidentified as "MESH" on restore (line 276 defaults to "MESH" when type is missing), losing the intended collider. Similarly, bodies seeded in api/mesh.js:805 via setupMesh don't persist their shape type at all.

Derive the real shape type from the returned object immediately after creation and cache it instead of the requested type.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@api/physics.js` around lines 58 - 62, The created collider's actual type
isn't being saved: in createPhysicsShape when you call
flock.createShapeFromBoundingBox(mesh, flock.scene) (and similarly where
setupMesh seeds bodies), capture the returned shape object, call
getShapeTypeFromPhysics(returnedShape) to derive the real type, and assign that
result to physicsShapeType (instead of the requested "CAPSULE" literal). Update
any mesh/body seeding code that previously set physicsShapeType implicitly to
instead inspect the created shape and persist the derived type so boxes created
by createShapeFromBoundingBox are stored correctly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants