diff --git a/Sources/Main.hx b/Sources/Main.hx index b4738d92..a2c651a4 100644 --- a/Sources/Main.hx +++ b/Sources/Main.hx @@ -22,6 +22,9 @@ class Main { public static function main() { tasks = 1; tasks++; Config.load(function() { tasks--; start(); }); + #if arm_physics + tasks++; loadPhysics(); + #end tasks--; start(); } @@ -61,8 +64,28 @@ class Main { #else new arm.App(); #end + #if arm_physics + o.addTrait(new arm.plugin.PhysicsWorld()); + #end }); }); }); } + + #if arm_physics + static function loadPhysics() { + var b = haxe.io.Bytes.ofData(Krom.loadBlob("data/ammo.wasm.js")); + var print = function(s:String) { trace(s); }; + var loaded = function() { tasks--; start(); }; + untyped __js__("(1, eval)({0})", b.toString()); + var instantiateWasm = function(imports, successCallback) { + var wasmbin = Krom.loadBlob("data/ammo.wasm.wasm"); + var module = new js.lib.webassembly.Module(wasmbin); + var inst = new js.lib.webassembly.Instance(module, imports); + successCallback(inst); + return inst.exports; + }; + untyped __js__("Ammo({print:print, instantiateWasm:instantiateWasm}).then(loaded)"); + } + #end } diff --git a/Sources/arm/io/ImportMesh.hx b/Sources/arm/io/ImportMesh.hx index 40592052..d7f391fc 100644 --- a/Sources/arm/io/ImportMesh.hx +++ b/Sources/arm/io/ImportMesh.hx @@ -160,7 +160,7 @@ class ImportMesh { // dim[2] = aabb.z; // object.raw.dimensions = dim; #if arm_physics - object.addTrait(new armory.trait.physics.RigidBody(0.0)); + object.addTrait(new arm.plugin.PhysicsBody(0)); #end Context.selectObject(object); diff --git a/Sources/arm/plugin/Bt.hx b/Sources/arm/plugin/Bt.hx new file mode 100644 index 00000000..78f896ff --- /dev/null +++ b/Sources/arm/plugin/Bt.hx @@ -0,0 +1,745 @@ +package arm.plugin; + +@:native('Ammo.btTypedObject') +extern class TypedObject { +} + +@:native('Ammo.btVector3') +extern class Vector3 { + public function new(x:Float, y:Float, z:Float):Void; + public function setValue(x:Float, y:Float, z:Float):Void; + public function setX(x:Float):Void; + public function setY(y:Float):Void; + public function setZ(z:Float):Void; + public function x():Float; + public function y():Float; + public function z():Float; + public function length():Float; + public function normalize():Void; +} + +@:native('Ammo.btQuadWord') +extern class QuadWord { + public function setX(x:Float):Void; + public function setY(y:Float):Void; + public function setZ(z:Float):Void; + public function setW(w:Float):Void; + public function x():Float; + public function y():Float; + public function z():Float; + public function w():Float; +} + +@:native('Ammo.btQuaternion') +extern class Quaternion extends QuadWord { + public function new(x:Float, y:Float, z:Float, w:Float):Void; + public function setEuler(yaw:Float, pitch:Float, roll:Float):Void; + public function slerp(q:Quaternion, t:Float):Quaternion; + public function setValue(x:Float, y:Float, z:Float, w:Float):Void; +} + +@:native('Ammo.btMatrix3x3') +extern class Matrix3x3 { + public function setEulerZYX(ex:Float, ey:Float, ez:Float):Void; + public function getRotation(q:Quaternion):Void; + public function getRow(y:Int):Vector3; +} + +@:native('Ammo.btActionInterface') +extern class ActionInterface { +} + +@:native('Ammo.btTransform') +extern class Transform { + public function new():Void; + public function setIdentity():Void; + public function setOrigin(inVec:Vector3):Void; + public function getOrigin():Vector3; + public function setRotation(inQuat:Quaternion):Void; + public function getRotation():Quaternion; +} + +@:native('Ammo.btIDebugDraw') +extern class IDebugDraw { + public function drawLine(from:Vector3, to:Vector3, color:Vector3):Void; + public function drawContactPoint(pointOnB:Vector3, normalOnB:Vector3, distance:Float, lifeTime:Int, color:Vector3):Void; + public function reportErrorWarning(warningString:String):Void; + public function draw3dText(location:Vector3, textString:String):Void; + public function setDebugMode(debugMode:Int):Void; + public function getDebugMode():Int; +} + +@:native('Ammo.btMotionState') +extern class MotionState { + public function getWorldTransform(centerOfMassWorldTrans:Transform):Void; + public function setWorldTransform(centerOfMassWorldTrans:Transform):Void; +} + +@:native('Ammo.btDefaultMotionState') +extern class DefaultMotionState extends MotionState { + public function new(worldTrans:Transform, centerOfMassOffset:Transform):Void; +} + +@:native('Ammo.btRigidBodyConstructionInfo') +extern class RigidBodyConstructionInfo { + public function new(mass:Float, motionState:MotionState, collisionShape:CollisionShape, localInertia:Vector3):Void; + public var m_friction:Float; + public var m_rollingFriction:Float; +} + +@:native('Ammo.btCollisionObject') +extern class CollisionObject { + public static inline var ACTIVE_TAG = 1; + public static inline var ISLAND_SLEEPING = 2; + public static inline var WANTS_DEACTIVATION = 3; + public static inline var DISABLE_DEACTIVATION = 4; + public static inline var DISABLE_SIMULATION = 5; + + public static inline var CF_STATIC_OBJECT= 1; + public static inline var CF_KINEMATIC_OBJECT= 2; + public static inline var CF_NO_CONTACT_RESPONSE = 4; + public static inline var CF_CHARACTER_OBJECT = 16; + + public function getWorldTransform():Transform; + public function setWorldTransform(trans:Transform):Void; + public function activate(forceActivation:Bool = false):Void; + public function setActivationState(newState:Int):Void; + public function getUserIndex():Int; + public function setUserIndex(index:Int):Void; + public function getUserPointer():Dynamic; + public function setUserPointer(userPointer:Dynamic):Void; + public function isActive():Bool; + public function isKinematicObject():Bool; + public function isStaticObject():Bool; + public function isStaticOrKinematicObject():Bool; + public function setFriction(frict:Float):Void; + public function setRollingFriction(frict:Float):Void; + public function setRestitution(rest:Float):Void; + public function setContactProcessingThreshold(contactProcessingThreshold:Float):Void; + public function setCollisionShape(collisionShape:CollisionShape):Void; + public function getCollisionShape():CollisionShape; + public function setCollisionFlags(flags:Int):Void; + public function getCollisionFlags():Int; + public function setCcdSweptSphereRadius(radius:Float):Void; + public function setCcdMotionThreshold(ccdMotionThreshold:Float):Void; +} + +@:native('Ammo.btRigidBody') +extern class RigidBody extends CollisionObject { + public function new(constructionInfo:RigidBodyConstructionInfo):Void; + public function setMassProps(mass:Float, inertia:Vector3):Void; + public function getMotionState():MotionState; + public function applyCentralForce(force:Vector3):Void; + public function applyForce(force:Vector3, rel_pos:Vector3):Void; + public function applyCentralImpulse(impulse:Vector3):Void; + public function applyImpulse(impulse:Vector3, rel_pos:Vector3):Void; + public function applyTorque(torque:Vector3):Void; + public function applyTorqueImpulse(torque:Vector3):Void; + public function clearForces():Void; + public function setDamping(linear:Float, angular:Float):Void; + public function updateInertiaTensor():Void; + public function getCenterOfMassPosition():Vector3; + public function getCenterOfMassTransform():Transform; + public function setCenterOfMassTransform(trans:Transform):Void; + public function getLinearVelocity():Vector3; + public function setLinearVelocity(lin_vel:Vector3):Void; + public function getAngularVelocity():Vector3; + public function setAngularVelocity(ang_vel:Vector3):Void; + public function setLinearFactor(linearFactor:Vector3):Void; + public function setAngularFactor(angFac:Vector3):Void; + public function setSleepingThresholds(linear:Float, angular:Float):Void; + public function applyGravity():Void; + public function getGravity():Vector3; + public function setGravity(acceleration:Vector3):Void; +} + +@:native('Ammo.btCollisionConfiguration') +extern class CollisionConfiguration { +} + +@:native('Ammo.btDefaultCollisionConfiguration') +extern class DefaultCollisionConfiguration extends CollisionConfiguration { + public function new():Void; +} + +@:native('Ammo.btSoftBodyRigidBodyCollisionConfiguration') +extern class SoftBodyRigidBodyCollisionConfiguration extends CollisionConfiguration { + public function new():Void; +} + +@:native('Ammo.btDispatcher') +extern class Dispatcher { + public function getManifoldByIndexInternal(index:Int):PersistentManifold; + public function getNumManifolds():Int; +} + +@:native('Ammo.btCollisionDispatcher') +extern class CollisionDispatcher extends Dispatcher { + public function new(collisionConfiguration:CollisionConfiguration):Void; +} + +@:native('Ammo.btBroadphaseInterface') +extern class BroadphaseInterface { +} + +@:native('Ammo.btDbvtBroadphase') +extern class DbvtBroadphase extends BroadphaseInterface { + public function new():Void; +} + +@:native('Ammo.btAxisSweep3') +extern class AxisSweep3 extends BroadphaseInterface { + public function new(worldAabbMin:Vector3, worldAabbMax:Vector3):Void; +} + +@:native('Ammo.btConstraintSolver') +extern class ConstraintSolver { +} + +@:native('Ammo.btSequentialImpulseConstraintSolver') +extern class SequentialImpulseConstraintSolver extends ConstraintSolver { + public function new():Void; +} + +@:native('Ammo.btDefaultSoftBodySolver') +extern class DefaultSoftBodySolver extends ConstraintSolver { + public function new():Void; +} + +@:native('Ammo.RayResultCallback') +extern class RayResultCallback { + public function hasHit():Bool; + public function new():Void; + public function get_m_collisionFilterGroup():Int; + public function set_m_collisionFilterGroup(g:Int):Void; + public function get_m_collisionFilterMask():Int; + public function set_m_collisionFilterMask(m:Int):Void; + public function get_m_collisionObject():CollisionObject; +} + +@:native('Ammo.ClosestRayResultCallback') +extern class ClosestRayResultCallback extends RayResultCallback { + public function new(rayFromWorld:Vector3, rayToWorld:Vector3):Void; + public function get_m_hitNormalWorld():Vector3; + public function get_m_hitPointWorld():Vector3; +} + +@:native('Ammo.ConcreteContactResultCallback') +extern class ConcreteContactResultCallback extends RayResultCallback { + public function new():Void; +} + +@:native('Ammo.btCollisionWorld') +extern class CollisionWorld { + public function rayTest(rayFromWorld:Vector3, rayToWorld:Vector3, resultCallback:RayResultCallback):Void; + public function updateSingleAabb(colObj:CollisionObject):Void; + public function getPairCache():OverlappingPairCache; + public function addCollisionObject(collisionObject:CollisionObject):Void; + public function removeCollisionObject(collisionObject:CollisionObject):Void; + @:native("addCollisionObject") + public function addCollisionObjectToGroup(collisionObject:CollisionObject, collisionFilterGroup:Int, collisionFilterMask:Int):Void; +} + +@:native('Ammo.btDynamicsWorld') +extern class DynamicsWorld extends CollisionWorld { + public function addRigidBody(body:RigidBody):Void; + @:native("addRigidBody") + public function addRigidBodyToGroup(body:RigidBody, group:Int, mask:Int):Void; + public function removeRigidBody(body:RigidBody):Void; + public function addAction(action:ActionInterface):Void; + public function removeAction(action:ActionInterface):Void; + public function addConstraint(constraint:TypedConstraint, disableCollisionsBetweenLinkedBodies:Bool = false):Void; + public function removeConstraint(constraint:TypedConstraint):Void; + public function getGravity():Vector3; + public function setGravity(v:Vector3):Void; + public function stepSimulation(timeStep:Float, maxSubSteps:Float = 1, fixedTimeStep:Float = 1.0 / 60.0):Void; +} + +@:native('Ammo.btDiscreteDynamicsWorld') +extern class DiscreteDynamicsWorld extends DynamicsWorld { + public function new(dispatcher:Dispatcher, pairCache:BroadphaseInterface, constraintSolver:ConstraintSolver, collisionConfiguration:CollisionConfiguration):Void; + public function debugDrawWorld():Void; +} + +@:native('Ammo.btSoftBodyWorldInfo') +extern class SoftBodyWorldInfo { + public function set_m_gravity(v:Vector3):Void; +} + +@:native('Ammo.btSoftRigidDynamicsWorld') +extern class SoftRigidDynamicsWorld extends DynamicsWorld { + public function new(dispatcher:Dispatcher, pairCache:BroadphaseInterface, constraintSolver:ConstraintSolver, collisionConfiguration:CollisionConfiguration, softConstraintSolver:ConstraintSolver):Void; + public function addSoftBody(body:SoftBody, collisionFilterGroup:Int, collisionFilterMask:Int):Void; + public function removeSoftBody(body:SoftBody):Void; + public function getWorldInfo():SoftBodyWorldInfo; +} + +@:native('Ammo.btSimpleDynamicsWorld') +extern class SimpleDynamicsWorld extends DynamicsWorld { + public function new(dispatcher:Dispatcher, pairCache:BroadphaseInterface, constraintSolver:ConstraintSolver, collisionConfiguration:CollisionConfiguration):Void; +} + +@:native('Ammo.btCollisionShape') +extern class CollisionShape { + public function calculateLocalInertia(mass:Float, inertia:Vector3):Void; + public function setMargin(margin:Float):Void; + public function setLocalScaling(scaling:Vector3):Void; + public function getLocalScaling():Vector3; +} + +@:native('Ammo.btCompoundShape') +extern class CompoundShape extends CollisionShape { + public function new(enableDynamicAabbTree:Bool = true):Void; + public function addChildShape(localTransform:Transform, shape:CollisionShape):Void; +} + +@:native('Ammo.btConvexShape') +extern class ConvexShape extends CollisionShape { +} + +@:native('Ammo.btConcaveShape') +extern class ConcaveShape extends CollisionShape { +} + +@:native('Ammo.btConvexInternalShape') +extern class ConvexInternalShape extends ConvexShape { +} + +@:native('Ammo.btPolyhedralConvexShape') +extern class PolyhedralConvexShape extends ConvexInternalShape { +} + +@:native('Ammo.btBoxShape') +extern class BoxShape extends PolyhedralConvexShape { + public function new(boxHalfExtents:Vector3):Void; +} + +@:native('Ammo.btSphereShape') +extern class SphereShape extends ConvexInternalShape { + public function new(radius:Float):Void; +} + +@:native('Ammo.btStaticPlaneShape') +extern class StaticPlaneShape extends ConcaveShape { + public function new(planeNormal:Vector3, planeConstant:Float):Void; +} + +@:native('Ammo.btPolyhedralConvexAabbCachingShape') +extern class PolyhedralConvexAabbCachingShape extends PolyhedralConvexShape { +} + +@:native('Ammo.btConvexHullShape') +extern class ConvexHullShape extends PolyhedralConvexAabbCachingShape { + public function new():Void; + public function addPoint(point:Vector3, recalculateLocalAabb:Bool = true):Void; +} + +@:native('Ammo.btCapsuleShape') +extern class CapsuleShape extends ConvexInternalShape { + public function new(radius:Float, height:Float):Void; + public function getUpAxis():Int; + public function getRadius():Float; + public function getHalfHeight():Float; +} + +@:native('Ammo.btCapsuleShapeX') +extern class CapsuleShapeX extends CapsuleShape { + public function new(radius:Float, height:Float):Void; +} + +@:native('Ammo.btCapsuleShapeZ') +extern class CapsuleShapeZ extends CapsuleShape { + public function new(radius:Float, height:Float):Void; +} + +@:native('Ammo.btCylinderShape') +extern class CylinderShape extends ConvexInternalShape { + public function new(halfExtents:Vector3):Void; +} + +@:native('Ammo.btCylinderShapeX') +extern class CylinderShapeX extends CylinderShape { + public function new(halfExtents:Vector3):Void; +} + +@:native('Ammo.btCylinderShapeZ') +extern class CylinderShapeZ extends CylinderShape { + public function new(halfExtents:Vector3):Void; +} + +@:native('Ammo.btConeShape') +extern class ConeShape extends ConvexInternalShape { + public function new(radius:Float, height:Float):Void; +} + +@:native('Ammo.btConeShapeX') +extern class ConeShapeX extends ConeShape { + public function new(radius:Float, height:Float):Void; +} + +@:native('Ammo.btConeShapeZ') +extern class ConeShapeZ extends ConeShape { + public function new(radius:Float, height:Float):Void; +} + +@:native('Ammo.btHeightfieldTerrainShape') +extern class HeightfieldTerrainShape extends ConcaveShape { + // heightDataType - float, double, integer, short, fixedpoint88, uchar + public function new(heightStickWidth:Int, heightStickLength:Int, heightfieldData:Dynamic, heightScale:Float, minHeight:Float, maxHeight:Float, upAxis:Int, heightDataType:Int, flipQuadEdges:Bool):Void; +} + +@:native('Ammo.btStridingMeshInterface') +extern class StridingMeshInterface { + public function new():Void; +} + +@:native('Ammo.btIndexedMesh') +extern class IndexedMesh { + public function new():Void; +} + +@:native('Ammo.btTriangleIndexVertexArray') +extern class TriangleIndexVertexArray extends StridingMeshInterface { + public function new():Void; +} + +@:native('Ammo.btTriangleMesh') +extern class TriangleMesh extends TriangleIndexVertexArray { + public function new(use32bitIndices:Bool = true, use4componentVertices:Bool = true):Void; + public function addTriangle(vertex0:Vector3, vertex1:Vector3, vertex2:Vector3, removeDuplicateVertices:Bool = false):Void; +} + +@:native('Ammo.btTriangleMeshShape') +extern class TriangleMeshShape extends ConcaveShape { + public function new(meshInterface:StridingMeshInterface):Void; +} + +@:native('Ammo.btBvhTriangleMeshShape') +extern class BvhTriangleMeshShape extends TriangleMeshShape { + public function new(meshInterface:StridingMeshInterface, useQuantizedAabbCompression:Bool, buildBvh:Bool = true):Void; +} + +@:native('Ammo.btGImpactMeshShape') +extern class GImpactMeshShape extends ConcaveShape { + public function new(meshInterface:StridingMeshInterface):Void; + public function updateBound():Void; +} + +@:native('Ammo.GImpactCollisionAlgorithm') +extern class GImpactCollisionAlgorithm { + public function new():Void; + public function registerAlgorithm(dispatcher:CollisionDispatcher):Void; +} + +@:native('Ammo.btVehicleTuning') +extern class VehicleTuning extends ActionInterface { + public function new():Void; +} + +@:native('Ammo.btVehicleRaycaster') +extern class VehicleRaycaster { +} + +@:native('Ammo.btDefaultVehicleRaycaster') +extern class DefaultVehicleRaycaster extends VehicleRaycaster { + public function new(world:DynamicsWorld):Void; +} + +@:native('Ammo.btWheelInfoConstructionInfo') +extern class RaycastInfo { + public var m_contactNormalWS:Vector3; + public var m_contactPointWS:Vector3; + public var m_suspensionLength:Float; + public var m_hardPointWS:Vector3; + public var m_wheelDirectionWS:Vector3; + public var m_wheelAxleWS:Vector3; + public var m_isInContact:Bool; +} + +@:native('Ammo.btWheelInfoConstructionInfo') +extern class WheelInfoConstructionInfo { + public function new():Void; + public var m_chassisConnectionCS:Vector3; + public var m_wheelDirectionCS:Vector3; + public var m_wheelAxleCS:Vector3; + public var m_suspensionRestLength:Float; + public var m_maxSuspensionTravelCm:Float; + public var m_wheelRadius:Float; + public var m_suspensionStiffness:Float; + public var m_wheelsDampingCompression:Float; + public var m_wheelsDampingRelaxation:Float; + public var m_frictionSlip:Float; + public var m_maxSuspensionForce:Float; + public var m_bIsFrontWheel:Bool; +} + +@:native('Ammo.btWheelInfo') +extern class WheelInfo { + public function new(ci:WheelInfoConstructionInfo):Void; + public var m_raycastInfo:RaycastInfo; + public var m_worldTransform:Transform; + public var m_chassisConnectionPointCS:Vector3; + public var m_wheelDirectionCS:Vector3; + public var m_wheelAxleCS:Vector3; + public var m_suspensionRestLength1:Float; + public var m_maxSuspensionTravelCm:Float; + public function getSuspensionRestLength():Float; + public var m_suspensionStiffness:Float; + public var m_wheelsDampingCompression:Float; + public var m_wheelsDampingRelaxation:Float; + public var m_frictionSlip:Float; + public var m_steering:Float; + public var m_rotation:Float; + public var m_deltaRotation:Float; + public var m_rollInfluence:Float; + public var m_maxSuspensionForce:Float; + public var m_wheelsRadius:Float; + public var m_engineForce:Float; + public var m_brake:Float; + public var m_bIsFrontWheel:Bool; + public var m_clippedInvContactDotSuspension:Float; + public var m_suspensionRelativeVelocity:Float; + public var m_wheelsSuspensionForce:Float; + public var m_skidInfo:Float; +} + +@:native('Ammo.btRaycastVehicle') +extern class RaycastVehicle extends ActionInterface { + public function new(tuning:VehicleTuning, chassis:RigidBody, raycaster:VehicleRaycaster):Void; + public function setCoordinateSystem(rightIndex:Int, upIndex:Int, forwardIndex:Int):Void; + public function addWheel(connectionPointCS0:Vector3, wheelDirectionCS0:Vector3, wheelAxleCS:Vector3, suspensionRestLength:Float, wheelRadius:Float, tuning:VehicleTuning, isFrontWheel:Bool):WheelInfo; + public function getNumWheels():Int; + public function getWheelInfo(index:Int):WheelInfo; + public function resetSuspension():Void; + public function getWheelTransformWS(wheelIndex:Int):Transform; + public function updateWheelTransform(wheelIndex:Int, interpolatedTransform:Bool = true):Void; + public function applyEngineForce(force:Float, wheel:Int):Void; + public function setBrake(brake:Float, wheelIndex:Int):Void; + public function setPitchControl(pitch:Float):Void; + public function updateSuspension(deltaTime:Float ):Void; + public function updateFriction(deltaTime:Float ):Void; + public function setSteeringValue(steering:Float, wheel:Int):Void; + public function getRightAxis():Int; + public function getUpAxis():Int; + public function getForwardAxis():Int; + public function getForwardVector():Vector3; + public function getCurrentSpeedKmHour():Float; +} + +@:native('Ammo.btPersistentManifold') +extern class PersistentManifold extends TypedObject { + public function new():Void; + public function getBody0():CollisionObject; + public function getBody1():CollisionObject; + public function getNumContacts():Int; + public function getContactPoint(index:Int):ManifoldPoint; +} + +@:native('Ammo.btManifoldPoint') +extern class ManifoldPoint { + public function new():Void; + public function get_m_positionWorldOnA():Vector3; + public function get_m_positionWorldOnB():Vector3; + public function get_m_normalWorldOnB():Vector3; + public function getDistance():Float; + public function getAppliedImpulse():Float; +} + +@:native('Ammo.btTypedConstraint') +extern class TypedConstraint extends TypedObject { + public function setBreakingImpulseThreshold(threshold:Float):Void; +} + +@:native('Ammo.btGeneric6DofConstraint') +extern class Generic6DofConstraint extends TypedConstraint { + public function new(rbB:RigidBody, frameInB:Transform, useLinearReferenceFrameB:Bool):Void; + public static inline function new2(rbA:RigidBody, rbB:RigidBody, frameInA:Transform, frameInB:Transform, useLinearReferenceFrameB:Bool = false):Generic6DofConstraint { + var _r1 = rbA, _r2 = rbB, _fa = frameInA, _fb = frameInB, _b = useLinearReferenceFrameB; + return untyped __js__("new Ammo.btGeneric6DofConstraint(_r1, _r2, _fa, _fb, _b)"); + } + public function setLinearLowerLimit(linearLower:Vector3):Void; + public function setLinearUpperLimit(linearUpper:Vector3):Void; + public function setAngularLowerLimit(angularLower:Vector3):Void; + public function setAngularUpperLimit(angularUpper:Vector3):Void; + public function setParam(num:Int, value:Float, axis:Int = -1):Void; + public function getFrameOffsetA():Transform; +} + +@:native('Ammo.btGeneric6DofSpringConstraint') +extern class Generic6DofSpringConstraint extends Generic6DofConstraint { + public function new(rbA:RigidBody, rbB:RigidBody, frameInA:Transform, frameInB:Transform, useLinearReferenceFrameB:Bool):Void; + public function enableSpring(index:Int, onOff:Bool):Void; + public function setStiffness(index:Int,stiffness:Float):Void; + public function setDamping(index:Int,damping:Float):Void; +} + +@:native('Ammo.btHingeConstraint') +extern class HingeConstraint extends TypedConstraint { + public function new(rbA:RigidBody, rbB:RigidBody, pivotInA:Vector3, pivotInB:Vector3, axisInA:Vector3, axisInB:Vector3, useReferenceFrameA:Bool = false):Void; + public function setLimit(low:Float, high:Float, _softness:Float = 0.9, _biasFactor:Float = 0.3, _relaxationFactor:Float = 1.0):Void; +} + +@:native('Ammo.btSliderConstraint') +extern class SliderConstraint extends TypedConstraint { + public function new(rbA:RigidBody, rbB:RigidBody, frameInA:Transform, frameInB:Transform, useReferenceFrameA:Bool):Void; + public function setLowerLinLimit(lowerLimit:Float):Void; + public function setUpperLinLimit(upperLimit:Float):Void; + public function setLowerAngLimit(lowerAngLimit:Float):Void; + public function setUpperAngLimit(upperAngLimit:Float):Void; +} + +@:native('Ammo.btPoint2PointConstraint') +extern class Point2PointConstraint extends TypedConstraint { + public function new(rbA:RigidBody, rbB:RigidBody, pivotInA:Vector3, pivotInB:Vector3):Void; + public function setPivotA(pivotA:Vector3):Void; + public function setPivotB(pivotB:Vector3):Void; + public function getPivotInA():Vector3; + public function getPivotInB():Vector3; +} + +@:native('Ammo.Config') +extern class Config { + public function set_viterations(i:Int):Void; + public function set_piterations(i:Int):Void; + public function set_collisions(i:Int):Void; + public function set_kDF(f:Float):Void; + public function set_kDP(f:Float):Void; + public function set_kPR(f:Float):Void; + public function set_kVC(f:Float):Void; + public function set_kAHR(f:Float):Void; +} + +@:native('Ammo.tNodeArray') +extern class TNodeArray { + public function at(i:Int):Node; + public function size():Int; +} + +@:native('Ammo.tMaterialArray') +extern class TMaterialArray { + public function at(i:Int):Material; +} + +@:native('Ammo.tAnchorArray') +extern class TAnchorArray { + public function new(); + public function at(i:Int):Anchor; + public function clear():Void; + public function size():Int; + public function push_back(anc:Anchor):Void; + public function pop_back():Void; +} + +@:native('Ammo.Node') +extern class Node { + public function get_m_x():Vector3; + public function get_m_n():Vector3; +} + +@:native('Ammo.Material') +extern class Material { + public function new(); + public function set_m_kLST(kAST:Float):Void; + public function get_m_kLST():Void; + public function set_m_kAST(kAST:Float):Void; + public function get_m_kAST():Void; + public function set_m_kVST(kVST:Float):Void; + public function get_m_kVST():Float; + public function set_m_flags(flags:Int):Void; + public function get_m_flags():Int; +} + +@:native('Ammo.Anchor') +extern class Anchor { + public function set_m_node(node:Node):Void; + public function get_m_node():Node; + public function set_m_local(local:Vector3):Void; + public function get_m_local():Vector3; + public function set_m_body(body:RigidBody):Void; + public function get_m_body():RigidBody; + public function set_m_influence(influence:Float):Void; + public function get_m_influence():Float; + public function set_m_c1(c1:Vector3):Void; + public function get_m_c1():Vector3; + public function set_m_c2(c2:Float):Void; + public function get_m_c2():Float; +} + +@:native('Ammo.btSoftBody') +extern class SoftBody extends CollisionObject { + public function get_m_nodes():TNodeArray; + public function get_m_cfg():Config; + public function get_m_materials():TMaterialArray; + public function get_m_anchors():TAnchorArray; + public function setTotalMass(mass:Float, fromfaces:Bool = false):Void; + public function generateClusters(k:Int, maxiterations:Int = 8192):Void; + public function generateBendingConstraints(distance:Int, mat:Dynamic = 0):Void; + public function appendAnchor(node:Int, body:RigidBody, disableCollisionBetweenLinkedBodies:Bool, influence:Float):Void; + public function appendLink(node:Node, node1:Node, mat:Material, bcheckexist:Bool=false):Void; + public function addForce(f:Vector3, node:Int):Void; +} + +@:native('Ammo.btSoftBodyHelpers') +extern class SoftBodyHelpers { + public function new():Void; + function CreateFromTriMesh(worldInfo:SoftBodyWorldInfo, vertices:haxe.ds.Vector, triangles:haxe.ds.Vector, ntriangles:Int, randomizeConstraints:Bool = true):SoftBody; +} + +@:native('Ammo.btOverlappingPairCallback') +extern class OverlappingPairCallback { +} + +@:native('Ammo.btGhostPairCallback') +extern class GhostPairCallback extends OverlappingPairCallback { + public function new():Void; +} + +@:native('Ammo.btOverlappingPairCache') +extern class OverlappingPairCache { + public function setInternalGhostPairCallback(ghostPairCallback:OverlappingPairCallback):Void; +} + +@:native('Ammo.btGhostObject') +extern class GhostObject extends CollisionObject { + public function new():Void; + public function getNumOverlappingObjects():Int; + public function getOverlappingObject(index:Int):CollisionObject; +} + +@:native('Ammo.btPairCachingGhostObject') +extern class PairCachingGhostObject extends GhostObject { + public function new():Void; +} + +@:native('Ammo.btKinematicCharacterController') +extern class KinematicCharacterController extends ActionInterface { + public function new(ghostObject:PairCachingGhostObject, convexShape:ConvexShape, stepHeight:Float, upAxis:Int = 1):Void; + public function setUpAxis(axis:Int):Void; // setUp in cpp + public function jump():Void; + public function setGravity(gravity:Float):Void; + public function getGravity():Float; + public function canJump():Bool; + public function onGround():Bool; + public function setWalkDirection(walkDirection:Vector3):Void; + public function setVelocityForTimeInterval(velocity:Vector3, timeInterval:Float):Void; + public function warp(origin:Vector3):Void; + public function preStep(collisionWorld:CollisionWorld):Void; + public function playerStep(collisionWorld:CollisionWorld, dt:Float):Void; + public function setFallSpeed(fallSpeed:Float):Void; + public function setJumpSpeed(jumpSpeed:Float):Void; + public function setMaxJumpHeight(maxJumpHeight:Float):Void; + public function setMaxSlope(slopeRadians:Float):Void; + public function getMaxSlope():Float; + public function getGhostObject():PairCachingGhostObject; + public function setUseGhostSweepTest(useGhostObjectSweepTest:Bool):Void; + public function setUpInterpolate(value:Bool):Void; +} + +@:native('Ammo') +extern class Ammo { + public static function destroy(obj:Dynamic):Void; + public static function castObject(obj:Dynamic, to:Class):Dynamic; + public static function _malloc(byteSize:Int):Int; + public static var HEAPU8:Dynamic; + public static var HEAPF32:Dynamic; +} diff --git a/Sources/arm/plugin/Gizmo.hx b/Sources/arm/plugin/Gizmo.hx index 39c5b36d..9876cde9 100644 --- a/Sources/arm/plugin/Gizmo.hx +++ b/Sources/arm/plugin/Gizmo.hx @@ -55,7 +55,7 @@ class Gizmo { object.transform.buildMatrix(); #if arm_physics - object.addTrait(new armory.trait.physics.RigidBody(0, 0)); + object.addTrait(new arm.plugin.PhysicsBody(0)); #end Context.selectObject(object); @@ -71,7 +71,7 @@ class Gizmo { object.transform.buildMatrix(); #if arm_physics - object.addTrait(new armory.trait.physics.RigidBody(0, 0)); + object.addTrait(new arm.plugin.PhysicsBody(0)); #end Context.selectObject(object); @@ -87,9 +87,9 @@ class Gizmo { if (mouse.started("middle")) { #if arm_physics - var physics = armory.trait.physics.PhysicsWorld.active; - var rb = physics.pickClosest(mouse.viewX, mouse.viewY); - if (rb != null) Context.selectObject(rb.object); + var physics = arm.plugin.PhysicsWorld.active; + var pb = physics.pickClosest(mouse.viewX, mouse.viewY); + if (pb != null) Context.selectObject(pb.object); #end } @@ -120,8 +120,8 @@ class Gizmo { Context.object.transform.loc.x = hit.x - UITrait.inst.axisStart; Context.object.transform.buildMatrix(); #if arm_physics - var rb = Context.object.getTrait(armory.trait.physics.RigidBody); - if (rb != null) rb.syncTransform(); + var pb = Context.object.getTrait(arm.plugin.PhysicsBody); + if (pb != null) pb.syncTransform(); #end } } @@ -132,8 +132,8 @@ class Gizmo { Context.object.transform.loc.y = hit.y - UITrait.inst.axisStart; Context.object.transform.buildMatrix(); #if arm_physics - var rb = Context.object.getTrait(armory.trait.physics.RigidBody); - if (rb != null) rb.syncTransform(); + var pb = Context.object.getTrait(arm.plugin.PhysicsBody); + if (pb != null) pb.syncTransform(); #end } } @@ -144,8 +144,8 @@ class Gizmo { Context.object.transform.loc.z = hit.z - UITrait.inst.axisStart; Context.object.transform.buildMatrix(); #if arm_physics - var rb = Context.object.getTrait(armory.trait.physics.RigidBody); - if (rb != null) rb.syncTransform(); + var pb = Context.object.getTrait(arm.plugin.PhysicsBody); + if (pb != null) pb.syncTransform(); #end } } diff --git a/Sources/arm/plugin/PhysicsBody.hx b/Sources/arm/plugin/PhysicsBody.hx new file mode 100644 index 00000000..209ee429 --- /dev/null +++ b/Sources/arm/plugin/PhysicsBody.hx @@ -0,0 +1,452 @@ +package arm.plugin; + +#if arm_physics + +import iron.math.Vec4; +import iron.math.Quat; +import iron.object.Transform; +import iron.object.MeshObject; +import iron.data.MeshData; + +@:access(arm.plugin.PhysicsWorld) +class PhysicsBody extends iron.Trait { + + public var mass = 1.0; + public var friction = 0.5; + public var restitution = 0.0; + public var collisionMargin = 0.0; + public var linearDamping = 0.04; + public var angularDamping = 0.1; + public var linearFactors = [1.0, 1.0, 1.0]; + public var angularFactors = [1.0, 1.0, 1.0]; + public var linearThreshold = 0.0; + public var angularThreshold = 0.0; + public var ccd = false; // Continuous collision detection + public var trigger = false; + public var group = 1; + public var mask = 1; + var shape:Shape; + var destroyed = false; + var bodyScaleX:Float; // Transform scale at creation time + var bodyScaleY:Float; + var bodyScaleZ:Float; + var currentScaleX:Float; + var currentScaleY:Float; + var currentScaleZ:Float; + + var body:Bt.RigidBody = null; + var motionState:Bt.MotionState; + var btshape:Bt.CollisionShape; + var ready = false; + public var id = 0; + public var heightData:haxe.io.Bytes = null; + + static var nextId = 0; + static var ammoArray:Int = -1; + static var gimpactRegistered = false; + static var first = true; + static var vec1:Bt.Vector3; + static var vec2:Bt.Vector3; + static var vec3:Bt.Vector3; + static var quat1:Bt.Quaternion; + static var trans1:Bt.Transform; + static var trans2:Bt.Transform; + static var quat = new Quat(); + + static var convexHullCache = new Map(); + static var triangleMeshCache = new Map(); + static var usersCache = new Map(); + + public function new(shape:Shape) { + super(); + + if (first) { + first = false; + vec1 = new Bt.Vector3(0, 0, 0); + vec2 = new Bt.Vector3(0, 0, 0); + vec3 = new Bt.Vector3(0, 0, 0); + quat1 = new Bt.Quaternion(0, 0, 0, 0); + trans1 = new Bt.Transform(); + trans2 = new Bt.Transform(); + } + + this.shape = shape; + notifyOnAdd(init); + } + + inline function withMargin(f:Float) { return f - f * collisionMargin; } + + function init() { + if (ready) return; + ready = true; + + if (!Std.is(object, MeshObject)) return; // No mesh data + var transform = object.transform; + var physics = PhysicsWorld.active; + + if (shape == Shape.Box) { + vec1.setX(withMargin(transform.dim.x / 2)); + vec1.setY(withMargin(transform.dim.y / 2)); + vec1.setZ(withMargin(transform.dim.z / 2)); + btshape = new Bt.BoxShape(vec1); + } + else if (shape == Shape.Sphere) { + btshape = new Bt.SphereShape(withMargin(transform.dim.x / 2)); + } + else if (shape == Shape.ConvexHull) { + var shapeConvex = fillConvexHull(transform.scale, collisionMargin); + btshape = shapeConvex; + } + else if (shape == Shape.Cone) { + var coneZ = new Bt.ConeShapeZ( + withMargin(transform.dim.x / 2), // Radius + withMargin(transform.dim.z)); // Height + var cone:Bt.ConeShape = coneZ; + btshape = cone; + } + else if (shape == Shape.Cylinder) { + vec1.setX(withMargin(transform.dim.x / 2)); + vec1.setY(withMargin(transform.dim.y / 2)); + vec1.setZ(withMargin(transform.dim.z / 2)); + var cylZ = new Bt.CylinderShapeZ(vec1); + var cyl:Bt.CylinderShape = cylZ; + btshape = cyl; + } + else if (shape == Shape.Capsule) { + var r = transform.dim.x / 2; + var capsZ = new Bt.CapsuleShapeZ( + withMargin(r), // Radius + withMargin(transform.dim.z - r * 2)); // Distance between 2 sphere centers + var caps:Bt.CapsuleShape = capsZ; + btshape = caps; + } + else if (shape == Shape.Mesh) { + var meshInterface = fillTriangleMesh(transform.scale); + if (mass > 0) { + var shapeGImpact = new Bt.GImpactMeshShape(meshInterface); + shapeGImpact.updateBound(); + var shapeConcave:Bt.ConcaveShape = shapeGImpact; + btshape = shapeConcave; + if (!gimpactRegistered) { + gimpactRegistered = true; + new Bt.GImpactCollisionAlgorithm().registerAlgorithm(physics.dispatcher); + } + } + else { + var shapeBvh = new Bt.BvhTriangleMeshShape(meshInterface, true, true); + var shapeTri:Bt.TriangleMeshShape = shapeBvh; + var shapeConcave:Bt.ConcaveShape = shapeTri; + btshape = shapeConcave; + } + } + else if (shape == Shape.Terrain) { + var length = heightData.length; + if (ammoArray == -1) { + ammoArray = Bt.Ammo._malloc(length); + } + // From texture bytes + for (i in 0...length) { + Bt.Ammo.HEAPU8[ammoArray + i] = heightData.get(i); + } + var slice = Std.int(Math.sqrt(length)); // Assuming square terrain data + var axis = 2; // z + var dataType = 5; // u8 + btshape = new Bt.HeightfieldTerrainShape(slice, slice, ammoArray, 1 / 255, 0, 1, axis, dataType, false); + vec1.setX(transform.dim.x / slice); + vec1.setY(transform.dim.y / slice); + vec1.setZ(transform.dim.z); + btshape.setLocalScaling(vec1); + } + + trans1.setIdentity(); + vec1.setX(transform.worldx()); + vec1.setY(transform.worldy()); + vec1.setZ(transform.worldz()); + trans1.setOrigin(vec1); + quat.fromMat(transform.world); + quat1.setValue(quat.x, quat.y, quat.z, quat.w); + trans1.setRotation(quat1); + trans2.setIdentity(); + motionState = new Bt.DefaultMotionState(trans1, trans2); // Transform, center of mass offset + + vec1.setX(0); + vec1.setY(0); + vec1.setZ(0); + var inertia = vec1; + + if (mass > 0) btshape.calculateLocalInertia(mass, inertia); + var bodyCI = new Bt.RigidBodyConstructionInfo(mass, motionState, btshape, inertia); + body = new Bt.RigidBody(bodyCI); + + body.setFriction(friction); + if (shape == Shape.Sphere || shape == Shape.Cylinder || shape == Shape.Cone || shape == Shape.Capsule) { + angularDamping += friction; + } + body.setRestitution(restitution); + // body.setSleepingThresholds(linearThreshold, angularThreshold); + // body.setDeactivationTime(deactivationTime); + body.setDamping(linearDamping, angularDamping); + setLinearFactor(linearFactors[0], linearFactors[1], linearFactors[2]); + setAngularFactor(angularFactors[0], angularFactors[1], angularFactors[2]); + if (trigger) body.setCollisionFlags(body.getCollisionFlags() | Bt.CollisionObject.CF_NO_CONTACT_RESPONSE); + if (mass == 0.0) body.setCollisionFlags(body.getCollisionFlags() | Bt.CollisionObject.CF_STATIC_OBJECT); + if (ccd) setCcd(transform.radius); + + bodyScaleX = currentScaleX = transform.scale.x; + bodyScaleY = currentScaleY = transform.scale.y; + bodyScaleZ = currentScaleZ = transform.scale.z; + + id = nextId++; + untyped body.userIndex = id; + + physics.addBody(this); + notifyOnRemove(removeFromWorld); + + Bt.Ammo.destroy(bodyCI); + } + + function physicsUpdate() { + if (!ready) return; + var trans = body.getWorldTransform(); + + var p = trans.getOrigin(); + var q = trans.getRotation(); + var qw:Bt.QuadWord = q; + + var transform = object.transform; + transform.loc.set(p.x(), p.y(), p.z()); + transform.rot.set(qw.x(), qw.y(), qw.z(), qw.w()); + 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(); + trace(p.z()); + } + + public function removeFromWorld() { + PhysicsWorld.active.removeBody(this); + } + + public function activate() { + body.activate(false); + } + + public function setGravity(v:Vec4) { + vec1.setValue(v.x, v.y, v.z); + body.setGravity(vec1); + } + + public function applyForce(force:Vec4, loc:Vec4 = null) { + activate(); + vec1.setValue(force.x, force.y, force.z); + if (loc == null) { + body.applyCentralForce(vec1); + } + else { + vec2.setValue(loc.x, loc.y, loc.z); + body.applyForce(vec1, vec2); + } + } + + public function applyImpulse(impulse:Vec4, loc:Vec4 = null) { + activate(); + vec1.setValue(impulse.x, impulse.y, impulse.z); + if (loc == null) { + body.applyCentralImpulse(vec1); + } + else { + vec2.setValue(loc.x, loc.y, loc.z); + body.applyImpulse(vec1, vec2); + } + } + + public function applyTorque(torque:Vec4) { + activate(); + vec1.setValue(torque.x, torque.y, torque.z); + body.applyTorque(vec1); + } + + public function applyTorqueImpulse(torque:Vec4) { + activate(); + vec1.setValue(torque.x, torque.y, torque.z); + body.applyTorqueImpulse(vec1); + } + + public function setLinearFactor(x:Float, y:Float, z:Float) { + vec1.setValue(x, y, z); + body.setLinearFactor(vec1); + } + + public function setAngularFactor(x:Float, y:Float, z:Float) { + vec1.setValue(x, y, z); + body.setAngularFactor(vec1); + } + + public function getLinearVelocity():Vec4 { + var v = body.getLinearVelocity(); + return new Vec4(v.x(), v.y(), v.z()); + } + + public function setLinearVelocity(x:Float, y:Float, z:Float) { + vec1.setValue(x, y, z); + body.setLinearVelocity(vec1); + } + + public function getAngularVelocity():Vec4 { + var v = body.getAngularVelocity(); + return new Vec4(v.x(), v.y(), v.z()); + } + + public function setAngularVelocity(x:Float, y:Float, z:Float) { + vec1.setValue(x, y, z); + body.setAngularVelocity(vec1); + } + + public function setFriction(f:Float) { + body.setFriction(f); + this.friction = f; + } + + function setScale(v:Vec4) { + currentScaleX = v.x; + currentScaleY = v.y; + currentScaleZ = v.z; + vec1.setX(v.x / bodyScaleX); + vec1.setY(v.y / bodyScaleY); + vec1.setZ(v.z / bodyScaleZ); + btshape.setLocalScaling(vec1); + var worldDyn:Bt.DynamicsWorld = PhysicsWorld.active.world; + var worldCol:Bt.CollisionWorld = worldDyn; + worldCol.updateSingleAabb(body); + } + + public function syncTransform() { + var t = object.transform; + t.buildMatrix(); + vec1.setValue(t.worldx(), t.worldy(), t.worldz()); + trans1.setOrigin(vec1); + quat.fromMat(t.world); + quat1.setValue(quat.x, quat.y, quat.z, quat.w); + trans1.setRotation(quat1); + body.setWorldTransform(trans1); + if (currentScaleX != t.scale.x || currentScaleY != t.scale.y || currentScaleZ != t.scale.z) setScale(t.scale); + activate(); + } + + function setCcd(sphereRadius:Float, motionThreshold = 1e-7) { + body.setCcdSweptSphereRadius(sphereRadius); + body.setCcdMotionThreshold(motionThreshold); + } + + function fillConvexHull(scale:Vec4, margin:kha.FastFloat):Bt.ConvexHullShape { + // Check whether shape already exists + var data = cast(object, MeshObject).data; + var shape = convexHullCache.get(data); + if (shape != null) { + usersCache.set(data, usersCache.get(data) + 1); + return shape; + } + + shape = new Bt.ConvexHullShape(); + convexHullCache.set(data, shape); + usersCache.set(data, 1); + + var positions = data.geom.positions; + + var sx:kha.FastFloat = scale.x * (1.0 - margin) * (1 / 32767); + var sy:kha.FastFloat = scale.y * (1.0 - margin) * (1 / 32767); + var sz:kha.FastFloat = scale.z * (1.0 - margin) * (1 / 32767); + + if (data.raw.scale_pos != null) { + sx *= data.raw.scale_pos; + sy *= data.raw.scale_pos; + sz *= data.raw.scale_pos; + } + + for (i in 0...Std.int(positions.length / 4)) { + vec1.setX(positions[i * 4 ] * sx); + vec1.setY(positions[i * 4 + 1] * sy); + vec1.setZ(positions[i * 4 + 2] * sz); + shape.addPoint(vec1, true); + } + return shape; + } + + function fillTriangleMesh(scale:Vec4):Bt.TriangleMesh { + // Check whether shape already exists + var data = cast(object, MeshObject).data; + var triangleMesh = triangleMeshCache.get(data); + if (triangleMesh != null) { + usersCache.set(data, usersCache.get(data) + 1); + return triangleMesh; + } + + triangleMesh = new Bt.TriangleMesh(true, true); + triangleMeshCache.set(data, triangleMesh); + usersCache.set(data, 1); + + var positions = data.geom.positions; + var indices = data.geom.indices; + + var sx:kha.FastFloat = scale.x * (1 / 32767); + var sy:kha.FastFloat = scale.y * (1 / 32767); + var sz:kha.FastFloat = scale.z * (1 / 32767); + + if (data.raw.scale_pos != null) { + sx *= data.raw.scale_pos; + sy *= data.raw.scale_pos; + sz *= data.raw.scale_pos; + } + + for (ar in indices) { + for (i in 0...Std.int(ar.length / 3)) { + vec1.setX(positions[ar[i * 3 ] * 4 ] * sx); + vec1.setY(positions[ar[i * 3 ] * 4 + 1] * sy); + vec1.setZ(positions[ar[i * 3 ] * 4 + 2] * sz); + vec2.setX(positions[ar[i * 3 + 1] * 4 ] * sx); + vec2.setY(positions[ar[i * 3 + 1] * 4 + 1] * sy); + vec2.setZ(positions[ar[i * 3 + 1] * 4 + 2] * sz); + vec3.setX(positions[ar[i * 3 + 2] * 4 ] * sx); + vec3.setY(positions[ar[i * 3 + 2] * 4 + 1] * sy); + vec3.setZ(positions[ar[i * 3 + 2] * 4 + 2] * sz); + triangleMesh.addTriangle(vec1, vec2, vec3); + } + } + return triangleMesh; + } + + public function delete() { + Bt.Ammo.destroy(motionState); + Bt.Ammo.destroy(body); + + // Delete shape if no other user is found + if (shape == Shape.ConvexHull || shape == Shape.Mesh) { + var data = cast(object, MeshObject).data; + var i = usersCache.get(data) - 1; + usersCache.set(data, i); + if (i <= 0) { + Bt.Ammo.destroy(btshape); + shape == Shape.ConvexHull ? + convexHullCache.remove(data) : + triangleMeshCache.remove(data); + } + } + else Bt.Ammo.destroy(btshape); + } +} + +@: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; +} + +#end diff --git a/Sources/arm/plugin/PhysicsWorld.hx b/Sources/arm/plugin/PhysicsWorld.hx new file mode 100644 index 00000000..62746ad0 --- /dev/null +++ b/Sources/arm/plugin/PhysicsWorld.hx @@ -0,0 +1,201 @@ +package arm.plugin; + +#if arm_physics + +import iron.system.Time; +import iron.math.Vec4; +import iron.math.RayCaster; + +@:access(arm.plugin.PhysicsBody) +class PhysicsWorld extends iron.Trait { + + public static var active:PhysicsWorld = null; + static var vec1:Bt.Vector3 = null; + static var vec2:Bt.Vector3 = null; + static var v1 = new iron.math.Vec4(); + static var v2 = new iron.math.Vec4(); + + var world:Bt.DiscreteDynamicsWorld; + var dispatcher:Bt.CollisionDispatcher; + var contacts:Array = []; + var bodyMap = new Map(); + var timeScale = 1.0; + var timeStep = 1 / 60; + var maxSteps = 1; + + public function new() { + super(); + + active = this; + vec1 = new Bt.Vector3(0, 0, 0); + vec2 = new Bt.Vector3(0, 0, 0); + init(); + + // Ensure physics are updated first in the lateUpdate list + _lateUpdate = [lateUpdate]; + @:privateAccess iron.App.traitLateUpdates.insert(0, lateUpdate); + } + + public function reset() { + for (body in bodyMap) removeBody(body); + } + + function init() { + var broadphase = new Bt.DbvtBroadphase(); + var collisionConfiguration = new Bt.DefaultCollisionConfiguration(); + dispatcher = new Bt.CollisionDispatcher(collisionConfiguration); + var solver = new Bt.SequentialImpulseConstraintSolver(); + world = new Bt.DiscreteDynamicsWorld(dispatcher, broadphase, solver, collisionConfiguration); + setGravity(new Vec4(0, 0, -9.81)); + } + + public function setGravity(v:Vec4) { + vec1.setValue(v.x, v.y, v.z); + world.setGravity(vec1); + } + + public function addBody(pb:PhysicsBody) { + world.addRigidBodyToGroup(pb.body, pb.group, pb.mask); + bodyMap.set(pb.id, pb); + } + + public function removeBody(pb:PhysicsBody) { + if (pb.destroyed) return; + pb.destroyed = true; + if (world != null) world.removeRigidBody(pb.body); + bodyMap.remove(pb.id); + pb.delete(); + } + + public function getContacts(pb:PhysicsBody):Array { + if (contacts.length == 0) return null; + var res:Array = []; + for (i in 0...contacts.length) { + var c = contacts[i]; + var pb:PhysicsBody = null; + if (c.a == untyped pb.body.userIndex) pb = bodyMap.get(c.b); + else if (c.b == untyped pb.body.userIndex) pb = bodyMap.get(c.a); + if (pb != null && res.indexOf(pb) == -1) res.push(pb); + } + return res; + } + + public function getContactPairs(pb:PhysicsBody):Array { + if (contacts.length == 0) return null; + var res:Array = []; + for (i in 0...contacts.length) { + var c = contacts[i]; + if (c.a == untyped pb.body.userIndex) res.push(c); + else if (c.b == untyped pb.body.userIndex) res.push(c); + } + return res; + } + + function lateUpdate() { + var t = Time.delta * timeScale; + if (t == 0.0) return; // Simulation paused + + world.stepSimulation(timeStep, maxSteps, t); + updateContacts(); + for (body in bodyMap) @:privateAccess body.physicsUpdate(); + } + + function updateContacts() { + contacts = []; + var disp:Bt.Dispatcher = dispatcher; + var numManifolds = disp.getNumManifolds(); + + for (i in 0...numManifolds) { + var contactManifold = disp.getManifoldByIndexInternal(i); + var body0 = untyped Bt.Ammo.btRigidBody.prototype.upcast(contactManifold.getBody0()); + var body1 = untyped Bt.Ammo.btRigidBody.prototype.upcast(contactManifold.getBody1()); + + var numContacts = contactManifold.getNumContacts(); + var pt:Bt.ManifoldPoint = null; + var posA:Bt.Vector3 = null; + var posB:Bt.Vector3 = null; + var nor:Bt.Vector3 = null; + for (j in 0...numContacts) { + pt = contactManifold.getContactPoint(j); + posA = pt.get_m_positionWorldOnA(); + posB = pt.get_m_positionWorldOnB(); + nor = pt.get_m_normalWorldOnB(); + var cp:TPair = { + a: untyped body0.userIndex, + b: untyped body1.userIndex, + posA: new Vec4(posA.x(), posA.y(), posA.z()), + posB: new Vec4(posB.x(), posB.y(), posB.z()), + normOnB: new Vec4(nor.x(), nor.y(), nor.z()), + impulse: pt.getAppliedImpulse(), + distance: pt.getDistance() + }; + contacts.push(cp); + } + } + } + + public function pickClosest(inputX:Float, inputY:Float):PhysicsBody { + var camera = iron.Scene.active.camera; + var start = new Vec4(); + var end = new Vec4(); + RayCaster.getDirection(start, end, inputX, inputY, camera); + var hit = rayCast(camera.transform.world.getLoc(), end); + var body = (hit != null) ? hit.body : null; + return body; + } + + public function rayCast(from:Vec4, to:Vec4, group:Int = 0x00000001, mask = 0xffffffff):THit { + var rayFrom = vec1; + var rayTo = vec2; + rayFrom.setValue(from.x, from.y, from.z); + rayTo.setValue(to.x, to.y, to.z); + + var rayCallback = new Bt.ClosestRayResultCallback(rayFrom, rayTo); + + rayCallback.set_m_collisionFilterGroup(group); + rayCallback.set_m_collisionFilterMask(mask); + + var worldDyn:Bt.DynamicsWorld = world; + var worldCol:Bt.CollisionWorld = worldDyn; + worldCol.rayTest(rayFrom, rayTo, rayCallback); + var pb:PhysicsBody = null; + var hitInfo:THit = null; + + var rc:Bt.RayResultCallback = rayCallback; + if (rc.hasHit()) { + var co = rayCallback.get_m_collisionObject(); + var body = untyped Bt.Ammo.btRigidBody.prototype.upcast(co); + var hit = rayCallback.get_m_hitPointWorld(); + v1.set(hit.x(), hit.y(), hit.z()); + var norm = rayCallback.get_m_hitNormalWorld(); + v2.set(norm.x(), norm.y(), norm.z()); + pb = bodyMap.get(untyped body.userIndex); + hitInfo = { + body: pb, + pos: v1, + normal: v2 + }; + } + + Bt.Ammo.destroy(rayCallback); + return hitInfo; + } +} + +typedef THit = { + public var body:PhysicsBody; + public var pos:Vec4; + public var normal:Vec4; +} + +typedef TPair = { + public var a:Int; + public var b:Int; + public var posA:Vec4; + public var posB:Vec4; + public var normOnB:Vec4; + public var impulse:Float; + public var distance:Float; +} + +#end diff --git a/Sources/arm/ui/TabProperties.hx b/Sources/arm/ui/TabProperties.hx index 3a07501d..f3792846 100644 --- a/Sources/arm/ui/TabProperties.hx +++ b/Sources/arm/ui/TabProperties.hx @@ -65,8 +65,8 @@ class TabProperties { Context.object.transform.rot.fromEuler(rot.x, rot.y, rot.z); Context.object.transform.buildMatrix(); #if arm_physics - var rb = Context.object.getTrait(armory.trait.physics.RigidBody); - if (rb != null) rb.syncTransform(); + var pb = Context.object.getTrait(arm.plugin.PhysicsBody); + if (pb != null) pb.syncTransform(); #end } @@ -90,32 +90,39 @@ class TabProperties { Context.object.transform.dirty = true; - if (Context.object.name == "Scene") { - var p = Scene.active.world.probe; - var envHandle = Id.handle({value: p.raw.strength}); - p.raw.strength = ui.slider(envHandle, "Strength", 0.0, 5.0, true); - if (envHandle.changed) { - Context.ddirty = 2; - } - } - else if (Std.is(Context.object, LightObject)) { - var light = cast(Context.object, LightObject); - var lhandle = Id.handle(); - lhandle.value = light.data.raw.strength / 1333; - lhandle.value = Std.int(lhandle.value * 100) / 100; - light.data.raw.strength = ui.slider(lhandle, "Strength", 0.0, 4.0, true) * 1333; - if (lhandle.changed) { - Context.ddirty = 2; - } - } - else if (Std.is(Context.object, CameraObject)) { - var scene = Scene.active; - var cam = scene.cameras[0]; - var fovHandle = Id.handle({value: Std.int(cam.data.raw.fov * 100) / 100}); - cam.data.raw.fov = ui.slider(fovHandle, "FoV", 0.3, 2.0, true); - if (fovHandle.changed) { - cam.buildProjection(); - Context.ddirty = 2; + // if (Context.object.name == "Scene") { + // var p = Scene.active.world.probe; + // var envHandle = Id.handle({value: p.raw.strength}); + // p.raw.strength = ui.slider(envHandle, "Strength", 0.0, 5.0, true); + // if (envHandle.changed) { + // Context.ddirty = 2; + // } + // } + // else if (Std.is(Context.object, LightObject)) { + // var light = cast(Context.object, LightObject); + // var lhandle = Id.handle(); + // lhandle.value = light.data.raw.strength / 1333; + // lhandle.value = Std.int(lhandle.value * 100) / 100; + // light.data.raw.strength = ui.slider(lhandle, "Strength", 0.0, 4.0, true) * 1333; + // if (lhandle.changed) { + // Context.ddirty = 2; + // } + // } + // else if (Std.is(Context.object, CameraObject)) { + // var scene = Scene.active; + // var cam = scene.cameras[0]; + // var fovHandle = Id.handle({value: Std.int(cam.data.raw.fov * 100) / 100}); + // cam.data.raw.fov = ui.slider(fovHandle, "FoV", 0.3, 2.0, true); + // if (fovHandle.changed) { + // cam.buildProjection(); + // Context.ddirty = 2; + // } + // } + + if (Context.object.traits.length > 0) { + ui.text("Traits:"); + for (t in Context.object.traits) { + ui.text(Type.getClassName(Type.getClass(t))); } } } diff --git a/khafile.js b/khafile.js index 7219e063..17cc1f5b 100644 --- a/khafile.js +++ b/khafile.js @@ -109,6 +109,7 @@ else { // player, creator project.addDefine('arm_soundcompress'); project.addDefine('arm_skin'); project.addDefine('arm_world'); + project.addDefine('arm_physics'); } resolve(project);