UNPKG

@threlte/rapier

Version:

Components and hooks to use the Rapier physics engine in Threlte

113 lines (112 loc) 5.14 kB
import { ActiveEvents, Collider, ColliderDesc, RigidBody, World } from '@dimforge/rapier3d-compat'; import { isInstanceOf } from '@threlte/core'; import { Mesh, Quaternion, Vector3 } from 'three'; const offset = new Vector3(); const worldPosition = new Vector3(); const worldQuaternion = new Quaternion(); const worldScale = new Vector3(); const size = new Vector3(); const rigidBodyWorldPos = new Vector3(); const rigidBodyWorldQuatInversed = new Quaternion(); /** * * Creates collider descriptions including default translations * * @param object * @param world * @param friction * @param restitution * @param collidersType * @param rigidBody * @returns */ export const createCollidersFromChildren = (object, collidersType, world, rigidBody, rigidBodyParentObject) => { const colliders = []; let description; /** * Trying to find the parent RigidBody. * If we find something, good. If not, * the Colliders are created on the world positions * of the meshes they resemble. */ rigidBodyWorldPos.set(0, 0, 0); rigidBodyWorldQuatInversed.set(0, 0, 0, 1); rigidBodyParentObject?.getWorldPosition(rigidBodyWorldPos); rigidBodyParentObject?.getWorldQuaternion(rigidBodyWorldQuatInversed).invert(); object.traverse((child) => { if (isInstanceOf(child, 'Mesh')) { const { geometry } = child; const worldPos = child.getWorldPosition(worldPosition); const translation = worldPos.sub(rigidBodyWorldPos); const worldQuat = child.getWorldQuaternion(worldQuaternion); const rotation = worldQuat.clone().premultiply(rigidBodyWorldQuatInversed); const scale = child.getWorldScale(worldScale); offset.set(0, 0, 0); switch (collidersType) { case 'cuboid': { geometry.computeBoundingBox(); const { boundingBox } = geometry; boundingBox.getSize(size); boundingBox.getCenter(offset); description = ColliderDesc.cuboid((size.x / 2) * scale.x, (size.y / 2) * scale.y, (size.z / 2) * scale.z); } break; case 'ball': { geometry.computeBoundingSphere(); const { boundingSphere } = geometry; const radius = boundingSphere.radius * scale.x; offset.copy(boundingSphere.center); description = ColliderDesc.ball(radius); } break; case 'trimesh': { const { x: scaleX, y: scaleY, z: scaleZ } = scale; const vertices = new Float32Array(geometry.attributes.position.array); if (scaleX !== 1 || scaleY !== 1 || scaleZ !== 1) { for (let i = 0; i < vertices.length; i += 3) { vertices[i] *= scaleX; vertices[i + 1] *= scaleY; vertices[i + 2] *= scaleZ; } } description = ColliderDesc.trimesh(vertices, new Uint32Array(geometry.index?.array ?? Uint32Array.from(Array(vertices.length / 3).keys()))); } break; case 'capsule': { geometry.computeBoundingBox(); const { boundingBox } = geometry; boundingBox.getSize(size); boundingBox.getCenter(offset); const radius = Math.max((size.x / 2) * scale.x, (size.z / 2) * scale.z); description = ColliderDesc.capsule((size.y / 2) * scale.y - radius, radius); } break; case 'convexHull': { const { x: scaleX, y: scaleY, z: scaleZ } = scale; const vertices = new Float32Array(geometry.attributes.position.array); if (scaleX !== 1 || scaleY !== 1 || scaleZ !== 1) { for (let i = 0; i < vertices.length; i += 3) { vertices[i] *= scaleX; vertices[i + 1] *= scaleY; vertices[i + 2] *= scaleZ; } } description = ColliderDesc.convexHull(vertices); } break; } description .setTranslation(translation.x + offset.x, translation.y + offset.y, translation.z + offset.z) .setRotation(rotation) .setActiveEvents(ActiveEvents.COLLISION_EVENTS); const collider = world.createCollider(description, rigidBody); colliders.push(collider); } }); return colliders; };