Skip to content

Commit 354fc4e

Browse files
mrdoobclaude
andcommitted
Examples: Add banking to roller coaster track and camera.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 469d8e0 commit 354fc4e

5 files changed

Lines changed: 84 additions & 4 deletions

File tree

examples/jsm/misc/RollerCoaster.js

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,10 @@ class RollerCoasterGeometry extends BufferGeometry {
195195

196196
const offset = new Vector3();
197197

198+
const sample1 = new Vector3();
199+
const sample2 = new Vector3();
200+
const rollQuaternion = new Quaternion();
201+
198202
for ( let i = 1; i <= divisions; i ++ ) {
199203

200204
point.copy( curve.getPointAt( i / divisions ) );
@@ -209,6 +213,20 @@ class RollerCoasterGeometry extends BufferGeometry {
209213

210214
quaternion.setFromAxisAngle( up, angle );
211215

216+
// banking
217+
218+
const bankDelta = 0.01;
219+
const t = i / divisions;
220+
221+
sample1.copy( curve.getTangentAt( ( ( t - bankDelta ) % 1 + 1 ) % 1 ) );
222+
sample2.copy( curve.getTangentAt( ( t + bankDelta ) % 1 ) );
223+
224+
let headingChange = Math.atan2( sample2.x, sample2.z ) - Math.atan2( sample1.x, sample1.z );
225+
if ( headingChange > Math.PI ) headingChange -= Math.PI * 2;
226+
if ( headingChange < -Math.PI ) headingChange += Math.PI * 2;
227+
228+
quaternion.premultiply( rollQuaternion.setFromAxisAngle( forward, - Math.atan( headingChange * 8 ) * 0.5 ) );
229+
212230
if ( i % 2 === 0 ) {
213231

214232
drawShape( step, color2 );
@@ -356,6 +374,11 @@ class RollerCoasterLiftersGeometry extends BufferGeometry {
356374
const fromPoint = new Vector3();
357375
const toPoint = new Vector3();
358376

377+
const sample1 = new Vector3();
378+
const sample2 = new Vector3();
379+
const bankedQuaternion = new Quaternion();
380+
const rollQuaternion = new Quaternion();
381+
359382
for ( let i = 1; i <= divisions; i ++ ) {
360383

361384
point.copy( curve.getPointAt( i / divisions ) );
@@ -365,6 +388,22 @@ class RollerCoasterLiftersGeometry extends BufferGeometry {
365388

366389
quaternion.setFromAxisAngle( up, angle );
367390

391+
// banking
392+
393+
const bankDelta = 0.01;
394+
const t = i / divisions;
395+
396+
sample1.copy( curve.getTangentAt( ( ( t - bankDelta ) % 1 + 1 ) % 1 ) );
397+
sample2.copy( curve.getTangentAt( ( t + bankDelta ) % 1 ) );
398+
399+
let headingChange = Math.atan2( sample2.x, sample2.z ) - Math.atan2( sample1.x, sample1.z );
400+
if ( headingChange > Math.PI ) headingChange -= Math.PI * 2;
401+
if ( headingChange < -Math.PI ) headingChange += Math.PI * 2;
402+
403+
bankedQuaternion.copy( quaternion );
404+
rollQuaternion.setFromAxisAngle( tangent, - Math.atan( headingChange * 8 ) * 0.5 );
405+
bankedQuaternion.premultiply( rollQuaternion );
406+
368407
//
369408

370409
if ( point.y > 10 ) {
@@ -402,12 +441,11 @@ class RollerCoasterLiftersGeometry extends BufferGeometry {
402441
} else {
403442

404443
fromPoint.set( 0, - 0.2, 0 );
405-
fromPoint.applyQuaternion( quaternion );
444+
fromPoint.applyQuaternion( bankedQuaternion );
406445
fromPoint.add( point );
407446

408-
toPoint.set( 0, - point.y, 0 );
409-
toPoint.applyQuaternion( quaternion );
410-
toPoint.add( point );
447+
toPoint.copy( fromPoint );
448+
toPoint.y = 0;
411449

412450
extrudeShape( tube3, fromPoint, toPoint );
413451

4.81 KB
Loading
4.48 KB
Loading

examples/webgpu_xr_rollercoaster.html

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,10 @@
204204

205205
const lookAt = new THREE.Vector3();
206206

207+
const tangent1 = new THREE.Vector3();
208+
const tangent2 = new THREE.Vector3();
209+
const bankQuaternion = new THREE.Quaternion();
210+
207211
let velocity = 0;
208212
let progress = 0;
209213

@@ -235,6 +239,23 @@
235239
velocity -= tangent.y * 0.0000001 * delta;
236240
velocity = Math.max( 0.00004, Math.min( 0.0002, velocity ) );
237241

242+
// banking
243+
244+
const bankDelta = 0.01;
245+
const t1 = ( ( progress - bankDelta ) % 1 + 1 ) % 1;
246+
const t2 = ( progress + bankDelta ) % 1;
247+
248+
tangent1.copy( curve.getTangentAt( t1 ) );
249+
tangent2.copy( curve.getTangentAt( t2 ) );
250+
251+
let headingChange = Math.atan2( tangent2.x, tangent2.z ) - Math.atan2( tangent1.x, tangent1.z );
252+
if ( headingChange > Math.PI ) headingChange -= Math.PI * 2;
253+
if ( headingChange < -Math.PI ) headingChange += Math.PI * 2;
254+
255+
train.up.set( 0, 1, 0 );
256+
bankQuaternion.setFromAxisAngle( tangent, - Math.atan( headingChange * 8 ) * 0.5 );
257+
train.up.applyQuaternion( bankQuaternion );
258+
238259
train.lookAt( lookAt.copy( position ).sub( tangent ) );
239260

240261
//

examples/webxr_vr_rollercoaster.html

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,10 @@
203203

204204
const lookAt = new THREE.Vector3();
205205

206+
const tangent1 = new THREE.Vector3();
207+
const tangent2 = new THREE.Vector3();
208+
const bankQuaternion = new THREE.Quaternion();
209+
206210
let velocity = 0;
207211
let progress = 0;
208212

@@ -234,6 +238,23 @@
234238
velocity -= tangent.y * 0.0000001 * delta;
235239
velocity = Math.max( 0.00004, Math.min( 0.0002, velocity ) );
236240

241+
// banking
242+
243+
const bankDelta = 0.01;
244+
const t1 = ( ( progress - bankDelta ) % 1 + 1 ) % 1;
245+
const t2 = ( progress + bankDelta ) % 1;
246+
247+
tangent1.copy( curve.getTangentAt( t1 ) );
248+
tangent2.copy( curve.getTangentAt( t2 ) );
249+
250+
let headingChange = Math.atan2( tangent2.x, tangent2.z ) - Math.atan2( tangent1.x, tangent1.z );
251+
if ( headingChange > Math.PI ) headingChange -= Math.PI * 2;
252+
if ( headingChange < -Math.PI ) headingChange += Math.PI * 2;
253+
254+
train.up.set( 0, 1, 0 );
255+
bankQuaternion.setFromAxisAngle( tangent, - Math.atan( headingChange * 8 ) * 0.5 );
256+
train.up.applyQuaternion( bankQuaternion );
257+
237258
train.lookAt( lookAt.copy( position ).sub( tangent ) );
238259

239260
//

0 commit comments

Comments
 (0)