PhysicsBody.hx 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. package arm.plugin;
  2. #if arm_physics
  3. import iron.math.Vec4;
  4. import iron.math.Quat;
  5. import iron.object.Transform;
  6. import iron.object.MeshObject;
  7. import iron.data.MeshData;
  8. @:access(arm.plugin.PhysicsWorld)
  9. class PhysicsBody extends iron.Trait {
  10. @:keep
  11. public var props = ["mass"];
  12. public var mass = 1.0;
  13. public var friction = 0.5;
  14. public var restitution = 0.0;
  15. public var collisionMargin = 0.0;
  16. public var linearDamping = 0.04;
  17. public var angularDamping = 0.1;
  18. public var linearFactors = [1.0, 1.0, 1.0];
  19. public var angularFactors = [1.0, 1.0, 1.0];
  20. public var linearThreshold = 0.0;
  21. public var angularThreshold = 0.0;
  22. public var ccd = false; // Continuous collision detection
  23. public var trigger = false;
  24. public var group = 1;
  25. public var mask = 1;
  26. var shape = ShapeBox;
  27. var destroyed = false;
  28. var bodyScaleX: Float; // Transform scale at creation time
  29. var bodyScaleY: Float;
  30. var bodyScaleZ: Float;
  31. var currentScaleX: Float;
  32. var currentScaleY: Float;
  33. var currentScaleZ: Float;
  34. var body: Bt.RigidBody = null;
  35. var motionState: Bt.MotionState;
  36. var btshape: Bt.CollisionShape;
  37. var ready = false;
  38. public var id = 0;
  39. public var heightData: haxe.io.Bytes = null;
  40. static var nextId = 0;
  41. static var ammoArray: Int = -1;
  42. static var gimpactRegistered = false;
  43. static var first = true;
  44. static var vec1: Bt.Vector3;
  45. static var vec2: Bt.Vector3;
  46. static var vec3: Bt.Vector3;
  47. static var quat1: Bt.Quaternion;
  48. static var trans1: Bt.Transform;
  49. static var trans2: Bt.Transform;
  50. static var quat = new Quat();
  51. static var convexHullCache = new Map<MeshData, Bt.ConvexHullShape>();
  52. static var triangleMeshCache = new Map<MeshData, Bt.TriangleMesh>();
  53. static var usersCache = new Map<MeshData, Int>();
  54. public function new() {
  55. super();
  56. if (first) {
  57. first = false;
  58. vec1 = new Bt.Vector3(0, 0, 0);
  59. vec2 = new Bt.Vector3(0, 0, 0);
  60. vec3 = new Bt.Vector3(0, 0, 0);
  61. quat1 = new Bt.Quaternion(0, 0, 0, 0);
  62. trans1 = new Bt.Transform();
  63. trans2 = new Bt.Transform();
  64. }
  65. notifyOnAdd(init);
  66. }
  67. inline function withMargin(f: Float) { return f - f * collisionMargin; }
  68. function init() {
  69. if (ready) return;
  70. ready = true;
  71. if (!Std.is(object, MeshObject)) return; // No mesh data
  72. var transform = object.transform;
  73. var physics = PhysicsWorld.active;
  74. if (shape == ShapeBox) {
  75. vec1.setX(withMargin(transform.dim.x / 2));
  76. vec1.setY(withMargin(transform.dim.y / 2));
  77. vec1.setZ(withMargin(transform.dim.z / 2));
  78. btshape = new Bt.BoxShape(vec1);
  79. }
  80. else if (shape == ShapeSphere) {
  81. btshape = new Bt.SphereShape(withMargin(transform.dim.x / 2));
  82. }
  83. else if (shape == ShapeConvexHull) {
  84. var shapeConvex = fillConvexHull(transform.scale, collisionMargin);
  85. btshape = shapeConvex;
  86. }
  87. else if (shape == ShapeCone) {
  88. var coneZ = new Bt.ConeShapeZ(
  89. withMargin(transform.dim.x / 2), // Radius
  90. withMargin(transform.dim.z)); // Height
  91. var cone: Bt.ConeShape = coneZ;
  92. btshape = cone;
  93. }
  94. else if (shape == ShapeCylinder) {
  95. vec1.setX(withMargin(transform.dim.x / 2));
  96. vec1.setY(withMargin(transform.dim.y / 2));
  97. vec1.setZ(withMargin(transform.dim.z / 2));
  98. var cylZ = new Bt.CylinderShapeZ(vec1);
  99. var cyl: Bt.CylinderShape = cylZ;
  100. btshape = cyl;
  101. }
  102. else if (shape == ShapeCapsule) {
  103. var r = transform.dim.x / 2;
  104. var capsZ = new Bt.CapsuleShapeZ(
  105. withMargin(r), // Radius
  106. withMargin(transform.dim.z - r * 2)); // Distance between 2 sphere centers
  107. var caps: Bt.CapsuleShape = capsZ;
  108. btshape = caps;
  109. }
  110. else if (shape == ShapeMesh) {
  111. var meshInterface = fillTriangleMesh(transform.scale);
  112. if (mass > 0) {
  113. var shapeGImpact = new Bt.GImpactMeshShape(meshInterface);
  114. shapeGImpact.updateBound();
  115. var shapeConcave: Bt.ConcaveShape = shapeGImpact;
  116. btshape = shapeConcave;
  117. if (!gimpactRegistered) {
  118. gimpactRegistered = true;
  119. new Bt.GImpactCollisionAlgorithm().registerAlgorithm(physics.dispatcher);
  120. }
  121. }
  122. else {
  123. var shapeBvh = new Bt.BvhTriangleMeshShape(meshInterface, true, true);
  124. var shapeTri: Bt.TriangleMeshShape = shapeBvh;
  125. var shapeConcave: Bt.ConcaveShape = shapeTri;
  126. btshape = shapeConcave;
  127. }
  128. }
  129. else if (shape == ShapeTerrain) {
  130. var length = heightData.length;
  131. if (ammoArray == -1) {
  132. ammoArray = Bt.Ammo._malloc(length);
  133. }
  134. // From texture bytes
  135. for (i in 0...length) {
  136. Bt.Ammo.HEAPU8[ammoArray + i] = heightData.get(i);
  137. }
  138. var slice = Std.int(Math.sqrt(length)); // Assuming square terrain data
  139. var axis = 2; // z
  140. var dataType = 5; // u8
  141. btshape = new Bt.HeightfieldTerrainShape(slice, slice, ammoArray, 1 / 255, 0, 1, axis, dataType, false);
  142. vec1.setX(transform.dim.x / slice);
  143. vec1.setY(transform.dim.y / slice);
  144. vec1.setZ(transform.dim.z);
  145. btshape.setLocalScaling(vec1);
  146. }
  147. trans1.setIdentity();
  148. vec1.setX(transform.worldx());
  149. vec1.setY(transform.worldy());
  150. vec1.setZ(transform.worldz());
  151. trans1.setOrigin(vec1);
  152. quat.fromMat(transform.world);
  153. quat1.setValue(quat.x, quat.y, quat.z, quat.w);
  154. trans1.setRotation(quat1);
  155. trans2.setIdentity();
  156. motionState = new Bt.DefaultMotionState(trans1, trans2); // Transform, center of mass offset
  157. vec1.setX(0);
  158. vec1.setY(0);
  159. vec1.setZ(0);
  160. var inertia = vec1;
  161. if (mass > 0) btshape.calculateLocalInertia(mass, inertia);
  162. var bodyCI = new Bt.RigidBodyConstructionInfo(mass, motionState, btshape, inertia);
  163. body = new Bt.RigidBody(bodyCI);
  164. body.setFriction(friction);
  165. if (shape == ShapeSphere || shape == ShapeCylinder || shape == ShapeCone || shape == ShapeCapsule) {
  166. angularDamping += friction;
  167. }
  168. body.setRestitution(restitution);
  169. // body.setSleepingThresholds(linearThreshold, angularThreshold);
  170. // body.setDeactivationTime(deactivationTime);
  171. body.setDamping(linearDamping, angularDamping);
  172. setLinearFactor(linearFactors[0], linearFactors[1], linearFactors[2]);
  173. setAngularFactor(angularFactors[0], angularFactors[1], angularFactors[2]);
  174. if (trigger) body.setCollisionFlags(body.getCollisionFlags() | Bt.CollisionObject.CF_NO_CONTACT_RESPONSE);
  175. if (mass == 0.0) body.setCollisionFlags(body.getCollisionFlags() | Bt.CollisionObject.CF_STATIC_OBJECT);
  176. if (ccd) setCcd(transform.radius);
  177. bodyScaleX = currentScaleX = transform.scale.x;
  178. bodyScaleY = currentScaleY = transform.scale.y;
  179. bodyScaleZ = currentScaleZ = transform.scale.z;
  180. id = nextId++;
  181. untyped body.userIndex = id;
  182. physics.addBody(this);
  183. notifyOnRemove(removeFromWorld);
  184. Bt.Ammo.destroy(bodyCI);
  185. }
  186. function physicsUpdate() {
  187. if (!ready) return;
  188. var trans = body.getWorldTransform();
  189. var p = trans.getOrigin();
  190. var q = trans.getRotation();
  191. var qw: Bt.QuadWord = q;
  192. var transform = object.transform;
  193. transform.loc.set(p.x(), p.y(), p.z());
  194. transform.rot.set(qw.x(), qw.y(), qw.z(), qw.w());
  195. if (object.parent != null) {
  196. var ptransform = object.parent.transform;
  197. transform.loc.x -= ptransform.worldx();
  198. transform.loc.y -= ptransform.worldy();
  199. transform.loc.z -= ptransform.worldz();
  200. }
  201. transform.buildMatrix();
  202. }
  203. public function removeFromWorld() {
  204. PhysicsWorld.active.removeBody(this);
  205. }
  206. public function activate() {
  207. body.activate(false);
  208. }
  209. public function setGravity(v: Vec4) {
  210. vec1.setValue(v.x, v.y, v.z);
  211. body.setGravity(vec1);
  212. }
  213. public function applyForce(force: Vec4, loc: Vec4 = null) {
  214. activate();
  215. vec1.setValue(force.x, force.y, force.z);
  216. if (loc == null) {
  217. body.applyCentralForce(vec1);
  218. }
  219. else {
  220. vec2.setValue(loc.x, loc.y, loc.z);
  221. body.applyForce(vec1, vec2);
  222. }
  223. }
  224. public function applyImpulse(impulse: Vec4, loc: Vec4 = null) {
  225. activate();
  226. vec1.setValue(impulse.x, impulse.y, impulse.z);
  227. if (loc == null) {
  228. body.applyCentralImpulse(vec1);
  229. }
  230. else {
  231. vec2.setValue(loc.x, loc.y, loc.z);
  232. body.applyImpulse(vec1, vec2);
  233. }
  234. }
  235. public function applyTorque(torque: Vec4) {
  236. activate();
  237. vec1.setValue(torque.x, torque.y, torque.z);
  238. body.applyTorque(vec1);
  239. }
  240. public function applyTorqueImpulse(torque: Vec4) {
  241. activate();
  242. vec1.setValue(torque.x, torque.y, torque.z);
  243. body.applyTorqueImpulse(vec1);
  244. }
  245. public function setLinearFactor(x: Float, y: Float, z: Float) {
  246. vec1.setValue(x, y, z);
  247. body.setLinearFactor(vec1);
  248. }
  249. public function setAngularFactor(x: Float, y: Float, z: Float) {
  250. vec1.setValue(x, y, z);
  251. body.setAngularFactor(vec1);
  252. }
  253. public function getLinearVelocity(): Vec4 {
  254. var v = body.getLinearVelocity();
  255. return new Vec4(v.x(), v.y(), v.z());
  256. }
  257. public function setLinearVelocity(x: Float, y: Float, z: Float) {
  258. vec1.setValue(x, y, z);
  259. body.setLinearVelocity(vec1);
  260. }
  261. public function getAngularVelocity(): Vec4 {
  262. var v = body.getAngularVelocity();
  263. return new Vec4(v.x(), v.y(), v.z());
  264. }
  265. public function setAngularVelocity(x: Float, y: Float, z: Float) {
  266. vec1.setValue(x, y, z);
  267. body.setAngularVelocity(vec1);
  268. }
  269. public function setFriction(f: Float) {
  270. body.setFriction(f);
  271. this.friction = f;
  272. }
  273. function setScale(v: Vec4) {
  274. currentScaleX = v.x;
  275. currentScaleY = v.y;
  276. currentScaleZ = v.z;
  277. vec1.setX(v.x / bodyScaleX);
  278. vec1.setY(v.y / bodyScaleY);
  279. vec1.setZ(v.z / bodyScaleZ);
  280. btshape.setLocalScaling(vec1);
  281. var worldDyn: Bt.DynamicsWorld = PhysicsWorld.active.world;
  282. var worldCol: Bt.CollisionWorld = worldDyn;
  283. worldCol.updateSingleAabb(body);
  284. }
  285. public function syncTransform() {
  286. var t = object.transform;
  287. t.buildMatrix();
  288. vec1.setValue(t.worldx(), t.worldy(), t.worldz());
  289. trans1.setOrigin(vec1);
  290. quat.fromMat(t.world);
  291. quat1.setValue(quat.x, quat.y, quat.z, quat.w);
  292. trans1.setRotation(quat1);
  293. body.setWorldTransform(trans1);
  294. if (currentScaleX != t.scale.x || currentScaleY != t.scale.y || currentScaleZ != t.scale.z) setScale(t.scale);
  295. activate();
  296. }
  297. function setCcd(sphereRadius: Float, motionThreshold = 1e-7) {
  298. body.setCcdSweptSphereRadius(sphereRadius);
  299. body.setCcdMotionThreshold(motionThreshold);
  300. }
  301. function fillConvexHull(scale: Vec4, margin: kha.FastFloat): Bt.ConvexHullShape {
  302. // Check whether shape already exists
  303. var data = cast(object, MeshObject).data;
  304. var shape = convexHullCache.get(data);
  305. if (shape != null) {
  306. usersCache.set(data, usersCache.get(data) + 1);
  307. return shape;
  308. }
  309. shape = new Bt.ConvexHullShape();
  310. convexHullCache.set(data, shape);
  311. usersCache.set(data, 1);
  312. var positions = data.geom.positions;
  313. var sx: kha.FastFloat = scale.x * (1.0 - margin) * (1 / 32767);
  314. var sy: kha.FastFloat = scale.y * (1.0 - margin) * (1 / 32767);
  315. var sz: kha.FastFloat = scale.z * (1.0 - margin) * (1 / 32767);
  316. if (data.raw.scale_pos != null) {
  317. sx *= data.raw.scale_pos;
  318. sy *= data.raw.scale_pos;
  319. sz *= data.raw.scale_pos;
  320. }
  321. for (i in 0...Std.int(positions.length / 4)) {
  322. vec1.setX(positions[i * 4 ] * sx);
  323. vec1.setY(positions[i * 4 + 1] * sy);
  324. vec1.setZ(positions[i * 4 + 2] * sz);
  325. shape.addPoint(vec1, true);
  326. }
  327. return shape;
  328. }
  329. function fillTriangleMesh(scale: Vec4): Bt.TriangleMesh {
  330. // Check whether shape already exists
  331. var data = cast(object, MeshObject).data;
  332. var triangleMesh = triangleMeshCache.get(data);
  333. if (triangleMesh != null) {
  334. usersCache.set(data, usersCache.get(data) + 1);
  335. return triangleMesh;
  336. }
  337. triangleMesh = new Bt.TriangleMesh(true, true);
  338. triangleMeshCache.set(data, triangleMesh);
  339. usersCache.set(data, 1);
  340. var positions = data.geom.positions;
  341. var indices = data.geom.indices;
  342. var sx: kha.FastFloat = scale.x * (1 / 32767);
  343. var sy: kha.FastFloat = scale.y * (1 / 32767);
  344. var sz: kha.FastFloat = scale.z * (1 / 32767);
  345. if (data.raw.scale_pos != null) {
  346. sx *= data.raw.scale_pos;
  347. sy *= data.raw.scale_pos;
  348. sz *= data.raw.scale_pos;
  349. }
  350. for (ar in indices) {
  351. for (i in 0...Std.int(ar.length / 3)) {
  352. vec1.setX(positions[ar[i * 3 ] * 4 ] * sx);
  353. vec1.setY(positions[ar[i * 3 ] * 4 + 1] * sy);
  354. vec1.setZ(positions[ar[i * 3 ] * 4 + 2] * sz);
  355. vec2.setX(positions[ar[i * 3 + 1] * 4 ] * sx);
  356. vec2.setY(positions[ar[i * 3 + 1] * 4 + 1] * sy);
  357. vec2.setZ(positions[ar[i * 3 + 1] * 4 + 2] * sz);
  358. vec3.setX(positions[ar[i * 3 + 2] * 4 ] * sx);
  359. vec3.setY(positions[ar[i * 3 + 2] * 4 + 1] * sy);
  360. vec3.setZ(positions[ar[i * 3 + 2] * 4 + 2] * sz);
  361. triangleMesh.addTriangle(vec1, vec2, vec3);
  362. }
  363. }
  364. return triangleMesh;
  365. }
  366. public function delete() {
  367. Bt.Ammo.destroy(motionState);
  368. Bt.Ammo.destroy(body);
  369. // Delete shape if no other user is found
  370. if (shape == ShapeConvexHull || shape == ShapeMesh) {
  371. var data = cast(object, MeshObject).data;
  372. var i = usersCache.get(data) - 1;
  373. usersCache.set(data, i);
  374. if (i <= 0) {
  375. Bt.Ammo.destroy(btshape);
  376. shape == ShapeConvexHull ?
  377. convexHullCache.remove(data) :
  378. triangleMeshCache.remove(data);
  379. }
  380. }
  381. else Bt.Ammo.destroy(btshape);
  382. }
  383. }
  384. @:enum abstract ShapeType(Int) from Int to Int {
  385. var ShapeBox = 0;
  386. var ShapeSphere = 1;
  387. var ShapeConvexHull = 2;
  388. var ShapeMesh = 3;
  389. var ShapeCone = 4;
  390. var ShapeCylinder = 5;
  391. var ShapeCapsule = 6;
  392. var ShapeTerrain = 7;
  393. }
  394. #end