tinyphysicsengine.h 29 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228
  1. #ifndef _TINYPHYSICSENGINE_H
  2. #define _TINYPHYSICSENGINE_H
  3. /**
  4. Orientations/rotations are in extrinsic Euler angles in the ZXY order (by Z,
  5. then by X, then by Y).
  6. Where it matters (e.g. rotations about axes) we consider a left-handed coord.
  7. system (x right, y up, z forward).
  8. */
  9. #include <stdint.h>
  10. typedef int32_t TPE_Unit;
  11. #define TPE_FRACTIONS_PER_UNIT 512
  12. #define TPE_JOINT_SIZE_MULTIPLIER 32
  13. #define TPE_JOINT_SIZE(joint) ((joint).sizeDivided * TPE_JOINT_SIZE_MULTIPLIER)
  14. #ifndef TPE_APPROXIMATE_LENGTH
  15. #define TPE_APPROXIMATE_LENGTH 0 /**< whether or not use length/distance
  16. approximation rather than exact
  17. calculation (1 is faster but less
  18. accurate) */
  19. #endif
  20. #if !TPE_APPROXIMATE_LENGTH
  21. #define TPE_DISTANCE TPE_dist
  22. #define TPE_LENGTH TPE_vec3Len
  23. #else
  24. #define TPE_DISTANCE TPE_distApprox
  25. #define TPE_LENGTH TPE_vec3LenApprox
  26. #endif
  27. // TODO: faster and more accurate distance approx function based on regions/LUT
  28. #ifndef TPE_LOG
  29. #define TPE_LOG(s) ;
  30. #endif
  31. #define TPE_PRINTF_VEC3(v) printf("[%d %d %d]",(v).x,(v).y,(v).z);
  32. typedef struct
  33. {
  34. TPE_Unit x;
  35. TPE_Unit y;
  36. TPE_Unit z;
  37. } TPE_Vec3;
  38. typedef struct
  39. {
  40. TPE_Vec3 position;
  41. int16_t velocity[3]; ///< for saving space only uses int16
  42. uint8_t sizeDivided; /**< size (radius, ...), for saving space divided by
  43. TPE_JOINT_SIZE_MULTIPLIER */
  44. } TPE_Joint;
  45. typedef struct
  46. {
  47. uint8_t joint1;
  48. uint16_t joint2;
  49. uint16_t length; ///< connection's preferred length, uint16_t saves space
  50. } TPE_Connection;
  51. #define TPE_BODY_FLAG_DEACTIVATED 1 /**< Not being updated due to low energy,
  52. "sleeping", will be woken by
  53. collisions etc. */
  54. #define TPE_BODY_FLAG_NONROTATING 2 /**< When set, the body won't rotate, will
  55. only move linearly. */
  56. #define TPE_BODY_FLAG_DISABLED 3 /**< Disabled, not taking part in
  57. simulation. */
  58. #define TPE_BODY_FLAG_SOFT 4 /**< Soft connections, effort won't be made
  59. to keep the body's shape. */
  60. typedef TPE_Vec3 (*TPE_ClosestPointFunction)(TPE_Vec3);
  61. typedef struct
  62. {
  63. TPE_Joint *joints;
  64. uint8_t jointCount;
  65. TPE_Connection *connections;
  66. uint8_t connectionCount;
  67. uint16_t jointMass;
  68. uint8_t flags;
  69. uint8_t disableCount;
  70. } TPE_Body;
  71. typedef struct
  72. {
  73. TPE_Body *bodies;
  74. uint16_t bodyCount;
  75. TPE_ClosestPointFunction environmentFunction;
  76. } TPE_World;
  77. void TPE_bodyInit(TPE_Body *body,
  78. TPE_Joint *joints, uint8_t jointCount,
  79. TPE_Connection *connections, uint8_t connectionCount,
  80. TPE_Unit mass);
  81. void TPE_worldInit(TPE_World *world,
  82. TPE_Body *bodies, uint16_t bodyCount,
  83. TPE_ClosestPointFunction environmentFunction);
  84. void TPE_vec3Normalize(TPE_Vec3 *v);
  85. void TPE_getVelocitiesAfterCollision(
  86. TPE_Unit *v1,
  87. TPE_Unit *v2,
  88. TPE_Unit m1,
  89. TPE_Unit m2,
  90. TPE_Unit elasticity);
  91. TPE_Vec3 TPE_vec3(TPE_Unit x, TPE_Unit y, TPE_Unit z);
  92. TPE_Vec3 TPE_vec3Plus(TPE_Vec3 v1, TPE_Vec3 v2);
  93. TPE_Vec3 TPE_vec3Minus(TPE_Vec3 v1, TPE_Vec3 v2);
  94. TPE_Vec3 TPE_vec3Cross(TPE_Vec3 v1, TPE_Vec3 v2);
  95. TPE_Vec3 TPE_vec3Project(TPE_Vec3 v, TPE_Vec3 base);
  96. TPE_Vec3 TPE_vec3Times(TPE_Vec3 v, TPE_Unit units);
  97. TPE_Vec3 TPE_vec3TimesNonNormalized(TPE_Vec3 v, TPE_Unit q);
  98. TPE_Vec3 TPE_vec3Normalized(TPE_Vec3 v);
  99. /* Computes orientation/rotation (see docs for orientation format) from two
  100. vectors (which should be at least a close to being perpensicular and do NOT
  101. need to be normalized). */
  102. TPE_Vec3 TPE_orientationFromVecs(TPE_Vec3 forward, TPE_Vec3 right);
  103. TPE_Unit TPE_vec3Dot(TPE_Vec3 v1, TPE_Vec3 v2);
  104. TPE_Unit TPE_sqrt(TPE_Unit value);
  105. TPE_Unit TPE_vec3Len(TPE_Vec3 v);
  106. TPE_Unit TPE_vec3LenApprox(TPE_Vec3 v);
  107. static inline TPE_Unit TPE_nonZero(TPE_Unit x);
  108. static inline TPE_Unit TPE_dist(TPE_Vec3 p1, TPE_Vec3 p2);
  109. static inline TPE_Unit TPE_distApprox(TPE_Vec3 p1, TPE_Vec3 p2);
  110. TPE_Joint TPE_joint(TPE_Vec3 position, TPE_Unit size);
  111. void TPE_jointsResolveCollision(TPE_Joint *j1, TPE_Joint *j2,
  112. TPE_Unit mass1, TPE_Unit mass2, TPE_Unit elasticity);
  113. void TPE_jointEnvironmentResolveCollision(TPE_Joint *joint, TPE_Unit elasticity,
  114. TPE_Unit friction, TPE_ClosestPointFunction env);
  115. void TPE_bodyEnvironmentResolveCollision(TPE_Body *body, TPE_Unit elasticity,
  116. TPE_Unit friction, TPE_ClosestPointFunction env);
  117. void TPE_bodiesResolveCollision(TPE_Body *b1, TPE_Body *b2);
  118. // body generation functions:
  119. void TPE_makeBox(TPE_Joint joints[8], TPE_Connection connections[16],
  120. TPE_Unit width, TPE_Unit depth, TPE_Unit height, TPE_Unit jointSize);
  121. void TPE_makeCenterBox(TPE_Joint joints[9], TPE_Connection connections[18],
  122. TPE_Unit width, TPE_Unit depth, TPE_Unit height, TPE_Unit jointSize);
  123. void TPE_makeRect(TPE_Joint joints[4], TPE_Connection connections[6],
  124. TPE_Unit width, TPE_Unit depth, TPE_Unit jointSize);
  125. void TPE_makeTriangle(TPE_Joint joints[3], TPE_Connection connections[3],
  126. TPE_Unit sideLength, TPE_Unit jointSize);
  127. void TPE_makeCenterRect(TPE_Joint joints[5], TPE_Connection connections[8],
  128. TPE_Unit width, TPE_Unit depth, TPE_Unit jointSize);
  129. void TPE_make2Line(TPE_Joint joints[2], TPE_Connection connections[1],
  130. TPE_Unit length, TPE_Unit jointSize);
  131. //---------------------------
  132. void TPE_worldStep(TPE_World *world);
  133. TPE_Unit TPE_bodyNetSpeed(const TPE_Body *body);
  134. TPE_Unit TPE_bodyAverageSpeed(const TPE_Body *body);
  135. void TPE_bodyLimitNetSpeed(TPE_Body *body, TPE_Unit speedMin,
  136. TPE_Unit speedMax);
  137. void TPE_bodyMultiplyNetSpeed(TPE_Body *body, TPE_Unit factor);
  138. /** Attempts to shift the joints of a soft body so that the tension of all
  139. strings becomes zero while keeping the joints near their current position. This
  140. function performs one iteration of the equalizing algorithm and doesn't
  141. guarantee a perfect solution, it may help to run multiple iterations (call this
  142. function multiple times). */
  143. void TPE_bodyReshape(TPE_Body *body, TPE_ClosestPointFunction
  144. environmentFunction);
  145. /** Move a soft body by certain offset. */
  146. void TPE_bodyMove(TPE_Body *body, TPE_Vec3 offset);
  147. /** Zero velocities of all soft body joints. */
  148. void TPE_bodyStop(TPE_Body *body);
  149. void TPE_bodyWake(TPE_Body *body);
  150. /** Add velocity to a soft body. */
  151. void TPE_bodyAccelerate(TPE_Body *body, TPE_Vec3 velocity);
  152. /** Add angular velocity to a soft body. The rotation vector specifies the axis
  153. of rotation by its direction and angular velocity by its magnitude (magnitude
  154. of TPE_FRACTIONS_PER_UNIT will add linear velocity of TPE_FRACTIONS_PER_UNIT per
  155. tick to a point in the distance of TPE_FRACTIONS_PER_UNIT from the rotation
  156. axis). */
  157. void TPE_bodySpin(TPE_Body *body, TPE_Vec3 rotation);
  158. /** Instantly rotate soft body, the rotation vector specifies the rotation axis
  159. by its direction and the rotation angle by its magnitude (TPE_FRACTIONS_PER_UNIT
  160. stands for full angle, i.e. 2 * PI x). */
  161. void TPE_bodyRotate(TPE_Body *body, TPE_Vec3 rotation);
  162. /** Compute the center of mass of a soft body. */
  163. TPE_Vec3 TPE_bodyGetCenter(const TPE_Body *body);
  164. /** Compute sine, TPE_FRACTIONS_PER_UNIT as argument corresponds to 2 * PI
  165. radians. Returns a number from -TPE_FRACTIONS_PER_UNIT to
  166. TPE_FRACTIONS_PER_UNIT. */
  167. TPE_Unit TPE_sin(TPE_Unit x);
  168. TPE_Unit TPE_cos(TPE_Unit x);
  169. TPE_Unit TPE_atan(TPE_Unit x);
  170. //------------------------------------------------------------------------------
  171. static inline TPE_Unit TPE_nonZero(TPE_Unit x)
  172. {
  173. return x != 0 ? x : 1;
  174. }
  175. TPE_Joint TPE_joint(TPE_Vec3 position, TPE_Unit size)
  176. {
  177. TPE_Joint result;
  178. result.velocity[0] = 0;
  179. result.velocity[1] = 0;
  180. result.velocity[2] = 0;
  181. result.position = position;
  182. size /= TPE_JOINT_SIZE_MULTIPLIER;
  183. if (size > 0xff)
  184. TPE_LOG("WARNING: joint size too big in TPE_joint");
  185. result.sizeDivided = size;
  186. return result;
  187. }
  188. TPE_Vec3 TPE_vec3(TPE_Unit x, TPE_Unit y, TPE_Unit z)
  189. {
  190. TPE_Vec3 r;
  191. r.x = x;
  192. r.y = y;
  193. r.z = z;
  194. return r;
  195. }
  196. TPE_Unit TPE_sqrt(TPE_Unit value)
  197. {
  198. int8_t sign = 1;
  199. if (value < 0)
  200. {
  201. sign = -1;
  202. value *= -1;
  203. }
  204. uint32_t result = 0;
  205. uint32_t a = value;
  206. uint32_t b = 1u << 30;
  207. while (b > a)
  208. b >>= 2;
  209. while (b != 0)
  210. {
  211. if (a >= result + b)
  212. {
  213. a -= result + b;
  214. result = result + 2 * b;
  215. }
  216. b >>= 2;
  217. result >>= 1;
  218. }
  219. return result * sign;
  220. }
  221. TPE_Unit TPE_vec3Len(TPE_Vec3 v)
  222. {
  223. return TPE_sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
  224. }
  225. TPE_Unit TPE_vec3LenApprox(TPE_Vec3 v)
  226. {
  227. if (v.x < 0)
  228. v.x *= -1;
  229. if (v.y < 0)
  230. v.y *= -1;
  231. if (v.z < 0)
  232. v.z *= -1;
  233. TPE_Unit sum = v.x + v.y + v.z;
  234. v.x = (v.x > v.y) ?
  235. (v.x > v.z ? v.x : v.z) :
  236. (v.y > v.z ? v.y : v.z);
  237. return (v.x + sum) / 2;
  238. }
  239. TPE_Unit TPE_dist(TPE_Vec3 p1, TPE_Vec3 p2)
  240. {
  241. p1 = TPE_vec3Minus(p1,p2);
  242. return TPE_vec3Len(p1);
  243. }
  244. TPE_Unit TPE_distApprox(TPE_Vec3 p1, TPE_Vec3 p2)
  245. {
  246. p1 = TPE_vec3Minus(p1,p2);
  247. return TPE_vec3LenApprox(p1);
  248. }
  249. void TPE_bodyInit(TPE_Body *body,
  250. TPE_Joint *joints, uint8_t jointCount,
  251. TPE_Connection *connections, uint8_t connectionCount,
  252. TPE_Unit mass)
  253. {
  254. body->joints = joints;
  255. body->jointCount = jointCount;
  256. body->connections = connections;
  257. body->connectionCount = connectionCount;
  258. body->disableCount = 0;
  259. body->flags = 0;
  260. body->jointMass = mass / jointCount;
  261. if (body->jointMass == 0)
  262. body->jointMass = 1;
  263. for (uint32_t i = 0; i < connectionCount; ++i)
  264. {
  265. TPE_Unit d = TPE_DISTANCE(
  266. joints[connections[i].joint1].position,
  267. joints[connections[i].joint2].position);
  268. if (d > 0xffff)
  269. TPE_LOG("WARNING: joint distance too long in TPE_bodyInit");
  270. connections[i].length = d != 0 ? d : 1; // prevent later division by zero
  271. }
  272. }
  273. void TPE_worldInit(TPE_World *world,
  274. TPE_Body *bodies, uint16_t bodyCount,
  275. TPE_ClosestPointFunction environmentFunction)
  276. {
  277. world->bodies = bodies;
  278. world->bodyCount = bodyCount;
  279. world->environmentFunction = environmentFunction;
  280. }
  281. #define C(n,a,b) connections[n].joint1 = a; connections[n].joint2 = b;
  282. void TPE_make2Line(TPE_Joint joints[2], TPE_Connection connections[1],
  283. TPE_Unit length, TPE_Unit jointSize)
  284. {
  285. joints[0] = TPE_joint(TPE_vec3(length / 2,0,0),jointSize);
  286. joints[1] = TPE_joint(TPE_vec3(length / -2,0,0),jointSize);
  287. C(0, 0,1)
  288. }
  289. void TPE_makeRect(TPE_Joint joints[4], TPE_Connection connections[6],
  290. TPE_Unit width, TPE_Unit depth, TPE_Unit jointSize)
  291. {
  292. width /= 2;
  293. depth /= 2;
  294. for (uint8_t i = 0; i < 4; ++i)
  295. joints[i] = TPE_joint(
  296. TPE_vec3((i % 2) ? -1 * width : width,
  297. 0,(i / 2) ? - 1 * depth : depth),
  298. jointSize);
  299. C(0, 0,1) C(1, 0,2) C (2, 3,1) C(3, 3,2)
  300. C(4, 0,3) C(5, 1,2)
  301. }
  302. void TPE_makeCenterRect(TPE_Joint joints[5], TPE_Connection connections[8],
  303. TPE_Unit width, TPE_Unit depth, TPE_Unit jointSize)
  304. {
  305. TPE_makeRect(joints,connections,width,depth,jointSize);
  306. joints[4] = TPE_joint(TPE_vec3(0,0,0),jointSize);
  307. C(6, 0,4) C(7, 3,4)
  308. }
  309. void TPE_makeTriangle(TPE_Joint joints[3], TPE_Connection connections[3],
  310. TPE_Unit sideLength, TPE_Unit jointSize)
  311. {
  312. joints[0] = TPE_joint(TPE_vec3(sideLength / 2,0,
  313. TPE_sqrt((sideLength * sideLength) / 2) / 2),
  314. jointSize);
  315. joints[1] = joints[0];
  316. joints[1].position.x *= -1;
  317. joints[2] = TPE_joint(TPE_vec3(0,0,-1 * joints[0].position.z),jointSize);
  318. C(0, 0,1) C(1, 1,2) C(2, 2,0)
  319. }
  320. void TPE_makeBox(TPE_Joint joints[8], TPE_Connection connections[16],
  321. TPE_Unit width, TPE_Unit depth, TPE_Unit height, TPE_Unit jointSize)
  322. {
  323. width /= 2;
  324. depth /= 2;
  325. height /= 2;
  326. for (uint8_t i = 0; i < 8; ++i)
  327. joints[i] = TPE_joint(
  328. TPE_vec3(
  329. (i % 2) ? -1 * width : width,
  330. ((i >> 1) % 2) ? -1 * height : height,
  331. ((i >> 2) % 2) ? -1 * depth : depth),
  332. jointSize);
  333. C(0, 0,1) C(1, 1,3) C(2, 3,2) C(3, 2,0) // top
  334. C(4, 4,5) C(5, 5,7) C(6, 7,6) C(7, 6,4) // bottom
  335. C(8, 0,4) C(9, 1,5) C(10,3,7) C(11,2,6) // middle
  336. C(12,0,7) C(13,1,6) C(14,2,5) C(15,3,4) // diagonal
  337. }
  338. void TPE_makeCenterBox(TPE_Joint joints[9], TPE_Connection connections[18],
  339. TPE_Unit width, TPE_Unit depth, TPE_Unit height, TPE_Unit jointSize)
  340. {
  341. TPE_makeBox(joints,connections,width,depth,height,jointSize);
  342. joints[8] = TPE_joint(TPE_vec3(0,0,0),jointSize);
  343. C(16, 0,8) C(17, 7,8)
  344. }
  345. #undef C
  346. void TPE_worldStep(TPE_World *world)
  347. {
  348. for (uint16_t i = 0; i < world->bodyCount; ++i)
  349. {
  350. TPE_Body *body = world->bodies + i;
  351. if (body->flags & (TPE_BODY_FLAG_DEACTIVATED | TPE_BODY_FLAG_DISABLED))
  352. continue;
  353. TPE_Joint *joint = body->joints, *joint2;
  354. for (uint16_t j = 0; j < body->jointCount; ++j) // apply velocities
  355. {
  356. joint->position.x += joint->velocity[0];
  357. joint->position.y += joint->velocity[1];
  358. joint->position.z += joint->velocity[2];
  359. joint++;
  360. }
  361. TPE_Connection *connection = body->connections;
  362. TPE_bodyEnvironmentResolveCollision(body,256,256,
  363. world->environmentFunction);
  364. TPE_Unit bodyTension = 0;
  365. for (uint16_t j = 0; j < body->connectionCount; ++j) // update velocities
  366. {
  367. joint = &(body->joints[connection->joint1]);
  368. joint2 = &(body->joints[connection->joint2]);
  369. TPE_Vec3 dir = TPE_vec3Minus(joint2->position,joint->position);
  370. TPE_Unit len = TPE_LENGTH(dir);
  371. len = (len * TPE_FRACTIONS_PER_UNIT) /
  372. connection->length - TPE_FRACTIONS_PER_UNIT;
  373. bodyTension += len > 0 ? len : -len;
  374. if ( len > 5 || len < -5 ) //len != 0) // TODO: magic
  375. {
  376. TPE_vec3Normalize(&dir);
  377. dir.x /= 32;
  378. dir.y /= 32;
  379. dir.z /= 32;
  380. if (len < 0)
  381. {
  382. dir.x *= -1;
  383. dir.y *= -1;
  384. dir.z *= -1;
  385. }
  386. joint->velocity[0] += dir.x;
  387. joint->velocity[1] += dir.y;
  388. joint->velocity[2] += dir.z;
  389. joint2->velocity[0] -= dir.x;
  390. joint2->velocity[1] -= dir.y;
  391. joint2->velocity[2] -= dir.z;
  392. /*
  393. dir.x = (dir.x * len) / (2 * TPE_FRACTIONS_PER_UNIT);
  394. dir.y = (dir.y * len) / (2 * TPE_FRACTIONS_PER_UNIT);
  395. dir.z = (dir.z * len) / (2 * TPE_FRACTIONS_PER_UNIT);
  396. joint->velocity[0] += dir.x;
  397. joint->velocity[1] += dir.y;
  398. joint->velocity[2] += dir.z;
  399. joint2->velocity[0] -= dir.x;
  400. joint2->velocity[1] -= dir.y;
  401. joint2->velocity[2] -= dir.z;
  402. */
  403. }
  404. connection++;
  405. }
  406. if (!(body->flags & TPE_BODY_FLAG_SOFT))
  407. {
  408. TPE_bodyReshape(body,world->environmentFunction);
  409. bodyTension /= body->connectionCount;
  410. if (bodyTension > 20)
  411. {
  412. TPE_bodyReshape(body,world->environmentFunction);
  413. TPE_bodyReshape(body,world->environmentFunction);
  414. TPE_bodyReshape(body,world->environmentFunction);
  415. TPE_bodyReshape(body,world->environmentFunction);
  416. }
  417. }
  418. printf("%d\n",body->disableCount);
  419. if (body->disableCount >= 64)
  420. {
  421. body->disableCount = 0;
  422. body->flags |= TPE_BODY_FLAG_DEACTIVATED;
  423. }
  424. else if (TPE_bodyAverageSpeed(body) <= 25) // TODO: magic + optimize
  425. {
  426. body->disableCount++;
  427. }
  428. else
  429. body->disableCount = 0;
  430. }
  431. }
  432. void TPE_bodyWake(TPE_Body *body)
  433. {
  434. TPE_bodyStop(body);
  435. body->flags &= ~TPE_BODY_FLAG_DEACTIVATED;
  436. }
  437. TPE_Unit TPE_bodyNetSpeed(const TPE_Body *body)
  438. {
  439. TPE_Unit velocity = 0;
  440. const TPE_Joint *joint = body->joints;
  441. for (uint16_t i = 0; i < body->jointCount; ++i)
  442. {
  443. velocity += TPE_LENGTH(
  444. TPE_vec3(joint->velocity[0],joint->velocity[1],joint->velocity[2]));
  445. joint++;
  446. }
  447. return velocity;
  448. }
  449. TPE_Unit TPE_bodyAverageSpeed(const TPE_Body *body)
  450. {
  451. return TPE_bodyNetSpeed(body) / body->jointCount;
  452. }
  453. void TPE_bodyMultiplyNetSpeed(TPE_Body *body, TPE_Unit factor)
  454. {
  455. TPE_Joint *joint = body->joints;
  456. for (uint16_t j = 0; j < body->jointCount; ++j)
  457. {
  458. for (uint8_t k = 0; k < 3; ++k)
  459. joint->velocity[k] =
  460. (((TPE_Unit) joint->velocity[k]) * factor) /
  461. TPE_FRACTIONS_PER_UNIT;
  462. joint++;
  463. }
  464. }
  465. void TPE_bodyLimitNetSpeed(TPE_Body *body, TPE_Unit speedMin,
  466. TPE_Unit speedMax)
  467. {
  468. for (uint8_t i = 0; i < 16; ++i)
  469. {
  470. TPE_Unit speed = TPE_bodyNetSpeed(body);
  471. if (speed >= speedMin && speed <= speedMax)
  472. return;
  473. TPE_Unit fraction =
  474. (((speedMax + speedMin) / 2) * TPE_FRACTIONS_PER_UNIT) /
  475. TPE_nonZero(speed);
  476. TPE_bodyMultiplyNetSpeed(body,fraction);
  477. }
  478. }
  479. void TPE_bodyReshape(TPE_Body *body,
  480. TPE_ClosestPointFunction environmentFunction)
  481. {
  482. for (uint16_t i = 0; i < body->connectionCount; ++i)
  483. {
  484. TPE_Connection *c = &body->connections[i];
  485. TPE_Joint *j1 = &(body->joints[c->joint1]);
  486. TPE_Joint *j2 = &(body->joints[c->joint2]);
  487. TPE_Vec3 dir = TPE_vec3Minus(j2->position,j1->position);
  488. TPE_Vec3 middle = TPE_vec3Plus(j1->position,j2->position);
  489. middle.x /= 2;
  490. middle.y /= 2;
  491. middle.z /= 2;
  492. TPE_vec3Normalize(&dir);
  493. dir.x = (dir.x * c->length) / TPE_FRACTIONS_PER_UNIT;
  494. dir.y = (dir.y * c->length) / TPE_FRACTIONS_PER_UNIT;
  495. dir.z = (dir.z * c->length) / TPE_FRACTIONS_PER_UNIT;
  496. TPE_Vec3 positionBackup = j1->position;
  497. j1->position.x = middle.x - dir.x / 2;
  498. j1->position.y = middle.y - dir.y / 2;
  499. j1->position.z = middle.z - dir.z / 2;
  500. if (environmentFunction != 0 &&
  501. TPE_LENGTH(TPE_vec3Minus(j1->position,environmentFunction(j1->position)))
  502. < TPE_JOINT_SIZE(*j1))
  503. j1->position = positionBackup;
  504. positionBackup = j2->position;
  505. j2->position.x = j1->position.x + dir.x;
  506. j2->position.y = j1->position.y + dir.y;
  507. j2->position.z = j1->position.z + dir.z;
  508. if (environmentFunction != 0 &&
  509. TPE_LENGTH(TPE_vec3Minus(j2->position,environmentFunction(j2->position)))
  510. < TPE_JOINT_SIZE(*j2))
  511. j2->position = positionBackup;
  512. }
  513. }
  514. TPE_Vec3 TPE_vec3Plus(TPE_Vec3 v1, TPE_Vec3 v2)
  515. {
  516. v1.x += v2.x;
  517. v1.y += v2.y;
  518. v1.z += v2.z;
  519. return v1;
  520. }
  521. TPE_Vec3 TPE_vec3Minus(TPE_Vec3 v1, TPE_Vec3 v2)
  522. {
  523. v1.x -= v2.x;
  524. v1.y -= v2.y;
  525. v1.z -= v2.z;
  526. return v1;
  527. }
  528. void TPE_vec3Normalize(TPE_Vec3 *v)
  529. {
  530. TPE_Unit l = TPE_LENGTH(*v);
  531. if (l != 0)
  532. {
  533. v->x = (v->x * TPE_FRACTIONS_PER_UNIT) / l;
  534. v->y = (v->y * TPE_FRACTIONS_PER_UNIT) / l;
  535. v->z = (v->z * TPE_FRACTIONS_PER_UNIT) / l;
  536. }
  537. }
  538. TPE_Vec3 TPE_bodyGetCenter(const TPE_Body *body)
  539. {
  540. // TODO: take into account possibly different joint sizes? does it even matter?
  541. TPE_Vec3 result = TPE_vec3(0,0,0);
  542. const TPE_Joint *j = body->joints;
  543. for (uint16_t i = 0; i < body->jointCount; ++i)
  544. {
  545. result = TPE_vec3Plus(result,j->position);
  546. j++;
  547. }
  548. result.x /= body->jointCount;
  549. result.y /= body->jointCount;
  550. result.z /= body->jointCount;
  551. return result;
  552. }
  553. void TPE_bodySpin(TPE_Body *body, TPE_Vec3 rotation)
  554. {
  555. TPE_Vec3 center = TPE_bodyGetCenter(body);
  556. TPE_Unit anuglarV = TPE_LENGTH(rotation);
  557. for (uint16_t i = 0; i < body->jointCount; ++i)
  558. {
  559. TPE_Joint *j = body->joints + i;
  560. TPE_Vec3 toPoint = TPE_vec3Minus(j->position,center);
  561. toPoint = TPE_vec3Project(toPoint,rotation);
  562. toPoint = TPE_vec3Plus(center,toPoint);
  563. toPoint = TPE_vec3Minus(j->position,toPoint);
  564. toPoint = TPE_vec3Cross(toPoint,rotation);
  565. j->velocity[0] += toPoint.x;
  566. j->velocity[1] += toPoint.y;
  567. j->velocity[2] += toPoint.z;
  568. }
  569. }
  570. void TPE_bodyRotate(TPE_Body *body, TPE_Vec3 rotation)
  571. {
  572. TPE_Vec3 bodyCenter = TPE_bodyGetCenter(body);
  573. TPE_Unit angle = TPE_LENGTH(rotation);
  574. TPE_vec3Normalize(&rotation);
  575. for (uint16_t i = 0; i < body->jointCount; ++i)
  576. {
  577. TPE_Joint *j = body->joints + i;
  578. TPE_Vec3 toPoint = TPE_vec3Minus(j->position,bodyCenter);
  579. toPoint = TPE_vec3Project(toPoint,rotation);
  580. TPE_Vec3 rotationCenter = TPE_vec3Plus(bodyCenter,toPoint);
  581. toPoint = TPE_vec3Minus(j->position,rotationCenter);
  582. TPE_Vec3 toPoint2 = TPE_vec3Cross(toPoint,rotation);
  583. j->position =
  584. TPE_vec3Plus(rotationCenter,
  585. TPE_vec3Plus(
  586. TPE_vec3Times(toPoint,TPE_cos(angle)),
  587. TPE_vec3Times(toPoint2,TPE_sin(angle))
  588. )
  589. );
  590. }
  591. }
  592. TPE_Vec3 TPE_vec3Cross(TPE_Vec3 v1, TPE_Vec3 v2)
  593. {
  594. TPE_Vec3 r;
  595. r.x = (v1.y * v2.z - v1.z * v2.y) / TPE_FRACTIONS_PER_UNIT;
  596. r.y = (v1.z * v2.x - v1.x * v2.z) / TPE_FRACTIONS_PER_UNIT;
  597. r.z = (v1.x * v2.y - v1.y * v2.x) / TPE_FRACTIONS_PER_UNIT;
  598. return r;
  599. }
  600. TPE_Vec3 TPE_vec3Project(TPE_Vec3 v, TPE_Vec3 base)
  601. {
  602. TPE_Vec3 r;
  603. TPE_vec3Normalize(&base);
  604. TPE_Unit p = TPE_vec3Dot(v,base);
  605. r.x = (p * base.x) / TPE_FRACTIONS_PER_UNIT;
  606. r.y = (p * base.y) / TPE_FRACTIONS_PER_UNIT;
  607. r.z = (p * base.z) / TPE_FRACTIONS_PER_UNIT;
  608. return r;
  609. }
  610. void TPE_bodyMove(TPE_Body *body, TPE_Vec3 offset)
  611. {
  612. for (uint16_t i = 0; i < body->jointCount; ++i)
  613. body->joints[i].position = TPE_vec3Plus(body->joints[i].position,
  614. offset);
  615. }
  616. void TPE_bodyAccelerate(TPE_Body *body, TPE_Vec3 velocity)
  617. {
  618. for (uint16_t i = 0; i < body->jointCount; ++i)
  619. {
  620. body->joints[i].velocity[0] += velocity.x;
  621. body->joints[i].velocity[1] += velocity.y;
  622. body->joints[i].velocity[2] += velocity.z;
  623. }
  624. }
  625. void TPE_bodyStop(TPE_Body *body)
  626. {
  627. for (uint16_t i = 0; i < body->jointCount; ++i)
  628. {
  629. body->joints[i].velocity[0] = 0;
  630. body->joints[i].velocity[1] = 0;
  631. body->joints[i].velocity[2] = 0;
  632. }
  633. }
  634. TPE_Unit TPE_vec3Dot(TPE_Vec3 v1, TPE_Vec3 v2)
  635. {
  636. return (v1.x * v2.x + v1.y * v2.y + v1.z * v2.z) / TPE_FRACTIONS_PER_UNIT;
  637. }
  638. TPE_Unit TPE_cos(TPE_Unit x)
  639. {
  640. return TPE_sin(x + TPE_FRACTIONS_PER_UNIT / 4);
  641. }
  642. TPE_Unit TPE_sin(TPE_Unit x)
  643. {
  644. int8_t sign = 1;
  645. if (x < 0) // odd function
  646. {
  647. x *= -1;
  648. sign = -1;
  649. }
  650. x %= TPE_FRACTIONS_PER_UNIT;
  651. if (x > TPE_FRACTIONS_PER_UNIT / 2)
  652. {
  653. x -= TPE_FRACTIONS_PER_UNIT / 2;
  654. sign *= -1;
  655. }
  656. TPE_Unit tmp = TPE_FRACTIONS_PER_UNIT - 2 * x;
  657. #define _PI2 ((TPE_Unit) (9.8696044 * TPE_FRACTIONS_PER_UNIT))
  658. return sign * // Bhaskara's approximation
  659. (((32 * x * _PI2) / TPE_FRACTIONS_PER_UNIT) * tmp) /
  660. ((_PI2 * (5 * TPE_FRACTIONS_PER_UNIT - (8 * x * tmp) /
  661. TPE_FRACTIONS_PER_UNIT)) / TPE_FRACTIONS_PER_UNIT);
  662. #undef _PI2
  663. }
  664. void TPE_bodiesResolveCollision(TPE_Body *b1, TPE_Body *b2)
  665. {
  666. // TODO: bounding sphere (or AABB? maybe ifdef)
  667. for (uint16_t i = 0; i < b1->jointCount; ++i)
  668. for (uint16_t j = 0; j < b2->jointCount; ++j)
  669. {
  670. TPE_jointsResolveCollision(
  671. &(b1->joints[i]),
  672. &(b2->joints[j]),
  673. b1->jointMass,
  674. b2->jointMass,
  675. 512
  676. );
  677. }
  678. }
  679. void TPE_jointsResolveCollision(TPE_Joint *j1, TPE_Joint *j2,
  680. TPE_Unit mass1, TPE_Unit mass2, TPE_Unit elasticity)
  681. {
  682. TPE_Vec3 dir = TPE_vec3Minus(j2->position,j1->position);
  683. TPE_Unit d = TPE_LENGTH(dir) - TPE_JOINT_SIZE(*j1) - TPE_JOINT_SIZE(*j2);
  684. if (d < 0) // collision?
  685. {
  686. // separate bodies, the shift distance will depend on the weight ratio:
  687. d *= -1;
  688. TPE_vec3Normalize(&dir);
  689. TPE_Unit ratio = (mass2 * TPE_FRACTIONS_PER_UNIT) /
  690. TPE_nonZero(mass1 + mass2);
  691. TPE_Unit shiftDistance = (ratio * d) / TPE_FRACTIONS_PER_UNIT;
  692. TPE_Vec3 shift = TPE_vec3Times(dir,shiftDistance);
  693. j1->position = TPE_vec3Minus(j1->position,shift);
  694. shiftDistance = d - shiftDistance;
  695. shift = TPE_vec3Times(dir,shiftDistance);
  696. j2->position = TPE_vec3Plus(j2->position,shift);
  697. // compute new velocities:
  698. TPE_Unit v1, v2;
  699. TPE_Vec3 vel = TPE_vec3(j1->velocity[0],j1->velocity[1],j1->velocity[2]);
  700. vel = TPE_vec3Project(vel,dir);
  701. j1->velocity[0] = j1->velocity[0] - vel.x;
  702. j1->velocity[1] = j1->velocity[1] - vel.y;
  703. j1->velocity[2] = j1->velocity[2] - vel.z;
  704. v1 = TPE_vec3Dot(vel,dir);
  705. vel = TPE_vec3(j2->velocity[0],j2->velocity[1],j2->velocity[2]);
  706. vel = TPE_vec3Project(vel,dir);
  707. j2->velocity[0] = j2->velocity[0] - vel.x;
  708. j2->velocity[1] = j2->velocity[1] - vel.y;
  709. j2->velocity[2] = j2->velocity[2] - vel.z;
  710. v2 = TPE_vec3Dot(vel,dir);
  711. TPE_getVelocitiesAfterCollision(&v1,&v2,mass1,mass2,elasticity);
  712. vel = TPE_vec3Times(dir,v1);
  713. j1->velocity[0] = j1->velocity[0] + vel.x;
  714. j1->velocity[1] = j1->velocity[1] + vel.y;
  715. j1->velocity[2] = j1->velocity[2] + vel.z;
  716. vel = TPE_vec3Times(dir,v2);
  717. j2->velocity[0] = j2->velocity[0] + vel.x;
  718. j2->velocity[1] = j2->velocity[1] + vel.y;
  719. j2->velocity[2] = j2->velocity[2] + vel.z;
  720. }
  721. }
  722. TPE_Vec3 TPE_vec3Times(TPE_Vec3 v, TPE_Unit units)
  723. {
  724. v.x = (v.x * units) / TPE_FRACTIONS_PER_UNIT;
  725. v.y = (v.y * units) / TPE_FRACTIONS_PER_UNIT;
  726. v.z = (v.z * units) / TPE_FRACTIONS_PER_UNIT;
  727. return v;
  728. }
  729. TPE_Vec3 TPE_vec3TimesNonNormalized(TPE_Vec3 v, TPE_Unit q)
  730. {
  731. v.x *= q;
  732. v.y *= q;
  733. v.z *= q;
  734. return v;
  735. }
  736. void TPE_getVelocitiesAfterCollision(
  737. TPE_Unit *v1,
  738. TPE_Unit *v2,
  739. TPE_Unit m1,
  740. TPE_Unit m2,
  741. TPE_Unit elasticity
  742. )
  743. {
  744. /* in the following a lot of TPE_FRACTIONS_PER_UNIT cancel out, feel free to
  745. check if confused */
  746. TPE_Unit m1Pm2 = TPE_nonZero(m1 + m2);
  747. TPE_Unit v2Mv1 = TPE_nonZero(*v2 - *v1);
  748. TPE_Unit m1v1Pm2v2 = ((m1 * *v1) + (m2 * *v2));
  749. *v1 = (((elasticity * m2 / TPE_FRACTIONS_PER_UNIT) * v2Mv1)
  750. + m1v1Pm2v2) / m1Pm2;
  751. *v2 = (((elasticity * m1 / TPE_FRACTIONS_PER_UNIT) * -1 * v2Mv1)
  752. + m1v1Pm2v2) / m1Pm2;
  753. }
  754. void TPE_jointEnvironmentResolveCollision(TPE_Joint *joint, TPE_Unit elasticity,
  755. TPE_Unit friction, TPE_ClosestPointFunction env)
  756. {
  757. TPE_Vec3 toJoint = TPE_vec3Minus(joint->position,env(joint->position));
  758. TPE_Unit len = TPE_LENGTH(toJoint);
  759. if (len < TPE_JOINT_SIZE(*joint))
  760. {
  761. // colliding
  762. TPE_Vec3 positionBackup = joint->position, shift;
  763. uint8_t success = 0;
  764. if (len > 3) // TODO: magic constants
  765. {
  766. /* Joint center is still outside the geometry so we can determine the
  767. normal and use it to shift it outside. This can still leave the joint
  768. colliding though, so try to repeat it a few times. */
  769. for (int i = 0; i < 3; ++i)
  770. {
  771. shift = toJoint;
  772. TPE_vec3Normalize(&shift);
  773. shift = TPE_vec3Times(shift,TPE_JOINT_SIZE(*joint) - len +
  774. TPE_FRACTIONS_PER_UNIT / 64); // TODO: 128, magic val;
  775. joint->position = TPE_vec3Plus(joint->position,shift);
  776. toJoint = TPE_vec3Minus(joint->position,env(joint->position));
  777. len = TPE_LENGTH(toJoint); // still colliding?
  778. if (len >= TPE_JOINT_SIZE(*joint))
  779. {
  780. success = 1;
  781. break;
  782. }
  783. }
  784. }
  785. if (!success)
  786. {
  787. /* Shifting along normal was unsuccessfull, now try different approach:
  788. shift back by joint velocity. */
  789. shift = TPE_vec3(-1 * joint->velocity[0],-1 * joint->velocity[1],
  790. -1 * joint->velocity[2]);
  791. for (int i = 0; i < 3; ++i)
  792. {
  793. joint->position = TPE_vec3Plus(joint->position,shift);
  794. toJoint = TPE_vec3Minus(joint->position,env(joint->position));
  795. len = TPE_LENGTH(toJoint); // still colliding?
  796. if (len >= TPE_JOINT_SIZE(*joint))
  797. {
  798. success = 1;
  799. break;
  800. }
  801. shift.x /= 8;
  802. shift.y /= 8;
  803. shift.z /= 8;
  804. }
  805. }
  806. if (success)
  807. {
  808. TPE_Vec3 vel = TPE_vec3(joint->velocity[0],joint->velocity[1],
  809. joint->velocity[2]);
  810. vel = TPE_vec3Project(vel,shift);
  811. TPE_Vec3 vel2 = TPE_vec3Minus
  812. (
  813. TPE_vec3(joint->velocity[0],joint->velocity[1],joint->velocity[2]),
  814. vel
  815. );
  816. vel2 = TPE_vec3Times(vel2,friction);
  817. vel = TPE_vec3Times(vel,TPE_FRACTIONS_PER_UNIT + elasticity);
  818. joint->velocity[0] -= vel.x + vel2.x;
  819. joint->velocity[1] -= vel.y + vel2.y;
  820. joint->velocity[2] -= vel.z + vel2.z;
  821. }
  822. else
  823. {
  824. TPE_LOG("WARNING: joint-environment collision couldn't be resolved");
  825. joint->position = positionBackup;
  826. joint->velocity[0] = 0;
  827. joint->velocity[1] = 0;
  828. joint->velocity[2] = 0;
  829. }
  830. }
  831. }
  832. void TPE_bodyEnvironmentResolveCollision(TPE_Body *body, TPE_Unit elasticity,
  833. TPE_Unit friction, TPE_ClosestPointFunction env)
  834. {
  835. // TODO: bounding sphere
  836. for (uint16_t i = 0; i < body->jointCount; ++i)
  837. TPE_jointEnvironmentResolveCollision(
  838. body->joints + i,elasticity,friction,env);
  839. }
  840. TPE_Vec3 TPE_vec3Normalized(TPE_Vec3 v)
  841. {
  842. TPE_vec3Normalize(&v);
  843. return v;
  844. }
  845. TPE_Unit TPE_atan(TPE_Unit x)
  846. {
  847. /* atan approximation by polynomial
  848. WARNING: this will break with different value of TPE_FRACTIONS_PER_UNIT */
  849. TPE_Unit sign = 1, x2 = x * x;
  850. if (x < 0)
  851. {
  852. x *= -1;
  853. sign = -1;
  854. }
  855. return sign *
  856. (307 * x + x2) / ((267026 + 633 * x + x2) / 128);
  857. }
  858. void _TPE_vec2Rotate(TPE_Unit *x, TPE_Unit *y, TPE_Unit angle)
  859. {
  860. TPE_Unit tmp = *x;
  861. TPE_Unit s = TPE_sin(angle);
  862. TPE_Unit c = TPE_cos(angle);
  863. *x = (c * *x - s * *y) / TPE_FRACTIONS_PER_UNIT;
  864. *y = (s * tmp + c * *y) / TPE_FRACTIONS_PER_UNIT;
  865. }
  866. TPE_Unit _TPE_vec2Angle(TPE_Unit x, TPE_Unit y)
  867. {
  868. TPE_Unit r = 0;
  869. if (x != 0)
  870. {
  871. r = TPE_atan((y * TPE_FRACTIONS_PER_UNIT) / x);
  872. if (x < 0)
  873. r += TPE_FRACTIONS_PER_UNIT / 2;
  874. else if (r < 0)
  875. r = TPE_FRACTIONS_PER_UNIT + r;
  876. }
  877. else
  878. {
  879. if (y < 0)
  880. r = (3 * TPE_FRACTIONS_PER_UNIT) / 4;
  881. else if (y > 0)
  882. r = TPE_FRACTIONS_PER_UNIT / 4;
  883. // else (y == 0) r stays 0
  884. }
  885. return r;
  886. }
  887. TPE_Vec3 TPE_orientationFromVecs(TPE_Vec3 forward, TPE_Vec3 right)
  888. {
  889. TPE_Vec3 result;
  890. // get rotation around Y:
  891. result.y =
  892. (TPE_FRACTIONS_PER_UNIT - _TPE_vec2Angle(forward.z,forward.x)) %
  893. TPE_FRACTIONS_PER_UNIT;
  894. // now rotate back by this angle to align with x = 0 plane:
  895. _TPE_vec2Rotate(&forward.z,&forward.x,result.y);
  896. _TPE_vec2Rotate(&right.z,&right.x,result.y);
  897. // now do the same for the second axis:
  898. result.x =
  899. (TPE_FRACTIONS_PER_UNIT - _TPE_vec2Angle(forward.z,forward.y)) %
  900. TPE_FRACTIONS_PER_UNIT;
  901. _TPE_vec2Rotate(&forward.z,&forward.y,result.x);
  902. printf("aaa:");
  903. TPE_PRINTF_VEC3(forward);
  904. TPE_PRINTF_VEC3(right);
  905. printf("\n");
  906. _TPE_vec2Rotate(&right.z,&right.y,result.x);
  907. result.z =
  908. (TPE_FRACTIONS_PER_UNIT - _TPE_vec2Angle(right.x,right.y)) %
  909. TPE_FRACTIONS_PER_UNIT;
  910. return result;
  911. }
  912. #endif // guard