Chapter 12: Physics
The IWSDK provides a comprehensive physics system powered by the @babylonjs/havok
physics engine. This chapter covers everything you need to know about implementing realistic physics in your IWSDK applications.
What You'll Build
By the end of this chapter, you'll be able to:
- Set up the Havok physics engine with customized gravity settings
- Create falling objects with realistic collision shapes and material properties
- Build static environments with floors, walls, and other immovable objects
- Implement grabbable objects with proper physics interactions
- Apply forces and manipulate object velocities for dynamic interactions
- Optimize physics performance for complex scenes
Overview
The physics system is built using an Entity Component System (ECS) architecture and consists of three main components:
PhysicsSystem
- The core system that manages the Havok physics worldPhysicsShape
- Defines collision shapes and physics material properties (density, restition, friction)PhysicsBody
- Defines the motion behavior of entities (Static, Dynamic, Kinematic)PhysicsManipulation
- Applies one-time force and velocity changes
Quick Start
Here's a minimal example to get physics working in your scene:
import {
World,
PhysicsSystem,
PhysicsBody,
PhysicsShape,
PhysicsState,
PhysicsShapeType,
Mesh,
SphereGeometry,
MeshStandardMaterial,
} from '@iwsdk/core';
// 1. Register the physics system with customized gravity
world
.registerSystem(PhysicsSystem, { configData: { gravity: [0, -10, 0] } })
.registerComponent(PhysicsBody)
.registerComponent(PhysicsShape);
// 2. Create a mesh
const sphere = new Mesh(
new SphereGeometry(0.5),
new MeshStandardMaterial({ color: 0xff0000 }),
);
sphere.position.set(0, 5, 0);
scene.add(sphere);
// 3. Create entity and add physics components
const entity = world.createTransformEntity(sphere);
entity.addComponent(PhysicsShape, {
shape: PhysicsShapeType.Auto, // Automatically detects sphere
});
entity.addComponent(PhysicsBody, {
state: PhysicsState.Dynamic, // Falls due to gravity
});
System Setup
Step 1: Register the Physics System
The PhysicsSystem
must be registered with your world to enable physics simulation:
world
.registerSystem(PhysicsSystem)
.registerComponent(PhysicsBody)
.registerComponent(PhysicsShape);
Step 2: Physics World Configuration
The physics system automatically creates a Havok physics world with:
- Gravity: Default value
[0, -9.81, 0]
(configurable in physics system) (Earth-like gravity) - Step Rate: Synchronized with your application's frame rate
- Automatic Cleanup: Physics resources are cleaned up when entities are removed
Understanding the Components
PhysicsShape
Defines the collision shape and material properties of an entity.
entity.addComponent(PhysicsShape, {
shape: PhysicsShapeType.Box,
dimensions: [2, 1, 1],
density: 1.0,
friction: 0.5,
restitution: 0.0,
});
Required Properties
shape
- The collision shape type (see Shape Types)
Optional Properties
dimensions
- Shape-specific dimensions array. Not applicable whenPhysicsShapeType.Auto
is used.density
- Mass density (default: 1.0)friction
- Surface friction coefficient (default: 0.5)restitution
- Bounciness factor (default: 0.0)
Shape Types
Auto Detection
The most convenient option that automatically detects the best shape from your Three.js geometry. When this type is selected, the dimensions field in PhysicsShape will be overridden by the size of the Three.js geometry.
entity.addComponent(PhysicsShape, {
shape: PhysicsShapeType.Auto,
});
Mapping Rules:
SphereGeometry
→Sphere
BoxGeometry
→Box
PlaneGeometry
→Box
(thin)CylinderGeometry
→Cylinder
- Other geometries →
ConvexHull
Sphere
Most efficient for round objects.
entity.addComponent(PhysicsShape, {
shape: PhysicsShapeType.Sphere,
dimensions: [0.5, 0, 0], // [radius, unused, unused]
});
Box
Perfect for rectangular objects.
entity.addComponent(PhysicsShape, {
shape: PhysicsShapeType.Box,
dimensions: [2, 1, 1], // [width, height, depth]
});
Cylinder
For cylindrical objects.
entity.addComponent(PhysicsShape, {
shape: PhysicsShapeType.Cylinder,
dimensions: [0.5, 2, 0], // [radius, height, unused]
});
ConvexHull
A convex hull is the smallest convex shape containing points. It has good balance between accuracy and performance for complex shapes.
entity.addComponent(PhysicsShape, {
shape: PhysicsShapeType.ConvexHull,
// dimensions automatically calculated from geometry
});
TriMesh
Using tri mesh to fit the geometry. Most accurate but computationally expensive. Best for static objects.
entity.addComponent(PhysicsShape, {
shape: PhysicsShapeType.TriMesh,
// dimensions automatically calculated from geometry
});
Physics Material Properties
Density
Controls the mass of the object. Higher density = heavier object.
// Light object (foam ball)
entity.addComponent(PhysicsShape, {
shape: PhysicsShapeType.Sphere,
dimensions: [0.5, 0, 0],
density: 0.1,
});
// Heavy object (metal ball)
entity.addComponent(PhysicsShape, {
shape: PhysicsShapeType.Sphere,
dimensions: [0.5, 0, 0],
density: 10.0,
});
Friction
Controls sliding behavior on surfaces (0 = no friction, 1 = high friction).
// Slippery ice surface
iceEntity.addComponent(PhysicsShape, {
shape: PhysicsShapeType.Box,
dimensions: [10, 0.1, 10],
friction: 0.1,
});
// Grippy rubber surface
rubberEntity.addComponent(PhysicsShape, {
shape: PhysicsShapeType.Box,
dimensions: [10, 0.1, 10],
friction: 0.9,
});
Restitution
Controls bounciness (0 = no bounce, 1 = perfect bounce).
// Dead ball (no bounce)
deadBallEntity.addComponent(PhysicsShape, {
shape: PhysicsShapeType.Sphere,
dimensions: [0.5, 0, 0],
restitution: 0.0,
});
// Super bouncy ball
bouncyBallEntity.addComponent(PhysicsShape, {
shape: PhysicsShapeType.Sphere,
dimensions: [0.5, 0, 0],
restitution: 0.95,
});
PhysicsBody
Defines the motion behavior of an entity in the physics simulation.
entity.addComponent(PhysicsBody, {
state: PhysicsState.Dynamic, // Required: motion type
});
Motion Types
PhysicsState.Static
- Immovable objects like walls and floors- Never moves, but affects other bodies
- Ideal for environment geometry
PhysicsState.Dynamic
- Responds to forces and gravity- Affected by collisions, gravity, and applied forces
- Most common for interactive objects
PhysicsState.Kinematic
- Programmatically controlled- Can be moved by code but not affected by other bodies
- Useful for moving platforms or player-controlled objects
// Static floor
floorEntity.addComponent(PhysicsBody, { state: PhysicsState.Static });
// Dynamic ball that falls and bounces
ballEntity.addComponent(PhysicsBody, { state: PhysicsState.Dynamic });
// Kinematic elevator platform
elevatorEntity.addComponent(PhysicsBody, { state: PhysicsState.Kinematic });
PhysicsManipulation
The PhysicsManipulation
component allows you to apply one-time forces and set velocities. The component is automatically removed after application.
Applying Forces
Forces are applied as impulses at the entity's center of mass:
// Make object jump
entity.addComponent(PhysicsManipulation, {
force: [0, 10, 0], // Upward force
});
// Throw object forward
entity.addComponent(PhysicsManipulation, {
force: [5, 2, 0], // Forward and up
});
Setting Velocities
Directly set linear and angular velocities:
// Set specific movement
entity.addComponent(PhysicsManipulation, {
linearVelocity: [3, 0, 0], // Move right at 3 m/s
angularVelocity: [0, 2, 0], // Spin around Y-axis
});
Combined Manipulation
You can combine forces and velocities in a single manipulation:
entity.addComponent(PhysicsManipulation, {
force: [0, 5, 0], // Push up
linearVelocity: [2, 0, 0], // Set rightward velocity
angularVelocity: [0, 1, 0], // Add spin
});
Common Patterns
Creating a Falling Object
const box = new Mesh(new BoxGeometry(1, 1, 1), new MeshStandardMaterial());
box.position.set(0, 10, 0);
scene.add(box);
const entity = world.createTransformEntity(box);
entity.addComponent(PhysicsShape, {
shape: PhysicsShapeType.Auto,
});
entity.addComponent(PhysicsBody, {
state: PhysicsState.Dynamic,
});
Creating a Static Environment
const floor = new Mesh(new PlaneGeometry(20, 20), new MeshStandardMaterial());
floor.position.set(0, 0, 0);
scene.add(floor);
const floorEntity = world.createTransformEntity(floor);
floorEntity.addComponent(PhysicsShape, {
shape: PhysicsShapeType.Auto,
});
floorEntity.addComponent(PhysicsBody, {
state: PhysicsState.Static,
});
Interactive Grabbable Objects
// Create a grabbable ball
const ball = new Mesh(
new SphereGeometry(0.2),
new MeshStandardMaterial({ color: 0x00ff00 }),
);
ball.position.set(0, 2, 0);
scene.add(ball);
const ballEntity = world.createTransformEntity(ball);
ballEntity.addComponent(PhysicsShape, {
shape: PhysicsShapeType.Sphere,
dimensions: [0.2],
density: 0.5,
restitution: 0.6,
});
ballEntity.addComponent(PhysicsBody, {
state: PhysicsState.Dynamic,
});
// Add grabbing capability (requires GrabSystem)
ballEntity.addComponent(Interactable);
ballEntity.addComponent(OneHandGrabbable);
Kinematic Moving Platforms
const platform = new Mesh(
new BoxGeometry(3, 0.2, 3),
new MeshStandardMaterial({ color: 0x0000ff }),
);
scene.add(platform);
const platformEntity = world.createTransformEntity(platform);
platformEntity.addComponent(PhysicsShape, {
shape: PhysicsShapeType.Box,
dimensions: [3, 0.2, 3],
friction: 0.8,
});
platformEntity.addComponent(PhysicsBody, {
state: PhysicsState.Kinematic,
});
// Animate the platform (in your update loop)
function animatePlatform(time) {
platform.position.y = Math.sin(time * 0.001) * 2 + 2;
}
Performance Optimization
Use appropriate shape types:
- Primitives (Sphere, Box) are fastest
- ConvexHull for moderate complexity
- TriMesh only for static, highly detailed objects
Optimize shape complexity:
- Simplify meshes when possible. Use ConvexHull over TriMesh when possble.
- Use fewer vertices for ConvexHull and TriMesh shapes
Manage entity count:
- Remove physics components from distant objects
- Use object pooling for frequently created/destroyed objects
Troubleshooting
Common Issues
Physics objects fall through the floor:
- Ensure your floor has a
PhysicsShape
component - Make sure floor has a
PhysicsBody
component withPhysicsState.Static
- Check that floor dimensions match visual geometry
Objects don't collide:
- Verify both objects have
PhysicsShape
components - Check that shapes have appropriate dimensions
- Ensure objects are not starting inside each other
- Make sure floor has a
PhysicsBody
component withPhysicsState.Dynamic
Poor performance:
- Use simpler shape types when possible
- Reduce the number of physics entities
- Consider using TriMesh only for static objects
Objects behave unexpectedly:
- Check density values (very high/low values can cause issues)
- Verify friction and restitution are in reasonable ranges (0-1)
- Ensure forces aren't too large
Debug Tips
- Log component values to verify they're set correctly
- Test with simple primitive shapes before using complex geometry
- Check console for warnings from the physics system
MetaSpatial Integration
The physics components are also available in MetaSpatial projects through XML component definitions:
<!-- PhysicsBody component -->
<IWSDKPhysicsBody state="DYNAMIC" />
<!-- PhysicsShape component -->
<IWSDKPhysicsShape
shape="Box"
dimensions="2f, 1f, 1f"
density="1f"
friction="0.5f"
restitution="0f" />
<!-- PhysicsManipulation component -->
<IWSDKPhysicsManipulation
force="0f, 10f, 0f"
linearVelocity="0f, 0f, 0f"
angularVelocity="0f, 0f, 0f" />
Example Projects
Check out the examples/physics
project in the SDK for a complete working example that demonstrates:
- Basic physics setup
- Dynamic sphere creation
- Force application
- Integration with XR interactions
The example creates a bouncing sphere with applied forces and can be run with:
cd examples/physics
pnpm install
pnpm dev