test.c 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. /** General automatic test for tinyphysicsengine, this should always be run
  2. and passed before publishing a new version of TPE. */
  3. #include "../tinyphysicsengine.h"
  4. #include <stdio.h>
  5. #define ass(cond,text) { printf("[CHECKING] " text ": "); if (!(cond)) { puts("ERROR"); return 1; } else puts("OK"); }
  6. TPE_Unit rampPoits[6] =
  7. {
  8. 0,0,
  9. -2400,1400,
  10. -2400,0
  11. };
  12. TPE_Vec3 envFunc(TPE_Vec3 p, TPE_Unit maxD)
  13. {
  14. TPE_ENV_START( TPE_envAABoxInside(p,TPE_vec3(0,1000,0),TPE_vec3(7000,6000,7000)),p )
  15. TPE_ENV_NEXT( TPE_envAATriPrism(p,TPE_vec3(100,200,-10),rampPoits,3000,2),p)
  16. TPE_ENV_NEXT( TPE_envBox(p,TPE_vec3(30,200,-10),TPE_vec3(500,600,700),TPE_vec3(10,20,30)), p)
  17. TPE_ENV_NEXT( TPE_envAABox(p,TPE_vec3(-100,300,200),TPE_vec3(500,600,700)), p)
  18. TPE_ENV_END
  19. }
  20. TPE_Vec3 envFunc2(TPE_Vec3 p, TPE_Unit maxD)
  21. {
  22. TPE_ENV_START( TPE_envSphereInside(p,TPE_vec3(100,20,-3),5000), p)
  23. TPE_ENV_NEXT( TPE_envGround(p,-500), p)
  24. if (TPE_ENV_BCUBE_TEST(p,maxD,TPE_vec3(300,-40,50),1100) )
  25. {
  26. TPE_ENV_NEXT( TPE_envCylinder(p,TPE_vec3(300,-40,50), TPE_vec3(-400,-200,-50), 751), p)
  27. }
  28. TPE_ENV_NEXT( TPE_envCylinder(p,TPE_vec3(-500,500,-20),TPE_vec3(-400,600,700),500), p)
  29. TPE_ENV_END
  30. }
  31. TPE_Vec3 envFuncBad(TPE_Vec3 p, TPE_Unit maxD)
  32. {
  33. p.x += 200;
  34. return p;
  35. }
  36. TPE_Vec3 envFuncBad2(TPE_Vec3 p, TPE_Unit maxD)
  37. {
  38. if (p.y > p.x)
  39. p.x = p.y;
  40. return p;
  41. }
  42. TPE_Vec3 envSimple(TPE_Vec3 p, TPE_Unit maxD)
  43. {
  44. TPE_ENV_START( TPE_envAABoxInside(p,TPE_vec3(0,0,0),TPE_vec3(10000,10000,10000)), p)
  45. TPE_ENV_NEXT( TPE_envHalfPlane(p,TPE_vec3(0,-5000,0),TPE_vec3(500,500,0)), p)
  46. TPE_ENV_END
  47. }
  48. TPE_Unit heightMap(int32_t x, int32_t y)
  49. {
  50. x *= 16;
  51. y *= 16;
  52. return
  53. TPE_sin(x + TPE_cos(y * 2)) * TPE_sin(y * 2 + TPE_cos(x * 4)) /
  54. (TPE_FRACTIONS_PER_UNIT / 2);
  55. }
  56. TPE_Vec3 envFuncHeightmap(TPE_Vec3 p, TPE_Unit maxD)
  57. {
  58. return TPE_envHeightmap(p,TPE_vec3(10,20,30),500,heightMap,maxD);
  59. }
  60. int main(void)
  61. {
  62. puts("== testing tinyphysicsengine ==");
  63. {
  64. ass(TPE_vec2Angle(-100,0) == TPE_FRACTIONS_PER_UNIT / 2,"vec2 angle")
  65. TPE_Unit l;
  66. l = TPE_LENGTH(TPE_vec3Normalized(TPE_vec3(100,0,0)));
  67. ass(TPE_abs(l - TPE_FRACTIONS_PER_UNIT) < 5,"vec3 normalize")
  68. l = TPE_LENGTH(TPE_vec3Normalized(TPE_vec3(0,0,0)));
  69. ass(TPE_abs(l - TPE_FRACTIONS_PER_UNIT) < 5,"zero vec3 normalize")
  70. l = TPE_LENGTH(TPE_vec3Normalized(TPE_vec3(0,-1,0)));
  71. ass(TPE_abs(l - TPE_FRACTIONS_PER_UNIT) < 5,"small vec3 normalize")
  72. l = TPE_LENGTH(TPE_vec3Normalized(TPE_vec3(500000,300000,-700000)));
  73. ass(TPE_abs(l - TPE_FRACTIONS_PER_UNIT) < 5,"big vec3 normalize")
  74. }
  75. {
  76. TPE_Joint joints[16];
  77. TPE_Connection cons[16];
  78. joints[0] = TPE_joint(TPE_vec3(200,100,-400),300);
  79. joints[1] = TPE_joint(TPE_vec3(100,200,-400),300);
  80. joints[2] = TPE_joint(TPE_vec3(200,-400,200),300);
  81. joints[3] = TPE_joint(TPE_vec3(200,100,-400),400);
  82. cons[0].joint1 = 0; cons[0].joint2 = 1; cons[0].length = 1000;
  83. cons[1].joint1 = 1; cons[1].joint2 = 0; cons[1].length = 1000;
  84. cons[2].joint1 = 0; cons[2].joint2 = 1; cons[2].length = 1100;
  85. cons[3].joint1 = 2; cons[3].joint2 = 3; cons[3].length = 100;
  86. uint32_t jHashes[4], cHashes[4];
  87. for (int i = 0; i < 4; ++i)
  88. {
  89. jHashes[i] = TPE_jointHash(&joints[i]);
  90. cHashes[i] = TPE_connectionHash(&cons[i]);
  91. }
  92. for (int i = 0; i < 4; ++i)
  93. for (int j = i + 1; j < 4; ++j)
  94. ass(jHashes[i] != jHashes[j] && cHashes[i] != cHashes[j],"joints/connection hash");
  95. TPE_Body bodies[8];
  96. uint32_t bHashes[4];
  97. TPE_bodyInit(&bodies[0],joints,4,cons,4,300);
  98. TPE_bodyInit(&bodies[1],joints + 1,3,cons,4,300);
  99. TPE_bodyInit(&bodies[2],joints,4,cons,4,300);
  100. bodies[2].flags |= TPE_BODY_FLAG_SOFT | TPE_BODY_FLAG_NONROTATING;
  101. TPE_bodyInit(&bodies[3],joints,4,cons,4,200);
  102. for (int i = 0; i < 4; ++i)
  103. bHashes[i] = TPE_bodyHash(&bodies[i]);
  104. for (int i = 0; i < 4; ++i)
  105. for (int j = i + 1; j < 4; ++j)
  106. ass(bHashes[i] != bHashes[j],"body hash");
  107. TPE_World world;
  108. uint32_t wHashes[4];
  109. TPE_worldInit(&world,bodies,4,0);
  110. wHashes[0] = TPE_worldHash(&world);
  111. bodies[0].jointCount--;
  112. wHashes[1] = TPE_worldHash(&world);
  113. bodies[4] = bodies[0]; bodies[0] = bodies[3]; bodies[3] = bodies[4];
  114. wHashes[2] = TPE_worldHash(&world);
  115. world.bodyCount--;
  116. wHashes[3] = TPE_worldHash(&world);
  117. for (int i = 0; i < 4; ++i)
  118. for (int j = i + 1; j < 4; ++j)
  119. ass(wHashes[i] != wHashes[j],"world hash");
  120. }
  121. {
  122. puts("-- environment functions --");
  123. ass(TPE_testClosestPointFunction(envFunc,TPE_vec3(-3000,-3000,-3000),
  124. TPE_vec3(3000,3000,3000),32,40,0),"env function");
  125. ass(TPE_testClosestPointFunction(envFunc2,TPE_vec3(-2000,-1000,-5000),
  126. TPE_vec3(5000,6000,7000),32,30,0),"env function");
  127. ass(TPE_testClosestPointFunction(envFuncHeightmap,
  128. TPE_vec3(-2000,-1000,-5000),TPE_vec3(4000,6000,7000),6,50,0),
  129. "env function (heightmap)");
  130. ass(!TPE_testClosestPointFunction(envFuncBad,TPE_vec3(-1000,-1000,-1000),
  131. TPE_vec3(2000,3000,100),32,40,0),"env function bad");
  132. ass(!TPE_testClosestPointFunction(envFuncBad2,TPE_vec3(-1000,-2000,-200),
  133. TPE_vec3(1000,1000,2000),32,40,0),"env function bad");
  134. }
  135. {
  136. puts("-- simulation --");
  137. TPE_World w;
  138. TPE_Joint j[64];
  139. TPE_Connection c[128];
  140. TPE_Body b[4];
  141. j[0] = TPE_joint(TPE_vec3(0,0,0),320);
  142. TPE_bodyInit(&b[0],j,1,0,0,2124);
  143. TPE_bodyMoveBy(&b[0],TPE_vec3(-2000,3000,3000));
  144. TPE_makeBox(j + 1,c,800,900,850,320);
  145. TPE_bodyInit(&b[1],j + 1,8,c,16,1300);
  146. b[1].friction = 400;
  147. b[1].elasticity = 350;
  148. TPE_bodyMoveBy(&b[1],TPE_vec3(-2000,3800,1500));
  149. TPE_make2Line(j + 20,c + 32,1000,300);
  150. TPE_bodyInit(&b[2],j + 20,2,c + 32,1,1300);
  151. b[2].flags |= TPE_BODY_FLAG_NONROTATING;
  152. TPE_bodyMoveBy(&b[2],TPE_vec3(-3000,4100,-1500));
  153. TPE_makeCenterBox(j + 32,c + 64,600,500,510,300);
  154. TPE_bodyInit(&b[3],j + 32,9,c + 64,18,1200);
  155. b[3].flags |= TPE_BODY_FLAG_SOFT;
  156. TPE_bodyMoveBy(&b[3],TPE_vec3(-1500,3500,-3000));
  157. TPE_worldInit(&w,b,4,envSimple);
  158. int16_t bi;
  159. TPE_castBodyRay(TPE_vec3(-1857,3743,-4800),TPE_vec3(0,0,100),-1,&w,&bi,0);
  160. ass(bi >= 0,"body ray hit");
  161. puts("dropping bodies onto a ramp...");
  162. for (int i = 0; i < 300; ++i)
  163. {
  164. for (uint8_t j = 0; j < w.bodyCount; ++j)
  165. TPE_bodyApplyGravity(&w.bodies[j],8);
  166. if (i == 100)
  167. ass(TPE_worldGetNetSpeed(&w) > 100,"world net speed");
  168. TPE_worldStep(&w);
  169. }
  170. puts("simulation finished");
  171. uint32_t hash = TPE_worldHash(&w);
  172. printf("world hash: %lu\n",hash);
  173. // change the hash if functionality changes:
  174. ass(hash == 3411004027,"world hash");
  175. for (int i = 0; i < w.bodyCount; ++i)
  176. {
  177. ass(TPE_bodyGetCenterOfMass(&w.bodies[i]).x > 0,"x position > 0");
  178. ass(w.bodies[i].flags & TPE_BODY_FLAG_DEACTIVATED,"deactivated?");
  179. }
  180. ass(TPE_worldGetNetSpeed(&w) < 100,"world net speed");
  181. TPE_bodyAccelerate(&w.bodies[0],TPE_vec3(200,300,-20));
  182. TPE_bodyAccelerate(&w.bodies[1],TPE_vec3(-700,400,0));
  183. TPE_bodyAccelerate(&w.bodies[2],TPE_vec3(20,-300,-100));
  184. TPE_bodyAccelerate(&w.bodies[3],TPE_vec3(0,30,-900));
  185. puts("exploding bodies...");
  186. for (int i = 0; i < 100; ++i)
  187. {
  188. for (uint8_t j = 0; j < w.bodyCount; ++j)
  189. TPE_bodyApplyGravity(&w.bodies[j],8);
  190. TPE_worldStep(&w);
  191. }
  192. // check if within environment
  193. for (int i = 0; i < w.bodyCount; ++i)
  194. {
  195. TPE_Vec3 p = TPE_bodyGetCenterOfMass(&w.bodies[i]);
  196. ass(p.x < 5000 && p.x > -5000 && p.y < 5000 && p.y > -5000 &&
  197. p.z < 5000 && p.z > -5000,"body within environment");
  198. }
  199. }
  200. {
  201. /* Here we'll be casting environment rays and checking if inside rays return
  202. inside results and outside rays return outside results. The function doesn't
  203. guarantee this but at least mostly this should hold. */
  204. int correct = 0;
  205. int incorrect = 0;
  206. for (int y = -3200; y < 1000; y += 52)
  207. for (int x = -4000; x < 3000; x += 40)
  208. {
  209. TPE_Vec3 p = TPE_vec3(x,y,x / 16);
  210. TPE_Vec3 p2 = envFunc(p,300);
  211. uint8_t inside = p.x == p2.x && p.y == p2.y && p.z == p2.z;
  212. TPE_Vec3 p3 = TPE_castEnvironmentRay(p,TPE_vec3(x / 64 + 1,y / 32 -2,3),
  213. envFunc,30,100,32);
  214. p2 = envFunc(p3,300);
  215. uint8_t inside2 = p3.x == p2.x && p3.y == p2.y && p3.z == p2.z;
  216. if (inside != inside2)
  217. incorrect++;
  218. else
  219. correct++;
  220. }
  221. ass(correct > incorrect * 5,"environment rays behave kind of OK?");
  222. printf("(%d OK, %d not so OK)\n",correct,incorrect);
  223. }
  224. puts("DONE, all OK");
  225. return 0;
  226. }