armory/Sources/armory/trait/VehicleBody.hx
2016-10-25 13:01:20 +02:00

275 lines
7.5 KiB
Haxe
Executable file

package armory.trait;
import iron.Trait;
import iron.object.Object;
import iron.object.CameraObject;
import iron.object.Transform;
import iron.system.Time;
import armory.trait.internal.PhysicsWorld;
import armory.system.Keymap;
#if arm_physics
import haxebullet.Bullet;
#end
@:keep
class VehicleBody extends Trait {
#if (!arm_physics)
public function new() { super(); }
#else
var physics:PhysicsWorld;
var transform:Transform;
var camera:CameraObject;
// Wheels
var wheels:Array<Object> = [];
var wheelNames:Array<String>;
var vehicle:BtRaycastVehiclePointer = null;
var carChassis:BtRigidBodyPointer;
var chassis_mass = 600.0;
var wheelFriction = 1000;
var suspensionStiffness = 20.0;
var suspensionDamping = 2.3;
var suspensionCompression = 4.4;
var suspensionRestLength = 0.6;
var rollInfluence = 0.1;
var maxEngineForce = 3000.0;
var maxBreakingForce = 500.0;
var engineForce = 0.0;
var breakingForce = 0.0;
var vehicleSteering = 0.0;
public function new(wheelName1:String, wheelName2:String, wheelName3:String, wheelName4:String) {
super();
wheelNames = [wheelName1, wheelName2, wheelName3, wheelName4];
Scene.active.notifyOnInit(init);
}
var forward = false;
var backward = false;
var left = false;
var right = false;
var brake = false;
function onKeyDown(key:kha.Key, char:String) {
if (char == Keymap.forward) forward = true;
else if (char == Keymap.backward) backward = true;
else if (char == Keymap.left) left = true;
else if (char == Keymap.right) right = true;
else if (char == Keymap.brake) brake = true;
}
function onKeyUp(key:kha.Key, char:String) {
if (char == Keymap.forward) forward = false;
else if (char == Keymap.backward) backward = false;
else if (char == Keymap.left) left = false;
else if (char == Keymap.right) right = false;
else if (char == Keymap.brake) brake = false;
}
function init() {
physics = armory.trait.internal.PhysicsWorld.active;
transform = object.transform;
camera = iron.Scene.active.camera;
for (n in wheelNames) {
wheels.push(iron.Scene.active.root.getChild(n));
}
var wheelDirectionCS0 = BtVector3.create(0, 0, -1).value;
var wheelAxleCS = BtVector3.create(1, 0, 0).value;
var chassisShape = BtBoxShape.create(BtVector3.create(
transform.size.x / 2,
transform.size.y / 2,
transform.size.z / 2).value);
var compound = BtCompoundShape.create();
var localTrans = BtTransform.create();
localTrans.value.setIdentity();
localTrans.value.setOrigin(BtVector3.create(0, 0, 1).value);
#if js
compound.addChildShape(localTrans, chassisShape);
#elseif cpp
compound.value.addChildShape(localTrans.value, chassisShape);
#end
carChassis = createRigidBody(chassis_mass, compound);
// Create vehicle
var tuning = BtVehicleTuning.create();
var vehicleRayCaster = BtDefaultVehicleRaycaster.create(physics.world);
vehicle = BtRaycastVehicle.create(tuning.value, carChassis, vehicleRayCaster);
// Never deactivate the vehicle
carChassis.ptr.setActivationState(BtCollisionObject.DISABLE_DEACTIVATION);
// Choose coordinate system
var rightIndex = 0;
var upIndex = 2;
var forwardIndex = 1;
vehicle.ptr.setCoordinateSystem(rightIndex, upIndex, forwardIndex);
// Add wheels
for (i in 0...wheels.length) {
var vehicleWheel = new VehicleWheel(i, wheels[i].transform, object.transform);
vehicle.ptr.addWheel(
vehicleWheel.getConnectionPoint(),
wheelDirectionCS0,
wheelAxleCS,
suspensionRestLength,
vehicleWheel.wheelRadius,
tuning.value,
vehicleWheel.isFrontWheel);
}
// Setup wheels
for (i in 0...vehicle.ptr.getNumWheels()){
var wheel = vehicle.ptr.getWheelInfo(i);
wheel.m_suspensionStiffness = suspensionStiffness;
wheel.m_wheelsDampingRelaxation = suspensionDamping;
wheel.m_wheelsDampingCompression = suspensionCompression;
wheel.m_frictionSlip = wheelFriction;
wheel.m_rollInfluence = rollInfluence;
}
physics.world.ptr.addAction(vehicle);
kha.input.Keyboard.get().notify(onKeyDown, onKeyUp);
notifyOnUpdate(update);
}
function update() {
if (vehicle == null) return;
if (forward) {
engineForce = maxEngineForce;
}
else if (backward) {
engineForce = -maxEngineForce;
}
else if (brake) {
breakingForce = 100;
}
else {
engineForce = 0;
breakingForce = 20;
}
if (left) {
if (vehicleSteering < 0.3) vehicleSteering += Time.step;
}
else if (right) {
if (vehicleSteering > -0.3) vehicleSteering -= Time.step;
}
else if (vehicleSteering != 0) {
var step = Math.abs(vehicleSteering) < Time.step ? Math.abs(vehicleSteering) : Time.step;
if (vehicleSteering > 0) vehicleSteering -= step;
else vehicleSteering += step;
}
vehicle.ptr.applyEngineForce(engineForce, 2);
vehicle.ptr.setBrake(breakingForce, 2);
vehicle.ptr.applyEngineForce(engineForce, 3);
vehicle.ptr.setBrake(breakingForce, 3);
vehicle.ptr.setSteeringValue(vehicleSteering, 0);
vehicle.ptr.setSteeringValue(vehicleSteering, 1);
for (i in 0...vehicle.ptr.getNumWheels()) {
// Synchronize the wheels with the chassis worldtransform
vehicle.ptr.updateWheelTransform(i, true);
// Update wheels transforms
var trans = vehicle.ptr.getWheelTransformWS(i);
var p = trans.getOrigin();
var q = trans.getRotation();
wheels[i].transform.localOnly = true;
wheels[i].transform.loc.set(p.x(), p.y(), p.z());
wheels[i].transform.rot.set(q.x(), q.y(), q.z(), q.w());
wheels[i].transform.dirty = true;
}
var trans = carChassis.ptr.getWorldTransform();
var p = trans.getOrigin();
var q = trans.getRotation();
transform.loc.set(p.x(), p.y(), p.z());
transform.rot.set(q.x(), q.y(), q.z(), q.w());
var up = transform.matrix.up();
transform.loc.add(up);
transform.dirty = true;
// TODO: fix parent matrix update
if (camera.parent != null) camera.parent.transform.buildMatrix();
camera.buildMatrix();
}
function createRigidBody(mass:Float, shape:BtCompoundShapePointer):BtRigidBodyPointer {
var localInertia = BtVector3.create(0, 0, 0);
shape.ptr.calculateLocalInertia(mass, localInertia.value);
var centerOfMassOffset = BtTransform.create();
centerOfMassOffset.value.setIdentity();
var startTransform = BtTransform.create();
startTransform.value.setIdentity();
startTransform.value.setOrigin(BtVector3.create(
transform.loc.x,
transform.loc.y,
transform.loc.z).value);
startTransform.value.setRotation(BtQuaternion.create(
transform.rot.x,
transform.rot.y,
transform.rot.z,
transform.rot.w).value);
var myMotionState = BtDefaultMotionState.create(startTransform.value, centerOfMassOffset.value);
var cInfo = BtRigidBodyConstructionInfo.create(mass, myMotionState, shape, localInertia.value).value;
var body = BtRigidBody.create(cInfo);
body.ptr.setLinearVelocity(BtVector3.create(0, 0, 0).value);
body.ptr.setAngularVelocity(BtVector3.create(0, 0, 0).value);
physics.world.ptr.addRigidBody(body);
return body;
}
#end
}
class VehicleWheel {
#if (!arm_physics)
public function new() { }
#else
public var isFrontWheel:Bool;
public var wheelRadius:Float;
public var wheelWidth:Float;
var locX:Float;
var locY:Float;
var locZ:Float;
public function new(id:Int, transform:Transform, vehicleTransform:Transform) {
wheelRadius = transform.size.z / 2;
wheelWidth = transform.size.x > transform.size.y ? transform.size.y : transform.size.x;
locX = transform.loc.x;
locY = transform.loc.y;
locZ = vehicleTransform.size.z / 2 + transform.loc.z;
}
public function getConnectionPoint():BtVector3 {
return BtVector3.create(locX, locY, locZ).value;
}
#end
}