armory/Sources/armory/trait/internal/PhysicsWorld.hx
2016-09-05 17:03:20 +02:00

197 lines
5.1 KiB
Haxe

package armory.trait.internal;
#if WITH_PHYSICS
import haxebullet.Bullet;
#end
import iron.Trait;
import iron.sys.Time;
import iron.math.Vec4;
import iron.math.RayCaster;
class ContactPair {
public var a:Int;
public var b:Int;
public function new(a:Int, b:Int) {
this.a = a;
this.b = b;
}
}
class PhysicsWorld extends Trait {
#if (!WITH_PHYSICS)
public function new() { super(); }
#else
#if WITH_PROFILE
public static var physTime = 0.0;
#end
public static var active:PhysicsWorld = null;
public var world:BtDiscreteDynamicsWorldPointer;
var dispatcher:BtCollisionDispatcherPointer;
var contacts:Array<ContactPair> = [];
public var rbMap:Map<Int, RigidBody>;
static inline var timeStep = 1 / 60;
static inline var fixedStep = 1 / 60;
public function new() {
super();
if (active != null) {
for (rb in active.rbMap) removeRigidBody(rb);
active.rbMap = new Map();
return;
}
active = this;
rbMap = new Map();
//var min = BtVector3.create(-100, -100, -100);
//var max = BtVector3.create(100, 100, 100);
//var broadphase = BtAxisSweep3.create(min.value, max.value);
var broadphase = BtDbvtBroadphase.create();
var collisionConfiguration = BtDefaultCollisionConfiguration.create();
dispatcher = BtCollisionDispatcher.create(collisionConfiguration);
var solver = BtSequentialImpulseConstraintSolver.create();
world = BtDiscreteDynamicsWorld.create(dispatcher, broadphase, solver, collisionConfiguration);
var gravity = iron.Scene.active.raw.gravity == null ? [0, 0, -9.81] : iron.Scene.active.raw.gravity;
world.ptr.setGravity(BtVector3.create(gravity[0], gravity[1], gravity[2]).value);
notifyOnUpdate(update);
}
public function addRigidBody(body:RigidBody) {
world.ptr.addRigidBody(body.body);
rbMap.set(body.id, body);
}
public function removeRigidBody(body:RigidBody) {
world.ptr.removeRigidBody(body.body);
#if js
Ammo.destroy(body.body);
#elseif cpp
body.body.destroy();
#end
rbMap.remove(body.id);
}
public function getContacts(body:RigidBody):Array<RigidBody> {
if (contacts.length == 0) return null;
var res:Array<RigidBody> = [];
for (i in 0...contacts.length) {
var c = contacts[i];
#if js
if (c.a == untyped body.body.userIndex) {
res.push(rbMap.get(c.b));
}
else if (c.b == untyped body.body.userIndex) {
res.push(rbMap.get(c.a));
}
#elseif cpp
if (c.a == body.body.ptr.getUserIndex()) {
res.push(rbMap.get(c.b));
}
else if (c.b == body.body.ptr.getUserIndex()) {
res.push(rbMap.get(c.a));
}
#end
}
return res;
}
public function update() {
#if WITH_PROFILE
var startTime = kha.Scheduler.realTime();
#end
world.ptr.stepSimulation(timeStep, 1, fixedStep);
updateContacts();
#if WITH_PROFILE
physTime = kha.Scheduler.realTime() - startTime;
#end
}
function updateContacts() {
contacts = [];
var numManifolds = dispatcher.value.getNumManifolds();
for (i in 0...numManifolds) {
var contactManifold = dispatcher.value.getManifoldByIndexInternal(i);
var obA = contactManifold.value.getBody0();
var obB = contactManifold.value.getBody1();
#if js
var bodyA = untyped Ammo.btRigidBody.prototype.upcast(obA);
var bodyB = untyped Ammo.btRigidBody.prototype.upcast(obB);
// TODO: remove ContactPair
var cp = new ContactPair(untyped bodyA.userIndex, untyped bodyB.userIndex);
#elseif cpp
var cp = new ContactPair(obA.value.getUserIndex(), obB.value.getUserIndex());
#end
var numContacts = contactManifold.value.getNumContacts();
for (j in 0...numContacts) {
var pt = contactManifold.value.getContactPoint(j);
if (pt.getDistance() < 0) {
//var ptA = pt.getPositionWorldOnA();
//var ptB = pt.getPositionWorldOnB();
contacts.push(cp);
break; // TODO: only one contact point for now
}
}
}
}
public var rayCallback:ClosestRayResultCallbackPointer;
public function pickClosest(inputX:Float, inputY:Float):RigidBody {
var rayFrom = getRayFrom();
var rayTo = getRayTo(inputX, inputY);
rayCallback = ClosestRayResultCallback.create(rayFrom.value, rayTo.value);
world.ptr.rayTest(rayFrom.value, rayTo.value, rayCallback.value);
if (rayCallback.value.hasHit()) {
#if js
var co = rayCallback.value.get_m_collisionObject();
var body = untyped Ammo.btRigidBody.prototype.upcast(co);
return rbMap.get(untyped body.userIndex);
#elseif cpp
var co = rayCallback.value.m_collisionObject;
return rbMap.get(co.value.getUserIndex());
#end
}
else {
return null;
}
}
public function getRayFrom():BtVector3Pointer {
var camera = iron.Scene.active.camera;
return BtVector3.create(camera.transform.loc.x, camera.transform.loc.y, camera.transform.loc.z);
}
public function getRayTo(inputX:Float, inputY:Float):BtVector3Pointer {
var camera = iron.Scene.active.camera;
var start = new Vec4();
var end = new Vec4();
RayCaster.getDirection(start, end, inputX, inputY, camera);
return BtVector3.create(end.x, end.y, end.z);
}
#end
}