topical media & game development

talk show tell print

mobile-query-three-plugins-car-Car.js / js



  

author: alteredq / alteredqualia.com/

  
  
  THREE.Car = function () {
  
          var scope = this;
  
          // car geometry manual parameters
  
          this.modelScale = 1;
  
          this.backWheelOffset = 2;
  
          this.autoWheelGeometry = true;
  
          // car geometry parameters automatically set from wheel mesh
          //         - assumes wheel mesh is front left wheel in proper global
          //    position with respect to body mesh
          //        - other wheels are mirrored against car root
          //        - if necessary back wheels can be offset manually
  
          this.wheelOffset = new THREE.Vector3();
  
          this.wheelDiameter = 1;
  
          // car "feel" parameters
  
          this.MAX_SPEED = 2200;
          this.MAX_REVERSE_SPEED = -1500;
  
          this.MAX_WHEEL_ROTATION = 0.6;
  
          this.FRONT_ACCELERATION = 1250;
          this.BACK_ACCELERATION = 1500;
  
          this.WHEEL_ANGULAR_ACCELERATION = 1.5;
  
          this.FRONT_DECCELERATION = 750;
          this.WHEEL_ANGULAR_DECCELERATION = 1.0;
  
          this.STEERING_RADIUS_RATIO = 0.0023;
  
          this.MAX_TILT_SIDES = 0.05;
          this.MAX_TILT_FRONTBACK = 0.015;
  
          // internal control variables
  
          this.speed = 0;
          this.acceleration = 0;
  
          this.wheelOrientation = 0;
          this.carOrientation = 0;
  
          // car rigging
  
          this.root = new THREE.Object3D();
  
          this.frontLeftWheelRoot = new THREE.Object3D();
          this.frontRightWheelRoot = new THREE.Object3D();
  
          this.bodyMesh = null;
  
          this.frontLeftWheelMesh = null;
          this.frontRightWheelMesh = null;
  
          this.backLeftWheelMesh = null;
          this.backRightWheelMesh = null;
  
          this.bodyGeometry = null;
          this.wheelGeometry = null;
  
          this.bodyMaterials = null;
          this.wheelMaterials = null;
  
          // internal helper variables
  
          this.loaded = false;
  
          this.meshes = [];
  
          // API
  
          this.enableShadows = function ( enable ) {
  
                  for ( var i = 0; i < this.meshes.length; i ++ ) {
  
                          this.meshes[ i ].castShadow = enable;
                          //this.meshes[ i ].receiveShadow = enable;
  
                  }
  
          };
  
          this.setVisible = function ( enable ) {
  
                  for ( var i = 0; i < this.meshes.length; i ++ ) {
  
                          this.meshes[ i ].visible = enable;
                          this.meshes[ i ].visible = enable;
  
                  }
  
          };
  
          this.loadPartsJSON = function ( bodyURL, wheelURL ) {
  
                  var loader = new THREE.JSONLoader();
  
                  loader.load( bodyURL, function( geometry, materials ) { createBody( geometry, materials ) } );
                  loader.load( wheelURL, function( geometry, materials ) { createWheels( geometry, materials ) } );
  
          };
  
          this.loadPartsBinary = function ( bodyURL, wheelURL ) {
  
                  var loader = new THREE.BinaryLoader();
  
                  loader.load( bodyURL, function( geometry, materials ) { createBody( geometry, materials ) } );
                  loader.load( wheelURL, function( geometry, materials ) { createWheels( geometry, materials ) } );
  
          };
  
          this.updateCarModel = function ( delta, controls ) {
  
                  // speed and wheels based on controls
  
                  if ( controls.moveForward ) {
  
                          this.speed = THREE.Math.clamp( this.speed + delta * this.FRONT_ACCELERATION, this.MAX_REVERSE_SPEED, this.MAX_SPEED );
                          this.acceleration = THREE.Math.clamp( this.acceleration + delta, -1, 1 );
  
                  }
  
                  if ( controls.moveBackward ) {
  
                          this.speed = THREE.Math.clamp( this.speed - delta * this.BACK_ACCELERATION, this.MAX_REVERSE_SPEED, this.MAX_SPEED );
                          this.acceleration = THREE.Math.clamp( this.acceleration - delta, -1, 1 );
  
                  }
  
                  if ( controls.moveLeft ) {
  
                          this.wheelOrientation = THREE.Math.clamp( this.wheelOrientation + delta * this.WHEEL_ANGULAR_ACCELERATION, - this.MAX_WHEEL_ROTATION, this.MAX_WHEEL_ROTATION );
  
                  }
  
                  if ( controls.moveRight ) {
  
                          this.wheelOrientation = THREE.Math.clamp( this.wheelOrientation - delta * this.WHEEL_ANGULAR_ACCELERATION, - this.MAX_WHEEL_ROTATION, this.MAX_WHEEL_ROTATION );
  
                  }
  
                  // speed decay
  
                  if ( ! ( controls.moveForward || controls.moveBackward ) ) {
  
                          if ( this.speed > 0 ) {
  
                                  var k = exponentialEaseOut( this.speed / this.MAX_SPEED );
  
                                  this.speed = THREE.Math.clamp( this.speed - k * delta * this.FRONT_DECCELERATION, 0, this.MAX_SPEED );
                                  this.acceleration = THREE.Math.clamp( this.acceleration - k * delta, 0, 1 );
  
                          } else {
  
                                  var k = exponentialEaseOut( this.speed / this.MAX_REVERSE_SPEED );
  
                                  this.speed = THREE.Math.clamp( this.speed + k * delta * this.BACK_ACCELERATION, this.MAX_REVERSE_SPEED, 0 );
                                  this.acceleration = THREE.Math.clamp( this.acceleration + k * delta, -1, 0 );
  
                          }
  
                  }
  
                  // steering decay
  
                  if ( ! ( controls.moveLeft || controls.moveRight ) ) {
  
                          if ( this.wheelOrientation > 0 ) {
  
                                  this.wheelOrientation = THREE.Math.clamp( this.wheelOrientation - delta * this.WHEEL_ANGULAR_DECCELERATION, 0, this.MAX_WHEEL_ROTATION );
  
                          } else {
  
                                  this.wheelOrientation = THREE.Math.clamp( this.wheelOrientation + delta * this.WHEEL_ANGULAR_DECCELERATION, - this.MAX_WHEEL_ROTATION, 0 );
  
                          }
  
                  }
  
                  // car update
  
                  var forwardDelta = this.speed * delta;
  
                  this.carOrientation += ( forwardDelta * this.STEERING_RADIUS_RATIO )* this.wheelOrientation;
  
                  // displacement
  
                  this.root.position.x += Math.sin( this.carOrientation ) * forwardDelta;
                  this.root.position.z += Math.cos( this.carOrientation ) * forwardDelta;
  
                  // steering
  
                  this.root.rotation.y = this.carOrientation;
  
                  // tilt
  
                  if ( this.loaded ) {
  
                          this.bodyMesh.rotation.z = this.MAX_TILT_SIDES * this.wheelOrientation * ( this.speed / this.MAX_SPEED );
                          this.bodyMesh.rotation.x = - this.MAX_TILT_FRONTBACK * this.acceleration;
  
                  }
  
                  // wheels rolling
  
                  var angularSpeedRatio = 1 / ( this.modelScale * ( this.wheelDiameter / 2 ) );
  
                  var wheelDelta = forwardDelta * angularSpeedRatio;
  
                  if ( this.loaded ) {
  
                          this.frontLeftWheelMesh.rotation.x += wheelDelta;
                          this.frontRightWheelMesh.rotation.x += wheelDelta;
                          this.backLeftWheelMesh.rotation.x += wheelDelta;
                          this.backRightWheelMesh.rotation.x += wheelDelta;
  
                  }
  
                  // front wheels steering
  
                  this.frontLeftWheelRoot.rotation.y = this.wheelOrientation;
                  this.frontRightWheelRoot.rotation.y = this.wheelOrientation;
  
          };
  
          // internal helper methods
  
          function createBody ( geometry, materials ) {
  
                  scope.bodyGeometry = geometry;
                  scope.bodyMaterials = materials;
  
                  createCar();
  
          };
  
          function createWheels ( geometry, materials ) {
  
                  scope.wheelGeometry = geometry;
                  scope.wheelMaterials = materials;
  
                  createCar();
  
          };
  
          function createCar () {
  
                  if ( scope.bodyGeometry && scope.wheelGeometry ) {
  
                          // compute wheel geometry parameters
  
                          if ( scope.autoWheelGeometry ) {
  
                                  scope.wheelGeometry.computeBoundingBox();
  
                                  var bb = scope.wheelGeometry.boundingBox;
  
                                  scope.wheelOffset.addVectors( bb.min, bb.max );
                                  scope.wheelOffset.multiplyScalar( 0.5 );
  
                                  scope.wheelDiameter = bb.max.y - bb.min.y;
  
                                  THREE.GeometryUtils.center( scope.wheelGeometry );
  
                          }
  
                          // rig the car
  
                          var s = scope.modelScale,
                                  delta = new THREE.Vector3();
  
                          var bodyFaceMaterial = new THREE.MeshFaceMaterial( scope.bodyMaterials );
                          var wheelFaceMaterial = new THREE.MeshFaceMaterial( scope.wheelMaterials );
  
                          // body
  
                          scope.bodyMesh = new THREE.Mesh( scope.bodyGeometry, bodyFaceMaterial );
                          scope.bodyMesh.scale.set( s, s, s );
  
                          scope.root.add( scope.bodyMesh );
  
                          // front left wheel
  
                          delta.multiplyVectors( scope.wheelOffset, new THREE.Vector3( s, s, s ) );
  
                          scope.frontLeftWheelRoot.position.add( delta );
  
                          scope.frontLeftWheelMesh = new THREE.Mesh( scope.wheelGeometry, wheelFaceMaterial );
                          scope.frontLeftWheelMesh.scale.set( s, s, s );
  
                          scope.frontLeftWheelRoot.add( scope.frontLeftWheelMesh );
                          scope.root.add( scope.frontLeftWheelRoot );
  
                          // front right wheel
                          delta.multiplyVectors( scope.wheelOffset, new THREE.Vector3( -s, s, s ) );
  
                          scope.frontRightWheelRoot.position.add( delta );
  
                          scope.frontRightWheelMesh = new THREE.Mesh( scope.wheelGeometry, wheelFaceMaterial );
  
                          scope.frontRightWheelMesh.scale.set( s, s, s );
                          scope.frontRightWheelMesh.rotation.z = Math.PI;
  
                          scope.frontRightWheelRoot.add( scope.frontRightWheelMesh );
                          scope.root.add( scope.frontRightWheelRoot );
  
                          // back left wheel
  
                          delta.multiplyVectors( scope.wheelOffset, new THREE.Vector3( s, s, -s ) );
                          delta.z -= scope.backWheelOffset;
  
                          scope.backLeftWheelMesh = new THREE.Mesh( scope.wheelGeometry, wheelFaceMaterial );
  
                          scope.backLeftWheelMesh.position.add( delta );
                          scope.backLeftWheelMesh.scale.set( s, s, s );
  
                          scope.root.add( scope.backLeftWheelMesh );
  
                          // back right wheel
  
                          delta.multiplyVectors( scope.wheelOffset, new THREE.Vector3( -s, s, -s ) );
                          delta.z -= scope.backWheelOffset;
  
                          scope.backRightWheelMesh = new THREE.Mesh( scope.wheelGeometry, wheelFaceMaterial );
  
                          scope.backRightWheelMesh.position.add( delta );
                          scope.backRightWheelMesh.scale.set( s, s, s );
                          scope.backRightWheelMesh.rotation.z = Math.PI;
  
                          scope.root.add( scope.backRightWheelMesh );
  
                          // cache meshes
  
                          scope.meshes = [ scope.bodyMesh, scope.frontLeftWheelMesh, scope.frontRightWheelMesh, scope.backLeftWheelMesh, scope.backRightWheelMesh ];
  
                          // callback
  
                          scope.loaded = true;
  
                          if ( scope.callback ) {
  
                                  scope.callback( scope );
  
                          }
  
                  }
  
          };
  
          function quadraticEaseOut( k ) { return - k * ( k - 2 ); }
          function cubicEaseOut( k ) { return --k * k * k + 1; }
          function circularEaseOut( k ) { return Math.sqrt( 1 - --k * k ); }
          function sinusoidalEaseOut( k ) { return Math.sin( k * Math.PI / 2 ); }
          function exponentialEaseOut( k ) { return k === 1 ? 1 : - Math.pow( 2, - 10 * k ) + 1; }
  
  };
  


(C) Æliens 04/09/2009

You may not copy or print any of this material without explicit permission of the author or the publisher. In case of other copyright issues, contact the author.