### Quick Start: Physics World Setup Source: https://github.com/isaac-mason/crashcat/blob/main/docs/README.template.md A minimal example demonstrating how to create a physics world with a static ground and dynamic boxes. ```typescript import { createWorld, WorldSettings, Body, Shape, Material, RigidBody, World } from "crashcat" const world = createWorld({ gravity: [-9.81, 0, 0], worldSettings: WorldSettings.default }) // Ground world.addBody(new RigidBody({ isStatic: true, shape: new Shape.Box({ halfExtents: [100, 0.1, 100] }), })) // Dynamic boxes for (let i = 0; i < 10; i++) { world.addBody(new RigidBody({ position: [Math.random() * 10 - 5, 10 + i * 0.5, Math.random() * 10 - 5], shape: new Shape.Box({ halfExtents: [0.5, 0.5, 0.5] }), material: new Material({ restitution: 0.3, friction: 0.7 }), })) } ``` -------------------------------- ### Initialize and Display Examples Source: https://github.com/isaac-mason/crashcat/blob/main/examples/index.html This function populates the sidebar with example items, including titles and tags, fetched from a JSON file. It's used to set up the initial view of the examples list. ```javascript import examples from "./src/examples.json" assert { type: "json" }; function addExamplesToSidebar() { const examplesList = document.getElementById("examples-list"); const examplesHTML = Object.entries(examples) .map(([key, example]) => { const tagsHTML = example.tags .map((tag) => `${tag}`) .join(""); return `
  • ${example.title}

    ${example.title}

  • `; }) .join(""); examplesList.innerHTML = examplesHTML; } // initialize examples list addExamplesToSidebar(); ``` -------------------------------- ### Quick Start: Create Physics World and Simulate Source: https://github.com/isaac-mason/crashcat/blob/main/README.md This snippet demonstrates the basic setup for a Crashcat physics world. It includes registering shapes, configuring world settings, creating static and dynamic rigid bodies, and simulating the world over time. Ensure all necessary modules are imported and registered before use. ```typescript import { addBroadphaseLayer, addObjectLayer, box, type CollideShapeHit, type ContactManifold, type ContactSettings, ContactValidateResult, createWorld, createWorldSettings, enableCollision, type Listener, MotionType, type RigidBody, registerAll, rigidBody, updateWorld, } from 'crashcat'; import type { Vec3 } from 'mathcat'; // register all shapes registerAll(); // create a simple world const worldSettings = createWorldSettings(); export const BROADPHASE_LAYER_MOVING = addBroadphaseLayer(worldSettings); export const BROADPHASE_LAYER_NOT_MOVING = addBroadphaseLayer(worldSettings); export const OBJECT_LAYER_MOVING = addObjectLayer(worldSettings, BROADPHASE_LAYER_MOVING); export const OBJECT_LAYER_NOT_MOVING = addObjectLayer(worldSettings, BROADPHASE_LAYER_NOT_MOVING); enableCollision(worldSettings, OBJECT_LAYER_MOVING, OBJECT_LAYER_NOT_MOVING); enableCollision(worldSettings, OBJECT_LAYER_MOVING, OBJECT_LAYER_MOVING); worldSettings.gravity = [0, -9.81, 0]; const world = createWorld(worldSettings); // create a static ground rigidBody.create(world, { motionType: MotionType.STATIC, shape: box.create({ halfExtents: [10, 1, 10] }), objectLayer: OBJECT_LAYER_NOT_MOVING, }); // create a stack of dynamic boxes for (let i = 0; i < 5; i++) { rigidBody.create(world, { motionType: MotionType.DYNAMIC, shape: box.create({ halfExtents: [1, 1, 1] }), objectLayer: OBJECT_LAYER_MOVING, position: [0, 2 + i * 2, 0], }); } // simulate 10 seconds for (let i = 0; i < 60 * 10; i++) { // typically you will do this in a loop, e.g. requestAnimationFrame or setInterval // pass 'undefined' for no physics listener updateWorld(world, undefined, 1 / 60); } ``` -------------------------------- ### Kinematic Character Controller (KCC) Example Source: https://github.com/isaac-mason/crashcat/blob/main/docs/README.template.md Example demonstrating the setup of a Kinematic Character Controller for precise player movement, including features like wall sliding and stair stepping. ```typescript const kcc = new KCC(); world.add(kcc); // Example movement logic input.onMove((dx, dy) => { kcc.move(dx, dy, 0); }); ``` -------------------------------- ### Implement Example Search Functionality Source: https://github.com/isaac-mason/crashcat/blob/main/examples/index.html Filters the displayed examples based on user input in the search bar. It searches through example titles and tags, showing only matching items. ```javascript const searchInput = document.getElementById("search-input"); // search functionality function filterExamples() { const searchTerm = searchInput.value.toLowerCase(); const exampleItems = document.querySelectorAll(".examples-list li"); exampleItems.forEach((item) => { const exampleKey = item .querySelector(".example-item") .getAttribute("data-example"); const example = examples[exampleKey]; // search in title and tags const searchableText = [ example.title, ...example.tags, ] .join(" ") .toLowerCase(); if (searchableText.includes(searchTerm)) { item.style.display = ""; } else { item.style.display = "none"; } }); } // attach search event listener searchInput.addEventListener("input", filterExamples); ``` -------------------------------- ### Load and Display Selected Example Source: https://github.com/isaac-mason/crashcat/blob/main/examples/index.html Loads a specific example into the main frame based on its key. It updates the title, frame source, active item in the sidebar, and the URL hash. Also updates the source link. ```javascript const titleElement = document.getElementById("example-title"); const frameElement = document.getElementById("example-frame"); function loadExample(exampleKey, scrollIntoView) { const example = examples[exampleKey]; if (!example) return; titleElement.textContent = example.title; frameElement.src = `./${exampleKey}.html`; // update active item const exampleItems = document.querySelectorAll(".example-item"); let activeItem = null; exampleItems.forEach((item) => { item.classList.remove("active"); if (item.getAttribute("data-example") === exampleKey) { item.classList.add("active"); activeItem = item; } }); // scroll active item into view if (scrollIntoView && activeItem) { activeItem.scrollIntoView({ block: "center", behavior: "auto" }); } // update URL window.location.hash = exampleKey; // update source link const sourceLink = document.getElementById("source-link"); sourceLink.href = `https://github.com/isaac-mason/crashcat/blob/main/examples/src/${exampleKey}.ts`; // close mobile sidebar sidebar.classList.remove("open"); sidebarOverlay.classList.remove("show"); } ``` -------------------------------- ### Install Crashcat Source: https://github.com/isaac-mason/crashcat/blob/main/docs/README.template.md Install the crashcat package using npm. ```bash > npm install crashcat ``` -------------------------------- ### Handle URL Hash Changes for Examples Source: https://github.com/isaac-mason/crashcat/blob/main/examples/index.html Listens for changes in the URL hash to load the corresponding example. If no hash is present or valid, it loads the first example in the list. This ensures deep linking and correct initial state. ```javascript // handle hash changes function handleHashChange(scrollIntoView = false) { const hash = window.location.hash.substring(1); if (hash && examples[hash]) { loadExample(hash, scrollIntoView); } else { // load first example by default const firstExample = Object.keys(examples)[0]; loadExample(firstExample, scrollIntoView); } } // initialize handleHashChange(true); window.addEventListener("hashchange", () => handleHashChange(true)); ``` -------------------------------- ### Handle Sidebar and Example Clicks Source: https://github.com/isaac-mason/crashcat/blob/main/examples/index.html Manages user interactions with the sidebar and example items. Clicking an example loads it into the main frame and updates the URL hash. Handles mobile sidebar toggling. ```javascript const hamburger = document.getElementById("hamburger"); const sidebar = document.getElementById("sidebar"); const sidebarOverlay = document.getElementById("sidebar-overlay"); // handle sidebar clicks function attachEventListeners() { const exampleItems = document.querySelectorAll(".example-item"); exampleItems.forEach((item) => { item.addEventListener("click", (e) => { e.preventDefault(); const exampleKey = item.getAttribute("data-example"); loadExample(exampleKey); }); }); } // mobile navigation hamburger.addEventListener("click", () => { sidebar.classList.toggle("open"); sidebarOverlay.classList.toggle("show"); }); sidebarOverlay.addEventListener("click", () => { sidebar.classList.remove("open"); sidebarOverlay.classList.remove("show"); }); attachEventListeners(); ``` -------------------------------- ### Crashcat Constraint Types Examples Source: https://github.com/isaac-mason/crashcat/blob/main/README.md Provides examples for creating various constraint types in Crashcat, including Point, Distance, Hinge, Slider, and Fixed constraints. Note the specific parameters required for each type. ```typescript // point constraint - connects two bodies at a point (removes 3 DOF) const point = pointConstraint.create(world, { bodyIdA: bodyA.id, bodyIdB: bodyB.id, pointA: [0, 1, 0], pointB: [0, 0, 0], }); // distance constraint - maintains distance between two points (removes 1 DOF) const distance = distanceConstraint.create(world, { bodyIdA: bodyA.id, bodyIdB: bodyB.id, pointA: [0, 1, 0], pointB: [0, 0, 0], minDistance: 1, maxDistance: 2, }); // hinge constraint - allows rotation around an axis (removes 5 DOF) const hinge = hingeConstraint.create(world, { bodyIdA: bodyA.id, bodyIdB: bodyB.id, pointA: [0, 0.5, 0], pointB: [0, -0.5, 0], hingeAxisA: [0, 0, 1], hingeAxisB: [0, 0, 1], normalAxisA: [1, 0, 0], normalAxisB: [1, 0, 0], space: ConstraintSpace.LOCAL, }); // slider constraint - allows movement along an axis (removes 5 DOF) const slider = sliderConstraint.create(world, { bodyIdA: bodyA.id, bodyIdB: bodyB.id, pointA: [0, 0, 0], pointB: [0, 0, 0], sliderAxisA: [1, 0, 0], sliderAxisB: [1, 0, 0], normalAxisA: [0, 1, 0], normalAxisB: [0, 1, 0], space: ConstraintSpace.LOCAL, limitsMin: -2, limitsMax: 2, }); // fixed constraint - completely locks two bodies together (removes 6 DOF) const fixed = fixedConstraint.create(world, { bodyIdA: bodyA.id, bodyIdB: bodyB.id, point1: [0, 0, 0], point2: [0, 0, 0], axisX1: [1, 0, 0], axisY1: [0, 1, 0], axisX2: [1, 0, 0], axisY2: [0, 1, 0], space: ConstraintSpace.LOCAL, }); ``` -------------------------------- ### Configure and Use Query Filters Source: https://github.com/isaac-mason/crashcat/blob/main/README.md Demonstrates how to create, modify, and use query filters for various collision scenarios. Includes examples for enabling/disabling layers, setting collision groups/masks, and implementing custom body filter callbacks. ```typescript // filters control what queries can hit, using object layers and collision groups/masks // basic: create a filter with all layers enabled const worldQueryFilter = filter.create(world.settings.layers); // filter specific object layers filter.disableObjectLayer(worldQueryFilter, world.settings.layers, OBJECT_LAYER_DEBRIS); filter.enableObjectLayer(worldQueryFilter, world.settings.layers, OBJECT_LAYER_MOVING); // filter specific broadphase layers filter.disableBroadphaseLayer(worldQueryFilter, world.settings.layers, BROADPHASE_LAYER_MOVING); filter.enableBroadphaseLayer(worldQueryFilter, world.settings.layers, BROADPHASE_LAYER_NOT_MOVING); // filter everything, then selectively enable filter.disableAllLayers(worldQueryFilter, world.settings.layers); filter.enableObjectLayer(worldQueryFilter, world.settings.layers, OBJECT_LAYER_MOVING); // collision groups and masks (works alongside layer filtering) worldQueryFilter.collisionGroups = 0b0001; // query belongs to group 1 worldQueryFilter.collisionMask = 0b0010 | 0b0100; // query hits groups 2 and 4 // custom body filter callback worldQueryFilter.bodyFilter = (body: RigidBody) => { // custom logic - exclude specific bodies if (body.userData === 'player') return false; // only hit dynamic bodies if (body.motionType !== MotionType.DYNAMIC) return false; return true; }; // setFromBody: configure filter to match what a body can collide with const playerBody = rigidBody.get(world, playerId)!; filter.setFromBody(worldQueryFilter, world.settings.layers, playerBody); // copy filter settings const rayFilter = filter.create(world.settings.layers); filter.copy(rayFilter, worldQueryFilter); // use filter in queries castRay(world, closestCollector, raySettings, rayOrigin, rayDirection, rayLength, worldQueryFilter); ``` -------------------------------- ### World Settings Configuration Source: https://github.com/isaac-mason/crashcat/blob/main/docs/README.template.md Example of configuring world settings, including broadphase and object layers for collision filtering. ```typescript import { createWorld, WorldSettings, BroadphaseLayer, ObjectLayer } from "crashcat" const world = createWorld({ gravity: [0, -9.81, 0], worldSettings: { ...WorldSettings.default, broadphaseLayers: [ new BroadphaseLayer({ name: "moving", objectLayers: ["player", "enemy", "projectile"] }), new BroadphaseLayer({ name: "static", objectLayers: ["terrain"] }), ], objectLayers: [ new ObjectLayer({ name: "player", broadphaseLayer: "moving" }), new ObjectLayer({ name: "enemy", broadphaseLayer: "moving" }), new ObjectLayer({ name: "terrain", broadphaseLayer: "static" }), new ObjectLayer({ name: "projectile", broadphaseLayer: "moving" }), ], collisionRules: [ { from: "player", to: "enemy", canCollide: true }, { from: "player", to: "terrain", canCollide: true }, { from: "projectile", to: "enemy", canCollide: true }, { from: "projectile", to: "player", canCollide: false }, ], }, }) ``` -------------------------------- ### Create and Configure Physics World Source: https://github.com/isaac-mason/crashcat/blob/main/README.md Sets up broadphase and object layers, defines collision rules, and creates the physics world with specified settings like gravity. Use registerAll() for easy setup or granular registration for better tree shaking. ```typescript import { addBroadphaseLayer, addObjectLayer, createWorld, createWorldSettings, enableCollision, registerAll } from 'crashcat'; // we can use registerShapes and registerConstraints to granularly declare // which shapes and constraints we want to use for the best tree shaking. // but early in development, it's easier to just register everything. registerAll(); // this is a container for all settings related to world simulation. // in a real project, you'd put this in a e.g. "physics-world-settings.ts" file seperate from the physics world // creation, and import it and below constants where needed. const worldSettings = createWorldSettings(); // earth gravity worldSettings.gravity = [0, -9.81, 0]; // we're first up going to define "broadphase layers". // for simple projects, a "moving" and "not moving" broadphase layer is a good start. export const BROADPHASE_LAYER_MOVING = addBroadphaseLayer(worldSettings); export const BROADPHASE_LAYER_NOT_MOVING = addBroadphaseLayer(worldSettings); // next, we'll define some "object layers". export const OBJECT_LAYER_MOVING = addObjectLayer(worldSettings, BROADPHASE_LAYER_MOVING); export const OBJECT_LAYER_NOT_MOVING = addObjectLayer(worldSettings, BROADPHASE_LAYER_NOT_MOVING); // here we declare that "moving" objects should collide with "not moving" objects, and with other "moving" objects. enableCollision(worldSettings, OBJECT_LAYER_MOVING, OBJECT_LAYER_NOT_MOVING); enableCollision(worldSettings, OBJECT_LAYER_MOVING, OBJECT_LAYER_MOVING); // time to create the physics world from the settings we've defined const world = createWorld(worldSettings); ``` -------------------------------- ### Floating Capsule Character Controller Example Source: https://github.com/isaac-mason/crashcat/blob/main/docs/README.template.md User-land implementation of a dynamic character controller using a rigid body and constraints. Suitable for AI or physics-based characters. ```typescript const body = new RigidBody({ /* ... */ }); const capsuleShape = new CapsuleShape(radius, height); world.add(body, capsuleShape); // Add constraints to keep the capsule upright const rotationConstraint = new RotationConstraint(); body.addConstraint(rotationConstraint); ``` -------------------------------- ### Three.js Debug Renderer Example Source: https://github.com/isaac-mason/crashcat/blob/main/docs/README.template.md This snippet demonstrates how to use the Three.js debug renderer provided by the 'crashcat/three' package to visualize physics simulation state during development. It's useful for debugging but can impact performance if visualizing many elements. ```typescript import * as THREE from 'three'; import { World } from 'crashcat'; import { DebugRenderer } from 'crashcat/three'; const world = new World(); const scene = new THREE.Scene(); const renderer = new THREE.WebGLRenderer(); // ... setup world and scene ... const debugRenderer = new DebugRenderer(world, scene); function animate() { world.updateWorld(1 / 60); debugRenderer.render(); renderer.render(scene, camera); requestAnimationFrame(animate); } animate(); ``` -------------------------------- ### Change Rigid Body Motion Type Source: https://github.com/isaac-mason/crashcat/blob/main/README.md Shows how to change a rigid body's motion type dynamically at runtime. This allows switching between static, kinematic, and dynamic behaviors, for example, when a player picks up or drops an object. ```typescript // change a body's motion type at runtime const switchableBody = rigidBody.create(world, { shape: box.create({ halfExtents: [1, 1, 1] }), motionType: MotionType.DYNAMIC, objectLayer: OBJECT_LAYER_MOVING, }); // make it kinematic (e.g. player grabs it) rigidBody.setMotionType(world, switchableBody, MotionType.KINEMATIC, true); // make it dynamic again (e.g. player drops it) rigidBody.setMotionType(world, switchableBody, MotionType.DYNAMIC, true); // make it static (e.g. permanently attach to world) rigidBody.setMotionType(world, switchableBody, MotionType.STATIC, false); ``` -------------------------------- ### Selective Shape Registration Source: https://github.com/isaac-mason/crashcat/blob/main/docs/README.template.md Register only the shapes you need to reduce bundle size. This example includes sphere, box, and capsule shapes. ```typescript import { registerShapes } from "crashcat"; registerShapes({ sphere: sphere.create, box: box.create, capsule: capsule.create, }); ``` -------------------------------- ### Create Rigid Bodies with Different Motion Types Source: https://github.com/isaac-mason/crashcat/blob/main/README.md Shows how to create static, dynamic, and kinematic rigid bodies. Static bodies are immovable, dynamic bodies are fully simulated, and kinematic bodies are controlled by user velocity and can push dynamic bodies. ```typescript // static: cannot move, infinite mass, not affected by forces const staticBody = rigidBody.create(world, { shape: box.create({ halfExtents: [5, 0.5, 5] }), motionType: MotionType.STATIC, objectLayer: OBJECT_LAYER_NOT_MOVING, }); // dynamic: fully simulated, affected by forces, gravity, and contacts const dynamicBody = rigidBody.create(world, { shape: sphere.create({ radius: 1 }), motionType: MotionType.DYNAMIC, objectLayer: OBJECT_LAYER_MOVING, }); // kinematic: user-controlled velocity, pushes dynamic bodies const kinematicBody = rigidBody.create(world, { shape: box.create({ halfExtents: [2, 0.2, 2] }), motionType: MotionType.KINEMATIC, objectLayer: OBJECT_LAYER_MOVING, }); ``` -------------------------------- ### Create and Remove a Point Constraint Source: https://github.com/isaac-mason/crashcat/blob/main/README.md Demonstrates the creation of two rigid bodies and a point constraint connecting them. Shows how to enable/disable and remove the constraint. ```typescript const world = createWorld(createWorldSettings()); // create two bodies const bodyA = rigidBody.create(world, { shape: box.create({ halfExtents: [0.5, 0.5, 0.5] }), objectLayer: 0, motionType: MotionType.STATIC, position: [0, 5, 0], }); const bodyB = rigidBody.create(world, { shape: sphere.create({ radius: 0.5 }), objectLayer: 1, motionType: MotionType.DYNAMIC, position: [0, 3, 0], }); // create a point constraint connecting them const constraint = pointConstraint.create(world, { bodyIdA: bodyA.id, bodyIdB: bodyB.id, pointA: [0, 4.5, 0], // point on bodyA in world space pointB: [0, 3.5, 0], // point on bodyB in world space space: ConstraintSpace.WORLD, }); // constraints can be enabled/disabled constraint.enabled = false; // remove constraint when done pointConstraint.remove(world, constraint); ``` -------------------------------- ### Physics and Rendering Units Source: https://github.com/isaac-mason/crashcat/blob/main/docs/README.template.md Crashcat uses SI units (meters, kilograms, seconds). Scale physics positions to rendering pixels for display. This example shows a human-scale body and rendering at 50 pixels per meter. ```typescript // physics: 1 meter = human-scale const body = rigidBody.create(world, { shape: capsule.create({ radius: 0.3, halfHeightOfCylinder: 0.7 }), // ~1.4m tall position: [0, 10, 0], // 10 meters up // ... }); // rendering: scale physics position to pixels const renderPosition = [ body.position[0] * 50, // 50 pixels per meter body.position[1] * 50, body.position[2] * 50, ]; ``` -------------------------------- ### Register All Shapes and Constraints Source: https://github.com/isaac-mason/crashcat/blob/main/docs/README.template.md Use registerAll() for simplicity during development. This must be called before creating any bodies or constraints. ```typescript import { registerAll } from "crashcat"; // MUST call this before updateWorld, or doing world queries registerAll(); ``` -------------------------------- ### Create Rigid Body with Automatic Mass Properties Source: https://github.com/isaac-mason/crashcat/blob/main/README.md Shows how to create a dynamic rigid body where mass properties are automatically calculated based on shape and density. ```typescript // mass properties are computed automatically from shape and density const body = rigidBody.create(world, { shape: sphere.create({ radius: 1, density: 1000 }), // density in kg/m³ motionType: MotionType.DYNAMIC, objectLayer: OBJECT_LAYER_MOVING, }); ``` -------------------------------- ### Create and Remove Rigid Bodies Source: https://github.com/isaac-mason/crashcat/blob/main/README.md Demonstrates how to create dynamic and static box bodies, and how to remove a body from the simulation world. Ensure correct object layers are assigned for collision filtering. ```typescript const dynamicBox = rigidBody.create(world, { shape: box.create({ halfExtents: [1, 1, 1] }), motionType: MotionType.DYNAMIC, objectLayer: OBJECT_LAYER_MOVING, position: [0, 5, 0], }); const ground = rigidBody.create(world, { shape: box.create({ halfExtents: [10, 0.5, 10] }), motionType: MotionType.STATIC, objectLayer: OBJECT_LAYER_NOT_MOVING, position: [0, -0.5, 0], }); rigidBody.remove(world, dynamicBox); ``` -------------------------------- ### Basic Constraint Creation Source: https://github.com/isaac-mason/crashcat/blob/main/docs/README.template.md Demonstrates the creation and removal of a basic constraint between two bodies. ```typescript const bodyA = world.createBody({ position: new Vector2(0, 0) }); const bodyB = world.createBody({ position: new Vector2(10, 0) }); const constraint = new PointConstraint(bodyA, bodyB); world.createConstraint(constraint); // ... later ... world.destroyConstraint(constraint); ``` -------------------------------- ### Create Rigid Bodies with Material Properties Source: https://github.com/isaac-mason/crashcat/blob/main/README.md Demonstrates creating rigid bodies with different friction and restitution values. Use these settings to control how surfaces interact during collisions. Custom combine modes can also be specified. ```typescript // low friction, high restitution const bouncyBall = rigidBody.create(world, { shape: sphere.create({ radius: 1 }), motionType: MotionType.DYNAMIC, objectLayer: OBJECT_LAYER_MOVING, friction: 0.1, restitution: 0.9, }); // high friction, no bounce const stickyBox = rigidBody.create(world, { shape: box.create({ halfExtents: [1, 1, 1] }), motionType: MotionType.DYNAMIC, objectLayer: OBJECT_LAYER_MOVING, friction: 1.0, restitution: 0.0, }); // custom combine modes const customCombine = rigidBody.create(world, { shape: sphere.create({ radius: 1 }), motionType: MotionType.DYNAMIC, objectLayer: OBJECT_LAYER_MOVING, friction: 0.5, restitution: 0.5, frictionCombineMode: MaterialCombineMode.MIN, restitutionCombineMode: MaterialCombineMode.MAX, }); ``` -------------------------------- ### Read and Set Rigid Body Transform Source: https://github.com/isaac-mason/crashcat/blob/main/README.md Demonstrates how to read the current position and quaternion of a rigid body, and how to set them together or separately. ```typescript // read position and quaternion const position = dynamicBody.position; // [x, y, z] const quaternion = dynamicBody.quaternion; // [x, y, z, w] // set position and quaternion together const newPosition = vec3.fromValues(0, 10, 0); const newQuaternion = quat.create(); rigidBody.setTransform(world, kinematicBody, newPosition, newQuaternion, false); // set position and quaternion separately igidBody.setPosition(world, kinematicBody, newPosition, false); igidBody.setQuaternion(world, kinematicBody, newQuaternion, false); ``` -------------------------------- ### Apply Forces and Impulses to Rigid Bodies Source: https://github.com/isaac-mason/crashcat/blob/main/README.md Illustrates how to add forces and impulses to a rigid body, both at its center of mass and at specific world positions to induce rotation. ```typescript // add force at center of mass (accumulates until next physics step) const force = vec3.fromValues(0, 100, 0); // upward force rigidBody.addForce(world, dynamicBody, force, true); // last arg: wake if sleeping // add force at specific position (generates torque) const worldPosition = vec3.fromValues(1, 0, 0); // apply force at right edge rigidBody.addForceAtPosition(world, dynamicBody, force, worldPosition, true); // add impulse (instant velocity change at center of mass) const impulse = vec3.fromValues(0, 10, 0); rigidBody.addImpulse(world, dynamicBody, impulse); // add impulse at position (instant velocity + angular velocity change) rigidBody.addImpulseAtPosition(world, dynamicBody, impulse, worldPosition); ``` -------------------------------- ### Create and Remove Rigid Bodies Source: https://github.com/isaac-mason/crashcat/blob/main/docs/README.template.md Demonstrates the creation and removal of rigid bodies. Be cautious about storing direct references to body objects due to internal pooling; prefer storing and retrieving by ID. ```typescript const body = rigidBody.create( { position: new Vector3(0, 10, 0), rotation: new Quaternion(0, 0, 0, 1), // ... other properties }, { // ... other options } ); // ... later rigidBody.destroy(body); ``` -------------------------------- ### Fixed Timestep Simulation Update Source: https://github.com/isaac-mason/crashcat/blob/main/README.md Steps the physics simulation at a fixed rate (e.g., 60 Hz) for maximum stability and determinism, decoupling it from the render frame rate. Interpolate body positions using alpha for smooth rendering. ```javascript const PHYSICS_DT = 1 / 60; let accumulator = 0; let lastTimeFixed = performance.now(); function gameLoopFixedTimestep() { const currentTime = performance.now(); const frameTime = Math.min((currentTime - lastTimeFixed) / 1000, 0.25); lastTimeFixed = currentTime; accumulator += frameTime; // step physics at fixed rate while (accumulator >= PHYSICS_DT) { updateWorld(world, undefined, PHYSICS_DT); accumulator -= PHYSICS_DT; } // ... render with interpolation ... // const alpha = accumulator / PHYSICS_DT; // interpolate body positions using alpha for smooth rendering requestAnimationFrame(gameLoopFixedTimestep); } ``` -------------------------------- ### Set Rigid Body Mass Properties Source: https://github.com/isaac-mason/crashcat/blob/main/docs/README.template.md Demonstrates setting mass properties for rigid bodies. This is usually computed automatically, but must be provided explicitly for triangle meshes. ```typescript rigidBody.setMassProperties(body, { mass: 10, centerOfMass: new Vector3(0, 0, 0), // ... other properties }); ``` -------------------------------- ### Enable Continuous Collision Detection (CCD) Source: https://github.com/isaac-mason/crashcat/blob/main/README.md Configure `MotionQuality.LINEAR_CAST` for dynamic bodies to prevent tunneling through thin objects. Adjust the `ccd.linearCastThreshold` in world settings. ```typescript const bullet = rigidBody.create(world, { shape: sphere.create({ radius: 0.1 }), motionType: MotionType.DYNAMIC, objectLayer: OBJECT_LAYER_MOVING, motionQuality: MotionQuality.LINEAR_CAST, // enables ccd }); // configure threshold in world settings (default 0.05 = 5%) // worldSettings.ccd.linearCastThreshold = 0.05; ``` -------------------------------- ### Basic Listener Implementation Source: https://github.com/isaac-mason/crashcat/blob/main/docs/README.template.md Implement a listener to react to and modify physics events during world updates. Pass the listener to `updateWorld()` to receive collision callbacks. ```typescript class MyListener implements PhysicsEventListener { onCollisionEnter(handleA: RigidBodyHandle, handleB: RigidBodyHandle, contactManifold: ContactManifold) { console.log('Collision detected between', handleA, 'and', handleB); } } const listener = new MyListener(); world.updateWorld(deltaTime, listener); ``` -------------------------------- ### Create Rigid Body with Overridden Mass Source: https://github.com/isaac-mason/crashcat/blob/main/README.md Demonstrates creating a dynamic rigid body and overriding only its mass, allowing inertia to be scaled automatically. ```typescript // override just the mass (inertia is scaled automatically) const customMassBody = rigidBody.create(world, { shape: box.create({ halfExtents: [1, 1, 1] }), motionType: MotionType.DYNAMIC, objectLayer: OBJECT_LAYER_MOVING, mass: 50, // kg }); ``` -------------------------------- ### Create a Sensor Body for Collision Detection Source: https://github.com/isaac-mason/crashcat/blob/main/README.md Shows how to create a sensor body that detects collisions without applying physical forces. This is useful for creating trigger zones or detection areas. A listener can be set up to react to bodies entering the sensor. ```typescript const triggerZone = rigidBody.create(world, { shape: box.create({ halfExtents: [5, 5, 5] }), motionType: MotionType.STATIC, objectLayer: OBJECT_LAYER_NOT_MOVING, sensor: true, }); // detect when bodies enter/exit sensor const listener: Listener = { onContactAdded: (bodyA, bodyB, manifold, settings) => { if (bodyA.id === triggerZone.id || bodyB.id === triggerZone.id) { // otherBody entered trigger zone const otherBody = bodyA.id === triggerZone.id ? bodyB : bodyA; } }, }; ``` -------------------------------- ### Stepping Simulation: Fixed Timestep Source: https://github.com/isaac-mason/crashcat/blob/main/docs/README.template.md Implement a fixed physics timestep with an accumulator for maximum stability and determinism. This decouples physics from rendering rate. ```typescript import { updateWorld } from "crashcat" const FIXED_TIMESTEP = 1 / 60 // 60 Hz let accumulator = 0 // In your game loop: const deltaTime = getDeltaTime() // Time since last frame in seconds accumulator += deltaTime while (accumulator >= FIXED_TIMESTEP) { updateWorld(world, listener, FIXED_TIMESTEP) accumulator -= FIXED_TIMESTEP } const alpha = accumulator / FIXED_TIMESTEP // Interpolation factor for rendering ``` -------------------------------- ### Enable Continuous Collision Detection (CCD) Source: https://github.com/isaac-mason/crashcat/blob/main/docs/README.template.md Shows how to enable Continuous Collision Detection (CCD) for a rigid body. CCD is crucial for fast-moving objects like bullets or vehicles to prevent them from tunneling through thin walls. ```typescript rigidBody.setCcdEnabled(body, true); ``` -------------------------------- ### Add Force and Impulse to Rigid Body Source: https://github.com/isaac-mason/crashcat/blob/main/docs/README.template.md Shows how to apply forces and impulses to a rigid body. Forces accumulate until the next physics step, while impulses provide instant velocity changes. Use `addForceAtPosition` or `addImpulseAtPosition` to induce rotation. ```typescript rigidBody.addForceAtPosition(body, new Vector3(0, 100, 0), new Vector3(0, 0, 1)); rigidBody.addImpulseAtPosition(body, new Vector3(0, 50, 0), new Vector3(0, 0, -1)); ``` -------------------------------- ### Read Rigid Body Mass Properties Source: https://github.com/isaac-mason/crashcat/blob/main/README.md Shows how to read the mass and inverse mass from a rigid body's motion properties. ```typescript // read mass properties const mass = 1 / body.motionProperties.invMass; // mass in kg const invMass = body.motionProperties.invMass; // 1/mass, used internally ``` -------------------------------- ### Control Rigid Body Sleeping Source: https://github.com/isaac-mason/crashcat/blob/main/docs/README.template.md Illustrates how to control the sleeping state of rigid bodies. Sleeping improves performance by skipping simulation for bodies that are at rest. ```typescript rigidBody.sleep(body); rigidBody.wakeUp(body); ``` -------------------------------- ### Cast Ray Through World Source: https://github.com/isaac-mason/crashcat/blob/main/README.md Demonstrates casting a ray to detect collisions with bodies in the world. Useful for line-of-sight checks and projectile trajectories. It shows how to use 'closest', 'any', and 'all' collectors. ```typescript // cast a ray through the world to find bodies const rayOrigin = vec3.fromValues(0, 5, 0); const rayDirection = vec3.fromValues(0, -1, 0); const rayLength = 100; // create a filter to control what the ray can hit const queryFilter = filter.create(world.settings.layers); // closest: finds the nearest hit along the ray const closestCollector = createClosestCastRayCollector(); const raySettings = createDefaultCastRaySettings(); castRay(world, closestCollector, raySettings, rayOrigin, rayDirection, rayLength, queryFilter); if (closestCollector.hit.status === CastRayStatus.COLLIDING) { const hitDistance = closestCollector.hit.fraction * rayLength; const hitPoint = vec3.scaleAndAdd(vec3.create(), rayOrigin, rayDirection, hitDistance); const hitBody = rigidBody.get(world, closestCollector.hit.bodyIdB)!; const surfaceNormal = rigidBody.getSurfaceNormal(vec3.create(), hitBody, hitPoint, closestCollector.hit.subShapeId); console.log('closest hit at', hitPoint); console.log('surface normal:', surfaceNormal); } // any: finds the first hit (fast early-out, useful for line-of-sight checks) const anyCollector = createAnyCastRayCollector(); anyCollector.reset(); castRay(world, anyCollector, raySettings, rayOrigin, rayDirection, rayLength, queryFilter); if (anyCollector.hit.status === CastRayStatus.COLLIDING) { console.log('ray hit something'); } // all: collects every hit along the ray const allCollector = createAllCastRayCollector(); allCollector.reset(); castRay(world, allCollector, raySettings, rayOrigin, rayDirection, rayLength, queryFilter); for (const hit of allCollector.hits) { if (hit.status === CastRayStatus.COLLIDING) { console.log('hit at fraction', hit.fraction); } } ``` -------------------------------- ### Create Rigid Body with Explicit Mass Properties Override Source: https://github.com/isaac-mason/crashcat/blob/main/README.md Explains how to completely override mass properties, which is particularly useful for triangle meshes. It shows setting mass and inertia for a box shape. ```typescript // completely override mass properties (useful for triangle meshes) const triangleMeshMassProperties = massProperties.create(); massProperties.setMassAndInertiaOfSolidBox(triangleMeshMassProperties, [2, 2, 2], 1000); // boxSize, density const dynamicTriangleMeshBody = rigidBody.create(world, { shape: box.create({ halfExtents: [1, 1, 1] }), motionType: MotionType.DYNAMIC, objectLayer: OBJECT_LAYER_MOVING, massPropertiesOverride: triangleMeshMassProperties, }); ``` -------------------------------- ### Create a Sphere Shape Source: https://github.com/isaac-mason/crashcat/blob/main/README.md Use sphere.create to define a sphere. You can specify the radius and density for mass calculation. ```typescript // simplest and fastest convex shape sphere.create({ radius: 1 }); // with density for mass calculation sphere.create({ radius: 1, density: 1000 }); // kg/m³ ``` -------------------------------- ### Read and Set Rigid Body Velocities Source: https://github.com/isaac-mason/crashcat/blob/main/README.md Shows how to access the linear and angular velocities of a dynamic body and how to modify them using the rigid body API. ```typescript // read velocities const linearVelocity = dynamicBody.motionProperties.linearVelocity; // [x, y, z] in m/s const angularVelocity = dynamicBody.motionProperties.angularVelocity; // [x, y, z] in rad/s // set linear velocity igidBody.setLinearVelocity(world, dynamicBody, [5, 0, 0]); // shoot to the right // set angular velocity igidBody.setAngularVelocity(world, dynamicBody, [0, 1, 0]); // spin around y-axis ``` -------------------------------- ### Move Kinematic Rigid Body Source: https://github.com/isaac-mason/crashcat/blob/main/docs/README.template.md Illustrates moving a kinematic rigid body to a target position and rotation using `moveKinematic`. This method computes necessary velocities for smooth interaction with dynamic bodies, avoiding teleportation. ```typescript rigidBody.moveKinematic(body, new Vector3(5, 5, 5), new Quaternion(0, 0, 0, 1)); ``` -------------------------------- ### Variable Timestep Simulation Update Source: https://github.com/isaac-mason/crashcat/blob/main/README.md Advances the physics simulation using the frame's delta time, suitable for simple use cases. Ensure delta time is capped to prevent instability. ```javascript let lastTime = performance.now(); const maxDelta = 1 / 30; function gameLoopVariableTimestep() { const currentTime = performance.now(); const delta = Math.min((currentTime - lastTime) / 1000, maxDelta); lastTime = currentTime; updateWorld(world, undefined, delta); // ... render ... requestAnimationFrame(gameLoopVariableTimestep); } ``` -------------------------------- ### Register All Shapes and Constraints Source: https://github.com/isaac-mason/crashcat/blob/main/docs/README.template.md Conveniently registers all built-in shapes and constraints for use in the physics world. This approach includes all features, potentially increasing bundle size. ```typescript import { registerAll } from '@crashcat/physics'; registerAll(); ``` -------------------------------- ### Object Pooling for Rigid Bodies Source: https://github.com/isaac-mason/crashcat/blob/main/docs/README.template.md Illustrates how rigid bodies are pooled internally for performance. It's recommended to store body IDs instead of direct references to avoid issues with invalidated IDs when bodies are removed and reused. ```typescript const bodyId = body.id; // ... later, to retrieve the body: const rigidBody = rigidBody.get(world, bodyId); ``` -------------------------------- ### Set Rigid Body Motion Types Source: https://github.com/isaac-mason/crashcat/blob/main/docs/README.template.md Shows how to set the motion type for a rigid body. Choose 'static' for immovable objects, 'dynamic' for objects affected by physics, and 'kinematic' for objects controlled by script/animation. ```typescript rigidBody.setMotionType(body, RigidBodyMotionType.Static); ``` -------------------------------- ### Move Kinematic Body with Velocities Source: https://github.com/isaac-mason/crashcat/blob/main/README.md Use `moveKinematic` to compute velocities for kinematic bodies to reach a target position and quaternion. Prefer this over `setTransform` for smooth movement. ```typescript const platform = rigidBody.create(world, { shape: box.create({ halfExtents: [2, 0.2, 2] }), motionType: MotionType.KINEMATIC, objectLayer: OBJECT_LAYER_MOVING, position: [0, 2, 0], }); // move each frame by computing velocities const deltaTime = 1 / 60; const targetPosition = vec3.fromValues(5, 2, 0); const targetQuaternion = quat.create(); quat.setAxisAngle(targetQuaternion, vec3.fromValues(0, 1, 0), Math.PI / 4); rigidBody.moveKinematic(platform, targetPosition, targetQuaternion, deltaTime); ``` -------------------------------- ### Set Rigid Body Maximum Velocities Source: https://github.com/isaac-mason/crashcat/blob/main/docs/README.template.md Shows how to set maximum linear and angular velocities for a rigid body. This helps prevent instability and tunneling issues caused by extremely high speeds. ```typescript rigidBody.setMaxVelocities(body, 100, 10); ``` -------------------------------- ### Attach User Data to Rigid Bodies Source: https://github.com/isaac-mason/crashcat/blob/main/docs/README.template.md Attach custom game-specific data to rigid bodies for easy lookup during collision callbacks. ```typescript const body = world.createRigidBody(rigidBodyDesc); body.userData = { level: 1, health: 100 }; ``` -------------------------------- ### Initialize and Update Three.js Debug Renderer Source: https://github.com/isaac-mason/crashcat/blob/main/README.md Initializes the debug renderer with default options and updates it each frame after the physics step. Ensure the debug renderer's object3d is added to your Three.js scene. ```typescript import type { World } from 'crashcat'; import { debugRenderer } from 'crashcat/three'; import type * as THREE from 'three'; declare const scene: THREE.Scene; declare const world: World; // create debug renderer with default options const options = debugRenderer.createDefaultOptions(); const state = debugRenderer.init(options); // add to scene scene.add(state.object3d); // update each frame after physics step function animate() { // ... update physics ... debugRenderer.update(state, world); // ... render scene ... } ``` -------------------------------- ### Set Rigid Body Damping Source: https://github.com/isaac-mason/crashcat/blob/main/docs/README.template.md Illustrates setting linear and angular damping for a rigid body. Higher damping values simulate increased air resistance or drag, causing the body to slow down faster. ```typescript rigidBody.setDamping(body, 0.5, 0.2); ``` -------------------------------- ### Customize Three.js Debug Renderer Visualization Options Source: https://github.com/isaac-mason/crashcat/blob/main/README.md Customizes which physics simulation elements are visualized and their appearance. Options include body visualization, contact points, constraints, and broadphase details. ```typescript const customOptions = debugRenderer.createDefaultOptions(); /* body visualization options */ customOptions.bodies.enabled = true; customOptions.bodies.wireframe = false; customOptions.bodies.showLinearVelocity = false; customOptions.bodies.showAngularVelocity = false; // unique color per body instance customOptions.bodies.color = debugRenderer.BodyColorMode.INSTANCE; // color by motion type (static, dynamic, kinematic) customOptions.bodies.color = debugRenderer.BodyColorMode.MOTION_TYPE; // color by sleeping state customOptions.bodies.color = debugRenderer.BodyColorMode.SLEEPING; // color by simulation island customOptions.bodies.color = debugRenderer.BodyColorMode.ISLAND; /* contact points options */ customOptions.contacts.enabled = true; /* contact constraints options */ customOptions.contactConstraints.enabled = true; /* constraints options (hinges, sliders, etc.) */ customOptions.constraints.enabled = true; customOptions.constraints.drawLimits = true; customOptions.constraints.size = 0.5; /* broadphase options */ customOptions.broadphaseDbvt.enabled = false; customOptions.broadphaseDbvt.showLeafNodes = true; customOptions.broadphaseDbvt.showNonLeafNodes = true; /* triangle mesh bvh options */ customOptions.triangleMeshBvh.enabled = false; customOptions.triangleMeshBvh.showLeafNodes = true; customOptions.triangleMeshBvh.showNonLeafNodes = true; ``` -------------------------------- ### Create a Triangle Mesh Shape Source: https://github.com/isaac-mason/crashcat/blob/main/README.md Triangle meshes are used for complex static geometry like terrain. They are created using positions and indices. For dynamic meshes, mass properties must be provided explicitly. ```typescript // triangle mesh: for complex static geometry like terrain const meshShape = triangleMesh.create({ positions: [-10, 0, -10, 10, 0, -10, 10, 0, 10, -10, 0, 10], indices: [0, 1, 2, 0, 2, 3], }); // triangle meshes typically used with static bodies rigidBody.create(world, { shape: meshShape, motionType: MotionType.STATIC, objectLayer: OBJECT_LAYER_NOT_MOVING, }); // for dynamic triangle meshes, provide mass properties explicitly const dynamicMeshProps = massProperties.create(); massProperties.setMassAndInertiaOfSolidBox(dynamicMeshProps, [2, 2, 2], 1000); rigidBody.create(world, { shape: meshShape, motionType: MotionType.DYNAMIC, objectLayer: OBJECT_LAYER_MOVING, massPropertiesOverride: dynamicMeshProps, }); ``` -------------------------------- ### Configure Linear and Angular Damping Source: https://github.com/isaac-mason/crashcat/blob/main/README.md Set linear and angular damping to simulate air resistance or drag. Higher values cause objects to slow down faster. Damping can be set during creation or modified afterward. ```typescript // linear damping reduces linear velocity over time (0-1, higher = more drag) const dampedBody = rigidBody.create(world, { shape: sphere.create({ radius: 1 }), motionType: MotionType.DYNAMIC, objectLayer: OBJECT_LAYER_MOVING, linearDamping: 0.5, // default is 0.05 angularDamping: 0.8, // default is 0.05 }); // damping can also be modified after creation dampedBody.motionProperties.linearDamping = 0.2; dampedBody.motionProperties.angularDamping = 0.2; ```