PhysicsBody.hx 13 KB

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