Skip to content

Commit 7f74cfa

Browse files
NikLeverMugen87
andauthored
Added additional Rapier physics examples (#30818)
* Added new Rapier physics examples * Delete attribute when using Rapier debugger * Added screenshots * Restored jsDocs to RapierPhysics.js * Updated files.json * added exception * Updates for Michael * Refactored debugger as RapierHelper * Removed unused imports from RapierPhysics.js * Update RapierPhysics.js * Update RapierPhysics.js Fix code style. * Refactored physics_rapier_joints.html to not use getBody. * Removed getBody from RapierPhysics.js * Update RapierPhysics.js * Update RapierPhysics.js * Refactored access to the RigidBody * Update physics_rapier_vehicle_controller.html --------- Co-authored-by: Michael Herzog <[email protected]>
1 parent f1ecf66 commit 7f74cfa

13 files changed

+1056
-2
lines changed

examples/files.json

+5-1
Original file line numberDiff line numberDiff line change
@@ -502,7 +502,11 @@
502502
"physics_ammo_terrain",
503503
"physics_ammo_volume",
504504
"physics_jolt_instancing",
505-
"physics_rapier_instancing"
505+
"physics_rapier_basic",
506+
"physics_rapier_instancing",
507+
"physics_rapier_joints",
508+
"physics_rapier_character_controller",
509+
"physics_rapier_vehicle_controller"
506510
],
507511
"misc": [
508512
"misc_animation_groups",

examples/jsm/helpers/RapierHelper.js

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { LineSegments, LineBasicMaterial, BufferAttribute } from 'three';
2+
/**
3+
* This class displays all Rapier Colliders in outline.
4+
*
5+
* @augments LineSegments
6+
*/
7+
class RapierHelper extends LineSegments {
8+
9+
/**
10+
* Constructs a new Rapier debug helper.
11+
*
12+
* @param {RAPIER.world} world - The Rapier world to visualize.
13+
*/
14+
constructor( world ) {
15+
16+
super();
17+
18+
/**
19+
* The Rapier world to visualize.
20+
*
21+
* @type {RAPIER.world}
22+
*/
23+
this.world = world;
24+
25+
this.material = new LineBasicMaterial( { vertexColors: true } );
26+
this.frustumCulled = false;
27+
28+
}
29+
30+
/**
31+
* Call this in the render loop to update the outlines.
32+
*/
33+
update() {
34+
35+
const { vertices, colors } = this.world.debugRender();
36+
37+
this.geometry.deleteAttribute( 'position' );
38+
this.geometry.deleteAttribute( 'color' );
39+
40+
this.geometry.setAttribute( 'position', new BufferAttribute( vertices, 3 ) );
41+
this.geometry.setAttribute( 'color', new BufferAttribute( colors, 4 ) );
42+
43+
}
44+
45+
/**
46+
* Frees the GPU-related resources allocated by this instance. Call this
47+
* method whenever this instance is no longer used in your app.
48+
*/
49+
dispose() {
50+
51+
this.geometry.dispose();
52+
this.material.dispose();
53+
54+
}
55+
56+
}
57+
58+
export { RapierHelper };

examples/jsm/physics/RapierPhysics.js

+20
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,20 @@ function getShape( geometry ) {
2828
const radius = parameters.radius !== undefined ? parameters.radius : 1;
2929
return RAPIER.ColliderDesc.ball( radius );
3030

31+
} else if ( geometry.type === 'CylinderGeometry' ) {
32+
33+
const radius = parameters.radiusBottom !== undefined ? parameters.radiusBottom : 0.5;
34+
const length = parameters.length !== undefined ? parameters.length : 0.5;
35+
36+
return RAPIER.ColliderDesc.cylinder( length / 2, radius );
37+
38+
} else if ( geometry.type === 'CapsuleGeometry' ) {
39+
40+
const radius = parameters.radius !== undefined ? parameters.radius : 0.5;
41+
const length = parameters.length !== undefined ? parameters.length : 0.5;
42+
43+
return RAPIER.ColliderDesc.capsule( length / 2, radius );
44+
3145
} else if ( geometry.type === 'BufferGeometry' ) {
3246

3347
const vertices = [];
@@ -121,6 +135,10 @@ async function RapierPhysics() {
121135
? createInstancedBody( mesh, mass, shape )
122136
: createBody( mesh.position, mesh.quaternion, mass, shape );
123137

138+
if ( ! mesh.userData.physics ) mesh.userData.physics = {};
139+
140+
mesh.userData.physics.body = body;
141+
124142
if ( mass > 0 ) {
125143

126144
meshes.push( mesh );
@@ -242,6 +260,8 @@ async function RapierPhysics() {
242260
setInterval( step, 1000 / frameRate );
243261

244262
return {
263+
RAPIER,
264+
world,
245265
/**
246266
* Adds the given scene to this physics simulation. Only meshes with a
247267
* `physics` object in their {@link Object3D#userData} field will be honored.

examples/physics_rapier_basic.html

+172
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<title>three.js physics - rapier3d basic</title>
5+
<meta charset="utf-8">
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
7+
<link type="text/css" rel="stylesheet" href="main.css">
8+
<style>
9+
body {
10+
color: #333;
11+
}
12+
</style>
13+
</head>
14+
<body>
15+
16+
<div id="info">
17+
<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> physics - <a href="https://github.com/dimforge/rapier.js" target="_blank">rapier</a> basic
18+
</div>
19+
20+
<script type="importmap">
21+
{
22+
"imports": {
23+
"three": "../build/three.module.js",
24+
"three/addons/": "./jsm/"
25+
}
26+
}
27+
</script>
28+
29+
<script type="module">
30+
31+
import * as THREE from 'three';
32+
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
33+
import { RapierPhysics } from 'three/addons/physics/RapierPhysics.js';
34+
import { RapierHelper } from 'three/addons/helpers/RapierHelper.js';
35+
import Stats from 'three/addons/libs/stats.module.js';
36+
37+
let camera, scene, renderer, stats;
38+
let physics, physicsHelper;
39+
40+
init();
41+
42+
async function init() {
43+
44+
scene = new THREE.Scene();
45+
scene.background = new THREE.Color( 0xbfd1e5 );
46+
47+
camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 0.1, 100 );
48+
camera.position.set( 0, 3, 10 );
49+
50+
const ambient = new THREE.HemisphereLight( 0x555555, 0xFFFFFF );
51+
52+
scene.add( ambient );
53+
54+
const light = new THREE.DirectionalLight( 0xffffff, 4 );
55+
56+
light.position.set( 0, 12.5, 12.5 );
57+
light.castShadow = true;
58+
light.shadow.radius = 3;
59+
light.shadow.blurSamples = 8;
60+
light.shadow.mapSize.width = 1024;
61+
light.shadow.mapSize.height = 1024;
62+
63+
const size = 10;
64+
light.shadow.camera.left = - size;
65+
light.shadow.camera.bottom = - size;
66+
light.shadow.camera.right = size;
67+
light.shadow.camera.top = size;
68+
light.shadow.camera.near = 1;
69+
light.shadow.camera.far = 50;
70+
71+
scene.add( light );
72+
73+
renderer = new THREE.WebGLRenderer( { antialias: true } );
74+
renderer.setPixelRatio( window.devicePixelRatio );
75+
renderer.setSize( window.innerWidth, window.innerHeight );
76+
renderer.shadowMap.enabled = true;
77+
document.body.appendChild( renderer.domElement );
78+
renderer.setAnimationLoop( animate );
79+
80+
const controls = new OrbitControls( camera, renderer.domElement );
81+
controls.target = new THREE.Vector3( 0, 2, 0 );
82+
controls.update();
83+
84+
const geometry = new THREE.BoxGeometry( 10, 0.5, 10 );
85+
const material = new THREE.MeshStandardMaterial( { color: 0xFFFFFF } );
86+
87+
const floor = new THREE.Mesh( geometry, material );
88+
floor.receiveShadow = true;
89+
90+
floor.position.y = - 0.25;
91+
floor.userData.physics = { mass: 0 };
92+
93+
scene.add( floor );
94+
95+
new THREE.TextureLoader().load( 'textures/grid.png', function ( texture ) {
96+
97+
texture.wrapS = THREE.RepeatWrapping;
98+
texture.wrapT = THREE.RepeatWrapping;
99+
texture.repeat.set( 20, 20 );
100+
floor.material.map = texture;
101+
floor.material.needsUpdate = true;
102+
103+
} );
104+
105+
stats = new Stats();
106+
document.body.appendChild( stats.dom );
107+
108+
initPhysics();
109+
110+
onWindowResize();
111+
112+
window.addEventListener( 'resize', onWindowResize, false );
113+
114+
}
115+
116+
async function initPhysics() {
117+
118+
//Initialize physics engine using the script in the jsm/physics folder
119+
physics = await RapierPhysics();
120+
121+
physics.addScene( scene );
122+
123+
addBody( );
124+
125+
//Optionally display collider outlines
126+
physicsHelper = new RapierHelper( physics.world );
127+
scene.add( physicsHelper );
128+
129+
setInterval( addBody, 1000 );
130+
131+
}
132+
133+
function addBody( ) {
134+
135+
const geometry = ( Math.random() > 0.5 ) ? new THREE.SphereGeometry( 0.5 ) : new THREE.BoxGeometry( 1, 1, 1 );
136+
const material = new THREE.MeshStandardMaterial( { color: Math.floor( Math.random() * 0xFFFFFF ) } );
137+
138+
const mesh = new THREE.Mesh( geometry, material );
139+
mesh.castShadow = true;
140+
141+
mesh.position.set( Math.random() * 2 - 1, Math.random() * 3 + 6, Math.random() * 2 - 1 );
142+
143+
scene.add( mesh );
144+
145+
//parameter 2 - mass, parameter 3 - restitution ( how bouncy )
146+
physics.addMesh( mesh, 1, 0.5 );
147+
148+
}
149+
150+
function onWindowResize( ) {
151+
152+
camera.aspect = window.innerWidth / window.innerHeight;
153+
camera.updateProjectionMatrix();
154+
155+
renderer.setSize( window.innerWidth, window.innerHeight );
156+
157+
}
158+
159+
160+
function animate() {
161+
162+
if ( physicsHelper ) physicsHelper.update();
163+
164+
renderer.render( scene, camera );
165+
166+
stats.update();
167+
168+
}
169+
170+
</script>
171+
</body>
172+
</html>

0 commit comments

Comments
 (0)