Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 70 additions & 65 deletions examples/jsm/physics/RapierPhysics.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Clock, Vector3, Quaternion, Matrix4 } from 'three';
import { Clock, Vector3, Quaternion, Matrix4, LineSegments, LineBasicMaterial, BufferGeometry, BufferAttribute } from 'three';

const RAPIER_PATH = 'https://cdn.skypack.dev/@dimforge/[email protected]';

Expand Down Expand Up @@ -28,6 +28,20 @@ function getShape( geometry ) {
const radius = parameters.radius !== undefined ? parameters.radius : 1;
return RAPIER.ColliderDesc.ball( radius );

} else if ( geometry.type === 'CylinderGeometry') {

const radius = parameters.radiusBottom !== undefined ? parameters.radiusBottom : 0.5;
const length = parameters.length !== undefined ? parameters.length : 0.5;

return RAPIER.ColliderDesc.cylinder( length / 2, radius);

} else if ( geometry.type === 'CapsuleGeometry') {

const radius = parameters.radius !== undefined ? parameters.radius : 0.5;
const length = parameters.length !== undefined ? parameters.length : 0.5;

return RAPIER.ColliderDesc.capsule( length / 2, radius);

} else if ( geometry.type === 'BufferGeometry' ) {

const vertices = [];
Expand All @@ -43,8 +57,8 @@ function getShape( geometry ) {

// if the buffer is non-indexed, generate an index buffer
const indices = geometry.getIndex() === null
? Uint32Array.from( Array( parseInt( vertices.length / 3 ) ).keys() )
: geometry.getIndex().array;
? Uint32Array.from( Array( parseInt( vertices.length / 3 ) ).keys() )
: geometry.getIndex().array;

return RAPIER.ColliderDesc.trimesh( vertices, indices );

Expand All @@ -54,19 +68,6 @@ function getShape( geometry ) {

}

/**
* @classdesc Can be used to include Rapier as a Physics engine into
* `three.js` apps. The API can be initialized via:
* ```js
* const physics = await RapierPhysics();
* ```
* The component automatically imports Rapier from a CDN so make sure
* to use the component with an active Internet connection.
*
* @name RapierPhysics
* @class
* @hideconstructor
*/
async function RapierPhysics() {

if ( RAPIER === null ) {
Expand Down Expand Up @@ -108,6 +109,10 @@ async function RapierPhysics() {

}

function getBody( mesh ){
return meshMap.get( mesh );
}

function addMesh( mesh, mass = 0, restitution = 0 ) {

const shape = getShape( mesh.geometry );
Expand All @@ -118,15 +123,17 @@ async function RapierPhysics() {
shape.setRestitution( restitution );

const body = mesh.isInstancedMesh
? createInstancedBody( mesh, mass, shape )
: createBody( mesh.position, mesh.quaternion, mass, shape );
? createInstancedBody( mesh, mass, shape )
: createBody( mesh.position, mesh.quaternion, mass, shape );

if ( mass > 0 ) {
//if ( mass > 0 ) {

meshes.push( mesh );
meshMap.set( mesh, body );

}
//}

return body;

}

Expand Down Expand Up @@ -242,55 +249,53 @@ async function RapierPhysics() {
setInterval( step, 1000 / frameRate );

return {
/**
* Adds the given scene to this physics simulation. Only meshes with a
* `physics` object in their {@link Object3D#userData} field will be honored.
* The object can be used to store the mass and restitution of the mesh. E.g.:
* ```js
* box.userData.physics = { mass: 1, restitution: 0 };
* ```
*
* @method
* @name RapierPhysics#addScene
* @param {Object3D} scene The scene or any type of 3D object to add.
*/
RAPIER,
world,
addScene: addScene,

/**
* Adds the given mesh to this physics simulation.
*
* @method
* @name RapierPhysics#addMesh
* @param {Mesh} mesh The mesh to add.
* @param {number} [mass=0] The mass in kg of the mesh.
* @param {number} [restitution=0] The restitution/friction of the mesh.
*/
addMesh: addMesh,

/**
* Set the position of the given mesh which is part of the physics simulation. Calling this
* method will reset the current simulated velocity of the mesh.
*
* @method
* @name RapierPhysics#setMeshPosition
* @param {Mesh} mesh The mesh to update the position for.
* @param {Vector3} position - The new position.
* @param {number} [index=0] - If the mesh is instanced, the index represents the instanced ID.
*/
setMeshPosition: setMeshPosition,

/**
* Set the velocity of the given mesh which is part of the physics simulation.
*
* @method
* @name RapierPhysics#setMeshVelocity
* @param {Mesh} mesh The mesh to update the velocity for.
* @param {Vector3} velocity - The new velocity.
* @param {number} [index=0] - If the mesh is instanced, the index represents the instanced ID.
*/
setMeshVelocity: setMeshVelocity
setMeshVelocity: setMeshVelocity,
getBody
};

}

export { RapierPhysics };
class RapierDebugRenderer {

mesh
world
enabled = true

constructor(scene, world) {

this.world = world;
this.mesh = new LineSegments(new BufferGeometry(), new LineBasicMaterial({ color: 0xffffff, vertexColors: true }));
this.mesh.frustumCulled = false;

scene.add(this.mesh);

}

update() {

if (this.enabled) {

const { vertices, colors } = this.world.debugRender();

this.mesh.geometry.deleteAttribute( 'position' );
this.mesh.geometry.deleteAttribute( 'color' );

this.mesh.geometry.setAttribute('position', new BufferAttribute(vertices, 3));
this.mesh.geometry.setAttribute('color', new BufferAttribute(colors, 4));

this.mesh.visible = true;

} else {

this.mesh.visible = false;

}
}
}

export { RapierPhysics, RapierDebugRenderer };
153 changes: 153 additions & 0 deletions examples/physics_rapier_basic.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js physics - rapier3d basic</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<link type="text/css" rel="stylesheet" href="main.css">
</head>
<body>

<div id="info">
<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> physics - <a href="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/dimforge/rapier.js" target="_blank">rapier</a> basic
</div>

<script type="importmap">
{
"imports": {
"three": "../build/three.module.js",
"three/addons/": "./jsm/"
}
}
</script>

<script type="module">

import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { RapierPhysics, RapierDebugRenderer } from 'three/addons/physics/RapierPhysics.js';
import Stats from 'three/addons/libs/stats.module.js';

let camera, scene, renderer, stats;
let physics, debugRender;

init();

async function init() {

scene = new THREE.Scene();

camera = new THREE.PerspectiveCamera( 60, window.innerWidth/window.innerHeight, 0.1, 100 );
camera.position.set(0, 3, 10);

const ambient = new THREE.HemisphereLight(0x555555, 0xFFFFFF);

scene.add(ambient);

const light = new THREE.DirectionalLight(0xffffff, 4);

light.position.set(0,12.5,12.5);
light.castShadow = true;
light.shadow.radius = 3;
light.shadow.blurSamples = 8;
light.shadow.mapSize.width = 1024;
light.shadow.mapSize.height = 1024;

const size = 10;
light.shadow.camera.left = -size;
light.shadow.camera.bottom = -size;
light.shadow.camera.right = size;
light.shadow.camera.top = size;
light.shadow.camera.near = 1;
light.shadow.camera.far = 50;

scene.add(light);

renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.shadowMap.enabled = true;
document.body.appendChild( renderer.domElement );
renderer.setAnimationLoop( animate );

const controls = new OrbitControls(camera, renderer.domElement);
controls.target = new THREE.Vector3(0, 2, 0);
controls.update();

const geometry = new THREE.BoxGeometry( 10, 0.5, 10 );
const material = new THREE.MeshStandardMaterial( { color: 0xFFFF99 } );

const floor = new THREE.Mesh( geometry, material );
floor.receiveShadow = true;

floor.position.y = -0.25;
floor.userData.physics = { mass: 0 };

scene.add( floor );

stats = new Stats();
document.body.appendChild( stats.dom );

initPhysics();

onWindowResize();

window.addEventListener( 'resize', onWindowResize, false );

}

async function initPhysics(){

//Initialize physics engine using the script in the jsm/physics folder
physics = await RapierPhysics();

//Optionally display collider outlines
debugRender = new RapierDebugRenderer( scene, physics.world );

physics.addScene( scene );

addBody( );

setInterval( addBody, 1000 );

}

function addBody( ){

const geometry = (Math.random() > 0.5) ? new THREE.SphereGeometry( 0.5 ) : new THREE.BoxGeometry( 1, 1, 1 );
const material = new THREE.MeshStandardMaterial( { color: Math.floor( Math.random() * 0xFFFFFF ) } );

const mesh = new THREE.Mesh( geometry, material );
mesh.castShadow = true;

mesh.position.set( Math.random()*2-1, Math.random()*3 + 6, Math.random()*2-1 );

scene.add(mesh);

//parameter 2 - mass, parameter 3 - restitution ( how bouncy )
physics.addMesh( mesh, 1, 0.5 );

}

function onWindowResize( event ) {

camera.aspect = window.innerWidth/window.innerHeight;
camera.updateProjectionMatrix();

renderer.setSize( window.innerWidth, window.innerHeight );

}


function animate() {

renderer.render( scene, camera );

if (debugRender) debugRender.update();

stats.update();

}

</script>
</body>
</html>
Loading
Loading