Browse Source

Examples: Add banking to roller coaster track and camera.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Mr.doob 3 tháng trước cách đây
mục cha
commit
354fc4ec29

+ 42 - 4
examples/jsm/misc/RollerCoaster.js

@@ -195,6 +195,10 @@ class RollerCoasterGeometry extends BufferGeometry {
 
 		const offset = new Vector3();
 
+		const sample1 = new Vector3();
+		const sample2 = new Vector3();
+		const rollQuaternion = new Quaternion();
+
 		for ( let i = 1; i <= divisions; i ++ ) {
 
 			point.copy( curve.getPointAt( i / divisions ) );
@@ -209,6 +213,20 @@ class RollerCoasterGeometry extends BufferGeometry {
 
 			quaternion.setFromAxisAngle( up, angle );
 
+			// banking
+
+			const bankDelta = 0.01;
+			const t = i / divisions;
+
+			sample1.copy( curve.getTangentAt( ( ( t - bankDelta ) % 1 + 1 ) % 1 ) );
+			sample2.copy( curve.getTangentAt( ( t + bankDelta ) % 1 ) );
+
+			let headingChange = Math.atan2( sample2.x, sample2.z ) - Math.atan2( sample1.x, sample1.z );
+			if ( headingChange > Math.PI ) headingChange -= Math.PI * 2;
+			if ( headingChange < -Math.PI ) headingChange += Math.PI * 2;
+
+			quaternion.premultiply( rollQuaternion.setFromAxisAngle( forward, - Math.atan( headingChange * 8 ) * 0.5 ) );
+
 			if ( i % 2 === 0 ) {
 
 				drawShape( step, color2 );
@@ -356,6 +374,11 @@ class RollerCoasterLiftersGeometry extends BufferGeometry {
 		const fromPoint = new Vector3();
 		const toPoint = new Vector3();
 
+		const sample1 = new Vector3();
+		const sample2 = new Vector3();
+		const bankedQuaternion = new Quaternion();
+		const rollQuaternion = new Quaternion();
+
 		for ( let i = 1; i <= divisions; i ++ ) {
 
 			point.copy( curve.getPointAt( i / divisions ) );
@@ -365,6 +388,22 @@ class RollerCoasterLiftersGeometry extends BufferGeometry {
 
 			quaternion.setFromAxisAngle( up, angle );
 
+			// banking
+
+			const bankDelta = 0.01;
+			const t = i / divisions;
+
+			sample1.copy( curve.getTangentAt( ( ( t - bankDelta ) % 1 + 1 ) % 1 ) );
+			sample2.copy( curve.getTangentAt( ( t + bankDelta ) % 1 ) );
+
+			let headingChange = Math.atan2( sample2.x, sample2.z ) - Math.atan2( sample1.x, sample1.z );
+			if ( headingChange > Math.PI ) headingChange -= Math.PI * 2;
+			if ( headingChange < -Math.PI ) headingChange += Math.PI * 2;
+
+			bankedQuaternion.copy( quaternion );
+			rollQuaternion.setFromAxisAngle( tangent, - Math.atan( headingChange * 8 ) * 0.5 );
+			bankedQuaternion.premultiply( rollQuaternion );
+
 			//
 
 			if ( point.y > 10 ) {
@@ -402,12 +441,11 @@ class RollerCoasterLiftersGeometry extends BufferGeometry {
 			} else {
 
 				fromPoint.set( 0, - 0.2, 0 );
-				fromPoint.applyQuaternion( quaternion );
+				fromPoint.applyQuaternion( bankedQuaternion );
 				fromPoint.add( point );
 
-				toPoint.set( 0, - point.y, 0 );
-				toPoint.applyQuaternion( quaternion );
-				toPoint.add( point );
+				toPoint.copy( fromPoint );
+				toPoint.y = 0;
 
 				extrudeShape( tube3, fromPoint, toPoint );
 

BIN
examples/screenshots/webgpu_xr_rollercoaster.jpg


BIN
examples/screenshots/webxr_vr_rollercoaster.jpg


+ 21 - 0
examples/webgpu_xr_rollercoaster.html

@@ -204,6 +204,10 @@
 
 			const lookAt = new THREE.Vector3();
 
+			const tangent1 = new THREE.Vector3();
+			const tangent2 = new THREE.Vector3();
+			const bankQuaternion = new THREE.Quaternion();
+
 			let velocity = 0;
 			let progress = 0;
 
@@ -235,6 +239,23 @@
 				velocity -= tangent.y * 0.0000001 * delta;
 				velocity = Math.max( 0.00004, Math.min( 0.0002, velocity ) );
 
+				// banking
+
+				const bankDelta = 0.01;
+				const t1 = ( ( progress - bankDelta ) % 1 + 1 ) % 1;
+				const t2 = ( progress + bankDelta ) % 1;
+
+				tangent1.copy( curve.getTangentAt( t1 ) );
+				tangent2.copy( curve.getTangentAt( t2 ) );
+
+				let headingChange = Math.atan2( tangent2.x, tangent2.z ) - Math.atan2( tangent1.x, tangent1.z );
+				if ( headingChange > Math.PI ) headingChange -= Math.PI * 2;
+				if ( headingChange < -Math.PI ) headingChange += Math.PI * 2;
+
+				train.up.set( 0, 1, 0 );
+				bankQuaternion.setFromAxisAngle( tangent, - Math.atan( headingChange * 8 ) * 0.5 );
+				train.up.applyQuaternion( bankQuaternion );
+
 				train.lookAt( lookAt.copy( position ).sub( tangent ) );
 
 				//

+ 21 - 0
examples/webxr_vr_rollercoaster.html

@@ -203,6 +203,10 @@
 
 			const lookAt = new THREE.Vector3();
 
+			const tangent1 = new THREE.Vector3();
+			const tangent2 = new THREE.Vector3();
+			const bankQuaternion = new THREE.Quaternion();
+
 			let velocity = 0;
 			let progress = 0;
 
@@ -234,6 +238,23 @@
 				velocity -= tangent.y * 0.0000001 * delta;
 				velocity = Math.max( 0.00004, Math.min( 0.0002, velocity ) );
 
+				// banking
+
+				const bankDelta = 0.01;
+				const t1 = ( ( progress - bankDelta ) % 1 + 1 ) % 1;
+				const t2 = ( progress + bankDelta ) % 1;
+
+				tangent1.copy( curve.getTangentAt( t1 ) );
+				tangent2.copy( curve.getTangentAt( t2 ) );
+
+				let headingChange = Math.atan2( tangent2.x, tangent2.z ) - Math.atan2( tangent1.x, tangent1.z );
+				if ( headingChange > Math.PI ) headingChange -= Math.PI * 2;
+				if ( headingChange < -Math.PI ) headingChange += Math.PI * 2;
+
+				train.up.set( 0, 1, 0 );
+				bankQuaternion.setFromAxisAngle( tangent, - Math.atan( headingChange * 8 ) * 0.5 );
+				train.up.applyQuaternion( bankQuaternion );
+
 				train.lookAt( lookAt.copy( position ).sub( tangent ) );
 
 				//

粤ICP备19079148号