Files
LNXSDK/leenkx/Sources/leenkx/trait/physics/jolt/RigidBody.hx

748 lines
21 KiB
Haxe

package leenkx.trait.physics.jolt;
#if lnx_jolt
import iron.Trait;
import iron.math.Vec4;
import iron.math.Quat;
import iron.data.SceneFormat;
import iron.object.Transform;
import iron.object.MeshObject;
@:enum abstract Shape(Int) from Int to Int {
var Box = 0;
var Sphere = 1;
var ConvexHull = 2;
var Mesh = 3;
var Cone = 4;
var Cylinder = 5;
var Capsule = 6;
var Terrain = 7;
}
class RigidBody extends Trait {
public var physics:PhysicsWorld;
public var transform:Transform = null;
public var body:jolt.Jt.Body;
public var bodyId:jolt.Jt.BodyID;
public var id:Int;
public var destroyed = false;
public var ready = false;
public var shape:Shape;
public var mass:Float;
public var friction:Float;
public var restitution:Float;
public var group:Int;
public var mask:Int;
public var linearDamping:Float;
public var angularDamping:Float;
public var animated:Bool;
public var staticObj:Bool;
public var trigger:Bool;
var lockTranslationX:Bool;
var lockTranslationY:Bool;
var lockTranslationZ:Bool;
var lockRotationX:Bool;
var lockRotationY:Bool;
var lockRotationZ:Bool;
var ccd:Bool;
var useDeactivation:Bool;
static var nextId = 0;
// Native bindings for complex shape creation (HL only)
#if hl
@:hlNative("jolt", "ConvexHullShapeSettings_new")
static function hlConvexHullShapeSettings_new():Dynamic { return null; }
@:hlNative("jolt", "ConvexHullShapeSettings_delete")
static function hlConvexHullShapeSettings_delete(settings:Dynamic):Void {}
@:hlNative("jolt", "ConvexHullShapeSettings_AddPoint")
static function hlConvexHullShapeSettings_AddPoint(settings:Dynamic, x:Float, y:Float, z:Float):Void {}
@:hlNative("jolt", "ConvexHullShapeSettings_Create")
static function hlConvexHullShapeSettings_Create(settings:Dynamic):Dynamic { return null; }
@:hlNative("jolt", "MeshShapeSettings_new")
static function hlMeshShapeSettings_new():Dynamic { return null; }
@:hlNative("jolt", "MeshShapeSettings_delete")
static function hlMeshShapeSettings_delete(settings:Dynamic):Void {}
@:hlNative("jolt", "MeshShapeSettings_AddVertex")
static function hlMeshShapeSettings_AddVertex(settings:Dynamic, x:Float, y:Float, z:Float):Void {}
@:hlNative("jolt", "MeshShapeSettings_AddTriangle")
static function hlMeshShapeSettings_AddTriangle(settings:Dynamic, i0:Int, i1:Int, i2:Int):Void {}
@:hlNative("jolt", "MeshShapeSettings_Create")
static function hlMeshShapeSettings_Create(settings:Dynamic):Dynamic { return null; }
#end
public function new(shape:Shape = Box, mass:Float = 1.0, friction:Float = 0.5, restitution:Float = 0.0, group:Int = 1, mask:Int = 1,
params:RigidBodyParams = null, flags:RigidBodyFlags = null) {
super();
if (params == null) params = {
linearDamping: 0.04,
angularDamping: 0.1,
angularFriction: 0.1,
linearFactorsX: 1.0,
linearFactorsY: 1.0,
linearFactorsZ: 1.0,
angularFactorsX: 1.0,
angularFactorsY: 1.0,
angularFactorsZ: 1.0,
collisionMargin: 0.0,
linearDeactivationThreshold: 0.0,
angularDeactivationThreshold: 0.0,
deactivationTime: 0.0,
linearVelocityMin: 0.0,
linearVelocityMax: 0.0,
angularVelocityMin: 0.0,
angularVelocityMax: 0.0,
lockTranslationX: false,
lockTranslationY: false,
lockTranslationZ: false,
lockRotationX: false,
lockRotationY: false,
lockRotationZ: false
};
if (flags == null) flags = {
animated: false,
trigger: false,
ccd: false,
interpolate: false,
staticObj: false,
useDeactivation: true
};
this.shape = shape;
this.mass = mass;
this.friction = friction;
this.restitution = restitution;
this.group = group;
this.mask = mask;
this.linearDamping = params.linearDamping;
this.angularDamping = params.angularDamping;
this.animated = flags.animated;
this.trigger = flags.trigger;
this.ccd = flags.ccd;
this.staticObj = flags.staticObj || mass == 0.0;
this.lockTranslationX = params.lockTranslationX;
this.lockTranslationY = params.lockTranslationY;
this.lockTranslationZ = params.lockTranslationZ;
this.lockRotationX = params.lockRotationX;
this.lockRotationY = params.lockRotationY;
this.lockRotationZ = params.lockRotationZ;
this.useDeactivation = flags.useDeactivation;
this.id = nextId++;
notifyOnAdd(init);
notifyOnRemove(removeFromWorld);
}
function init() {
if (ready)
return;
transform = object.transform;
transform.buildMatrix();
physics = PhysicsWorld.active;
if (physics == null) {
new PhysicsWorld();
physics = PhysicsWorld.active;
}
#if js
// Check if Jolt is initialized - defer if not
if (!physics.physicsReady) {
// Jolt not ready yet, retry after delay
haxe.Timer.delay(init, 16);
return;
}
#end
ready = true;
var t = transform;
t.buildMatrix();
var pos = t.world.getLoc();
var rot = new Quat();
rot.fromMat(t.world);
// Create shape based on type - use transform.dim like Bullet does
var joltShape = createShape(t);
// Determine motion type (0=Static, 1=Kinematic, 2=Dynamic)
var motionType:Int = staticObj ? 0 : (animated ? 1 : 2);
// Jolt uses RVec3 for world positions
var jPos = new jolt.Jt.RVec3(pos.x, pos.y, pos.z);
var jRot = new jolt.Jt.Quat(rot.x, rot.y, rot.z, rot.w);
if (staticObj || animated) mass = 0;
var settings = new jolt.Jt.BodyCreationSettings(joltShape, jPos, jRot, motionType, staticObj ? 0 : 1);
settings.mFriction = friction;
settings.mRestitution = restitution;
settings.mIsSensor = trigger;
settings.mLinearDamping = linearDamping;
settings.mAngularDamping = angularDamping;
// Match Bullet's deactivation: useDeactivation=false → DISABLE_DEACTIVATION
if (!useDeactivation) {
settings.mAllowSleeping = false;
}
// Set mass to match Bullet (CalculateInertia = 1: use provided mass, compute inertia from shape)
// Use explicit MassProperties object to avoid chained property access issues in HL
if (mass > 0) {
settings.mOverrideMassProperties = 1;
var mp = new jolt.Jt.MassProperties();
mp.mMass = mass;
settings.mMassPropertiesOverride = mp;
#if hl
mp.delete();
#end
}
// Set allowed DOFs (matching Bullet's linear/angular factors + lock properties)
var dofs = 0x3F; // All DOFs by default (0x3F = TranslationX|Y|Z|RotationX|Y|Z)
if (lockTranslationX) dofs &= ~0x01;
if (lockTranslationY) dofs &= ~0x02;
if (lockTranslationZ) dofs &= ~0x04;
if (lockRotationX) dofs &= ~0x08;
if (lockRotationY) dofs &= ~0x10;
if (lockRotationZ) dofs &= ~0x20;
if (dofs != 0x3F) settings.mAllowedDOFs = dofs;
// CCD for fast-moving objects
if (ccd) settings.mMotionQuality = 1; // LinearCast
body = physics.bodyInterface.CreateBody(settings);
bodyId = body.GetID();
#if hl
settings.delete();
jPos.delete();
jRot.delete();
#end
// Add to world (activate dynamic bodies, matching Bullet behavior)
physics.addRigidBody(this, !staticObj);
// Initialize cached position from body creation position
currentPosX = pos.x;
currentPosY = pos.y;
currentPosZ = pos.z;
currentRotX = rot.x;
currentRotY = rot.y;
currentRotZ = rot.z;
currentRotW = rot.w;
// Register visual update callback for non-animated bodies (matching Bullet)
if (!animated) notifyOnUpdate(update);
}
function createShape(t:Transform):jolt.Jt.Shape {
// Use transform.dim (mesh bounding box) for shape dimensions, matching Bullet
var dimX = t.dim.x;
var dimY = t.dim.y;
var dimZ = t.dim.z;
return switch (shape) {
case Box:
var halfExtent = new jolt.Jt.Vec3(dimX / 2, dimY / 2, dimZ / 2);
cast new jolt.Jt.BoxShape(halfExtent);
case Sphere:
var radius = dimX / 2;
cast new jolt.Jt.SphereShape(radius);
case Capsule:
var radius = dimX / 2;
var halfHeight = dimZ / 2 - radius;
if (halfHeight < 0) halfHeight = 0.01;
cast new jolt.Jt.CapsuleShape(halfHeight, radius);
case Cylinder:
var radius = Math.max(dimX, dimY) / 2;
var halfHeight = dimZ / 2;
cast new jolt.Jt.CylinderShape(halfHeight, radius);
case Cone:
var radius = Math.max(dimX, dimY) / 2;
var halfHeight = dimZ / 2;
cast new jolt.Jt.CylinderShape(halfHeight, radius);
case ConvexHull:
#if hl
createConvexHullShape(t.scale);
#else
createConvexHullShapeJS(t.scale, dimX, dimY, dimZ);
#end
case Mesh:
// Jolt MeshShape only works for static bodies (unlike Bullet's GImpact)
// Dynamic mesh bodies must use ConvexHullShape instead
if (staticObj) {
#if hl
createMeshShape(t.scale);
#else
createMeshShapeJS(t.scale, dimX, dimY, dimZ);
#end
} else {
#if hl
createConvexHullShape(t.scale);
#else
createConvexHullShapeJS(t.scale, dimX, dimY, dimZ);
#end
}
case Terrain:
#if hl
createTerrainShape(t.scale);
#else
createMeshShapeJS(t.scale, dimX, dimY, dimZ);
#end
default:
var halfExtent = new jolt.Jt.Vec3(dimX / 2, dimY / 2, dimZ / 2);
cast new jolt.Jt.BoxShape(halfExtent);
};
}
function createConvexHullShape(scale:Vec4):jolt.Jt.Shape {
var mo = cast(object, MeshObject);
if (mo == null || mo.data == null || mo.data.geom == null) {
return cast new jolt.Jt.BoxShape(new jolt.Jt.Vec3(scale.x * 0.5, scale.y * 0.5, scale.z * 0.5));
}
var positions = mo.data.geom.positions.values;
var scalePos = mo.data.scalePos;
#if hl
var settings = hlConvexHullShapeSettings_new();
var numVerts = Std.int(positions.length / 4);
for (i in 0...numVerts) {
var x = (positions[i * 4] / 32767) * scalePos * scale.x;
var y = (positions[i * 4 + 1] / 32767) * scalePos * scale.y;
var z = (positions[i * 4 + 2] / 32767) * scalePos * scale.z;
hlConvexHullShapeSettings_AddPoint(settings, x, y, z);
}
var shape = hlConvexHullShapeSettings_Create(settings);
hlConvexHullShapeSettings_delete(settings);
if (shape == null) {
return cast new jolt.Jt.BoxShape(new jolt.Jt.Vec3(scale.x * 0.5, scale.y * 0.5, scale.z * 0.5));
}
return cast shape;
#else
return cast new jolt.Jt.BoxShape(new jolt.Jt.Vec3(scale.x * 0.5, scale.y * 0.5, scale.z * 0.5));
#end
}
function createMeshShape(scale:Vec4):jolt.Jt.Shape {
var mo = cast(object, MeshObject);
if (mo == null || mo.data == null || mo.data.geom == null) {
return cast new jolt.Jt.BoxShape(new jolt.Jt.Vec3(scale.x * 0.5, scale.y * 0.5, scale.z * 0.5));
}
var positions = mo.data.geom.positions.values;
var indices = mo.data.geom.indices;
var scalePos = mo.data.scalePos;
#if hl
var settings = hlMeshShapeSettings_new();
var numVerts = Std.int(positions.length / 4);
for (i in 0...numVerts) {
var x = (positions[i * 4] / 32767) * scalePos * scale.x;
var y = (positions[i * 4 + 1] / 32767) * scalePos * scale.y;
var z = (positions[i * 4 + 2] / 32767) * scalePos * scale.z;
hlMeshShapeSettings_AddVertex(settings, x, y, z);
}
for (indexArray in indices) {
var numTris = Std.int(indexArray.length / 3);
for (i in 0...numTris) {
hlMeshShapeSettings_AddTriangle(settings, indexArray[i * 3], indexArray[i * 3 + 1], indexArray[i * 3 + 2]);
}
}
var shape = hlMeshShapeSettings_Create(settings);
hlMeshShapeSettings_delete(settings);
if (shape == null) {
return cast new jolt.Jt.BoxShape(new jolt.Jt.Vec3(scale.x * 0.5, scale.y * 0.5, scale.z * 0.5));
}
return cast shape;
#else
return cast new jolt.Jt.BoxShape(new jolt.Jt.Vec3(scale.x * 0.5, scale.y * 0.5, scale.z * 0.5));
#end
}
function createTerrainShape(scale:Vec4):jolt.Jt.Shape {
// Terrain/HeightField shape - requires height data from object
var mo = cast(object, MeshObject);
if (mo == null) {
return cast new jolt.Jt.BoxShape(new jolt.Jt.Vec3(scale.x * 0.5, scale.y * 0.5, scale.z * 0.5));
}
#if js
// For JS, use HeightFieldShapeSettings or fallback to mesh
// Terrain meshes are typically treated as mesh shapes in Jolt
return createMeshShape(scale);
#elseif hl
// For HashLink, terrain is also best represented as mesh shape
// HeightFieldShape requires specific grid data which terrain meshes may not have
return createMeshShape(scale);
#else
return cast new jolt.Jt.BoxShape(new jolt.Jt.Vec3(scale.x * 0.5, scale.y * 0.5, scale.z * 0.5));
#end
}
#if js
function createConvexHullShapeJS(scale:Vec4, dimX:Float, dimY:Float, dimZ:Float):jolt.Jt.Shape {
var mo = cast(object, MeshObject);
if (mo == null || mo.data == null || mo.data.geom == null) {
return cast new jolt.Jt.BoxShape(new jolt.Jt.Vec3(dimX / 2, dimY / 2, dimZ / 2));
}
var positions = mo.data.geom.positions.values;
var scalePos = mo.data.scalePos;
var numVerts = Std.int(positions.length / 4);
var settings:Dynamic = untyped __js__("new Jolt.ConvexHullShapeSettings()");
var points:Dynamic = untyped settings.mPoints;
points.clear();
for (i in 0...numVerts) {
var x:Float = (positions[i * 4] / 32767) * scalePos * scale.x;
var y:Float = (positions[i * 4 + 1] / 32767) * scalePos * scale.y;
var z:Float = (positions[i * 4 + 2] / 32767) * scalePos * scale.z;
var pt:Dynamic = untyped __js__("new Jolt.Vec3({0}, {1}, {2})", x, y, z);
untyped points.push_back(pt);
}
var result:Dynamic = untyped settings.Create();
if (untyped result.HasError()) {
return cast new jolt.Jt.BoxShape(new jolt.Jt.Vec3(dimX / 2, dimY / 2, dimZ / 2));
}
return cast untyped result.Get();
}
function createMeshShapeJS(scale:Vec4, dimX:Float, dimY:Float, dimZ:Float):jolt.Jt.Shape {
var mo = cast(object, MeshObject);
if (mo == null || mo.data == null || mo.data.geom == null) {
return cast new jolt.Jt.BoxShape(new jolt.Jt.Vec3(dimX / 2, dimY / 2, dimZ / 2));
}
var positions = mo.data.geom.positions.values;
var indices = mo.data.geom.indices;
var scalePos = mo.data.scalePos;
var numVerts = Std.int(positions.length / 4);
var settings:Dynamic = untyped __js__("new Jolt.MeshShapeSettings()");
var verts:Dynamic = untyped settings.mTriangleVertices;
var tris:Dynamic = untyped settings.mIndexedTriangles;
verts.clear();
tris.clear();
for (i in 0...numVerts) {
var x:Float = (positions[i * 4] / 32767) * scalePos * scale.x;
var y:Float = (positions[i * 4 + 1] / 32767) * scalePos * scale.y;
var z:Float = (positions[i * 4 + 2] / 32767) * scalePos * scale.z;
var v:Dynamic = untyped __js__("new Jolt.Float3({0}, {1}, {2})", x, y, z);
untyped verts.push_back(v);
}
for (indexArray in indices) {
var numTris = Std.int(indexArray.length / 3);
for (i in 0...numTris) {
var tri:Dynamic = untyped __js__("new Jolt.IndexedTriangle()");
untyped tri.set_mIdx(0, indexArray[i * 3]);
untyped tri.set_mIdx(1, indexArray[i * 3 + 1]);
untyped tri.set_mIdx(2, indexArray[i * 3 + 2]);
untyped tri.set_mMaterialIndex(0);
untyped tris.push_back(tri);
}
}
var result:Dynamic = untyped settings.Create();
if (untyped result.HasError()) {
return cast new jolt.Jt.BoxShape(new jolt.Jt.Vec3(dimX / 2, dimY / 2, dimZ / 2));
}
return cast untyped result.Get();
}
#end
// Cached physics state for visual interpolation (matching Bullet pattern)
var currentPosX:Float = 0;
var currentPosY:Float = 0;
var currentPosZ:Float = 0;
var currentRotX:Float = 0;
var currentRotY:Float = 0;
var currentRotZ:Float = 0;
var currentRotW:Float = 1;
public function physicsUpdate() {
if (!ready)
return;
if (staticObj)
return;
if (animated) {
syncTransform();
return;
}
var active = physics.bodyInterface.IsActive(bodyId);
if (!active)
return;
// Read position and rotation from Jolt into cached state
var p = physics.bodyInterface.GetPosition(bodyId);
var q = physics.bodyInterface.GetRotation(bodyId);
#if js
currentPosX = cast p.GetX();
currentPosY = cast p.GetY();
currentPosZ = cast p.GetZ();
currentRotX = cast q.GetX();
currentRotY = cast q.GetY();
currentRotZ = cast q.GetZ();
currentRotW = cast q.GetW();
// JS: getter return values use internal WASM wrappers - do NOT destroy
#else
currentPosX = p.GetX();
currentPosY = p.GetY();
currentPosZ = p.GetZ();
currentRotX = q.GetX();
currentRotY = q.GetY();
currentRotZ = q.GetZ();
currentRotW = q.GetW();
p.delete();
q.delete();
#end
}
function update() {
transform.loc.set(currentPosX, currentPosY, currentPosZ);
transform.rot.set(currentRotX, currentRotY, currentRotZ, currentRotW);
if (object.parent != null) {
var ptransform = object.parent.transform;
transform.loc.x -= ptransform.worldx();
transform.loc.y -= ptransform.worldy();
transform.loc.z -= ptransform.worldz();
}
transform.buildMatrix();
}
function removeFromWorld() {
if (physics != null) {
physics.removeRigidBody(this);
}
}
public function delete() {
// Cleanup handled by physics world
}
// Physics methods
public function applyForce(force:Vec4, ?loc:Vec4) {
activate();
if (loc == null) {
var f = new jolt.Jt.Vec3(force.x, force.y, force.z);
physics.bodyInterface.AddForce(bodyId, f);
#if hl f.delete(); #end
} else {
var f = new jolt.Jt.Vec3(force.x, force.y, force.z);
var l = new jolt.Jt.RVec3(loc.x, loc.y, loc.z);
physics.bodyInterface.AddForceAtPosition(bodyId, f, l);
#if hl f.delete(); l.delete(); #end
}
}
public function applyImpulse(impulse:Vec4, ?loc:Vec4) {
activate();
if (loc == null) {
var i = new jolt.Jt.Vec3(impulse.x, impulse.y, impulse.z);
physics.bodyInterface.AddImpulse(bodyId, i);
#if hl i.delete(); #end
} else {
var i = new jolt.Jt.Vec3(impulse.x, impulse.y, impulse.z);
var l = new jolt.Jt.RVec3(loc.x, loc.y, loc.z);
physics.bodyInterface.AddImpulseAtPosition(bodyId, i, l);
#if hl i.delete(); l.delete(); #end
}
}
public function applyTorque(torque:Vec4) {
activate();
var t = new jolt.Jt.Vec3(torque.x, torque.y, torque.z);
physics.bodyInterface.AddTorque(bodyId, t);
#if hl t.delete(); #end
}
public function applyTorqueImpulse(impulse:Vec4) {
activate();
var i = new jolt.Jt.Vec3(impulse.x, impulse.y, impulse.z);
physics.bodyInterface.AddAngularImpulse(bodyId, i);
#if hl i.delete(); #end
}
public function setLinearVelocity(v:Vec4) {
var vel = new jolt.Jt.Vec3(v.x, v.y, v.z);
physics.bodyInterface.SetLinearVelocity(bodyId, vel);
#if hl vel.delete(); #end
}
public function getLinearVelocity():Vec4 {
var v = physics.bodyInterface.GetLinearVelocity(bodyId);
var result = new Vec4(v.GetX(), v.GetY(), v.GetZ());
#if hl v.delete(); #end
return result;
}
public function setAngularVelocity(v:Vec4) {
var vel = new jolt.Jt.Vec3(v.x, v.y, v.z);
physics.bodyInterface.SetAngularVelocity(bodyId, vel);
#if hl vel.delete(); #end
}
public function getAngularVelocity():Vec4 {
var v = physics.bodyInterface.GetAngularVelocity(bodyId);
var result = new Vec4(v.GetX(), v.GetY(), v.GetZ());
#if hl v.delete(); #end
return result;
}
public function setFriction(f:Float) {
friction = f;
physics.bodyInterface.SetFriction(bodyId, f);
}
public function setRestitution(r:Float) {
restitution = r;
physics.bodyInterface.SetRestitution(bodyId, r);
}
public function setGravityFactor(f:Float) {
physics.bodyInterface.SetGravityFactor(bodyId, f);
}
public function activate() {
physics.bodyInterface.ActivateBody(bodyId);
}
public function disableSimulation() {
physics.bodyInterface.DeactivateBody(bodyId);
}
public function setPosition(pos:Vec4) {
var p = new jolt.Jt.RVec3(pos.x, pos.y, pos.z);
physics.bodyInterface.SetPosition(bodyId, p, 0);
#if hl p.delete(); #end
}
public function setRotation(rot:Quat) {
var q = new jolt.Jt.Quat(rot.x, rot.y, rot.z, rot.w);
physics.bodyInterface.SetRotation(bodyId, q, 0);
#if hl q.delete(); #end
}
public function syncTransform() {
var t = transform;
var pos = t.world.getLoc();
var rot = new Quat();
rot.fromMat(t.world);
setPosition(pos);
setRotation(rot);
}
public function isActive():Bool {
return physics.bodyInterface.IsActive(bodyId);
}
public function disableGravity() {
setGravityFactor(0.0);
}
public function enableGravity() {
setGravityFactor(1.0);
}
public function getPointVelocity(x:Float, y:Float, z:Float):Vec4 {
var linear = getLinearVelocity();
var relativePoint = new Vec4(x, y, z).sub(transform.world.getLoc());
var angular = getAngularVelocity().cross(relativePoint);
return linear.add(angular);
}
public function disableCollision() {
// In Jolt, use SetIsSensor to disable contact response
body.SetIsSensor(true);
}
public function enableCollision() {
body.SetIsSensor(false);
}
public function notifyOnContact(f:RigidBody->Void) {
if (onContact == null)
onContact = [];
onContact.push(f);
}
public function removeContact(f:RigidBody->Void) {
if (onContact != null)
onContact.remove(f);
}
public var onContact:Array<RigidBody->Void> = null;
public var onReady:Void->Void = null;
public function notifyOnReady(f:Void->Void) {
onReady = f;
if (ready)
onReady();
}
}
typedef RigidBodyParams = {
var linearDamping:Float;
var angularDamping:Float;
var angularFriction:Float;
var linearFactorsX:Float;
var linearFactorsY:Float;
var linearFactorsZ:Float;
var angularFactorsX:Float;
var angularFactorsY:Float;
var angularFactorsZ:Float;
var collisionMargin:Float;
var linearDeactivationThreshold:Float;
var angularDeactivationThreshold:Float;
var deactivationTime:Float;
var linearVelocityMin:Float;
var linearVelocityMax:Float;
var angularVelocityMin:Float;
var angularVelocityMax:Float;
var lockTranslationX:Bool;
var lockTranslationY:Bool;
var lockTranslationZ:Bool;
var lockRotationX:Bool;
var lockRotationY:Bool;
var lockRotationZ:Bool;
}
typedef RigidBodyFlags = {
var animated:Bool;
var trigger:Bool;
var ccd:Bool;
var interpolate:Bool;
var staticObj:Bool;
var useDeactivation:Bool;
}
#end