File: src/objects/RigidVehicle.js
var Body = require('./Body');
var Sphere = require('../shapes/Sphere');
var Box = require('../shapes/Box');
var Vec3 = require('../math/Vec3');
var HingeConstraint = require('../constraints/HingeConstraint');
module.exports = RigidVehicle;
/**
* Simple vehicle helper class with spherical rigid body wheels.
* @class RigidVehicle
* @constructor
* @param {Body} [options.chassisBody]
*/
function RigidVehicle(options){
this.wheelBodies = [];
/**
* @property coordinateSystem
* @type {Vec3}
*/
this.coordinateSystem = typeof(options.coordinateSystem)==='undefined' ? new Vec3(1, 2, 3) : options.coordinateSystem.clone();
/**
* @property {Body} chassisBody
*/
this.chassisBody = options.chassisBody;
if(!this.chassisBody){
// No chassis body given. Create it!
var chassisShape = new Box(new Vec3(5, 2, 0.5));
this.chassisBody = new Body(1, chassisShape);
}
/**
* @property constraints
* @type {Array}
*/
this.constraints = [];
this.wheelAxes = [];
this.wheelForces = [];
}
/**
* Add a wheel
* @method addWheel
* @param {object} options
* @param {boolean} [options.isFrontWheel]
* @param {Vec3} [options.position] Position of the wheel, locally in the chassis body.
* @param {Vec3} [options.direction] Slide direction of the wheel along the suspension.
* @param {Vec3} [options.axis] Axis of rotation of the wheel, locally defined in the chassis.
* @param {Body} [options.body] The wheel body.
*/
RigidVehicle.prototype.addWheel = function(options){
options = options || {};
var wheelBody = options.body;
if(!wheelBody){
wheelBody = new Body(1, new Sphere(1.2));
}
this.wheelBodies.push(wheelBody);
this.wheelForces.push(0);
// Position constrain wheels
var zero = new Vec3();
var position = typeof(options.position) !== 'undefined' ? options.position.clone() : new Vec3();
// Set position locally to the chassis
var worldPosition = new Vec3();
this.chassisBody.pointToWorldFrame(position, worldPosition);
wheelBody.position.set(worldPosition.x, worldPosition.y, worldPosition.z);
// Constrain wheel
var axis = typeof(options.axis) !== 'undefined' ? options.axis.clone() : new Vec3(0, 1, 0);
this.wheelAxes.push(axis);
var hingeConstraint = new HingeConstraint(this.chassisBody, wheelBody, {
pivotA: position,
axisA: axis,
pivotB: Vec3.ZERO,
axisB: axis,
collideConnected: false
});
this.constraints.push(hingeConstraint);
return this.wheelBodies.length - 1;
};
/**
* Set the steering value of a wheel.
* @method setSteeringValue
* @param {number} value
* @param {integer} wheelIndex
* @todo check coordinateSystem
*/
RigidVehicle.prototype.setSteeringValue = function(value, wheelIndex){
// Set angle of the hinge axis
var axis = this.wheelAxes[wheelIndex];
var c = Math.cos(value),
s = Math.sin(value),
x = axis.x,
y = axis.y;
this.constraints[wheelIndex].axisA.set(
c*x -s*y,
s*x +c*y,
0
);
};
/**
* Set the target rotational speed of the hinge constraint.
* @method setMotorSpeed
* @param {number} value
* @param {integer} wheelIndex
*/
RigidVehicle.prototype.setMotorSpeed = function(value, wheelIndex){
var hingeConstraint = this.constraints[wheelIndex];
hingeConstraint.enableMotor();
hingeConstraint.motorTargetVelocity = value;
};
/**
* Set the target rotational speed of the hinge constraint.
* @method disableMotor
* @param {number} value
* @param {integer} wheelIndex
*/
RigidVehicle.prototype.disableMotor = function(wheelIndex){
var hingeConstraint = this.constraints[wheelIndex];
hingeConstraint.disableMotor();
};
var torque = new Vec3();
/**
* Set the wheel force to apply on one of the wheels each time step
* @method setWheelForce
* @param {number} value
* @param {integer} wheelIndex
*/
RigidVehicle.prototype.setWheelForce = function(value, wheelIndex){
this.wheelForces[wheelIndex] = value;
};
/**
* Apply a torque on one of the wheels.
* @method applyWheelForce
* @param {number} value
* @param {integer} wheelIndex
*/
RigidVehicle.prototype.applyWheelForce = function(value, wheelIndex){
var axis = this.wheelAxes[wheelIndex];
var wheelBody = this.wheelBodies[wheelIndex];
var bodyTorque = wheelBody.torque;
axis.scale(value, torque);
wheelBody.vectorToWorldFrame(torque, torque);
bodyTorque.vadd(torque, bodyTorque);
};
/**
* Add the vehicle including its constraints to the world.
* @method addToWorld
* @param {World} world
*/
RigidVehicle.prototype.addToWorld = function(world){
var constraints = this.constraints;
var bodies = this.wheelBodies.concat([this.chassisBody]);
for (var i = 0; i < bodies.length; i++) {
world.add(bodies[i]);
}
for (var i = 0; i < constraints.length; i++) {
world.addConstraint(constraints[i]);
}
world.addEventListener('preStep', this._update.bind(this));
};
RigidVehicle.prototype._update = function(){
var wheelForces = this.wheelForces;
for (var i = 0; i < wheelForces.length; i++) {
this.applyWheelForce(wheelForces[i], i);
}
};
/**
* Remove the vehicle including its constraints from the world.
* @method removeFromWorld
* @param {World} world
*/
RigidVehicle.prototype.removeFromWorld = function(world){
var constraints = this.constraints;
var bodies = this.wheelBodies.concat([this.chassisBody]);
for (var i = 0; i < bodies.length; i++) {
world.remove(bodies[i]);
}
for (var i = 0; i < constraints.length; i++) {
world.removeConstraint(constraints[i]);
}
};
var worldAxis = new Vec3();
/**
* Get current rotational velocity of a wheel
* @method getWheelSpeed
* @param {integer} wheelIndex
*/
RigidVehicle.prototype.getWheelSpeed = function(wheelIndex){
var axis = this.wheelAxes[wheelIndex];
var wheelBody = this.wheelBodies[wheelIndex];
var w = wheelBody.angularVelocity;
this.chassisBody.vectorToWorldFrame(axis, worldAxis);
return w.dot(worldAxis);
};