demo_collision.cpp 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374
  1. /*************************************************************************
  2. * *
  3. * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. *
  4. * All rights reserved. Email: [email protected] Web: www.q12.org *
  5. * *
  6. * This library is free software; you can redistribute it and/or *
  7. * modify it under the terms of EITHER: *
  8. * (1) The GNU Lesser General Public License as published by the Free *
  9. * Software Foundation; either version 2.1 of the License, or (at *
  10. * your option) any later version. The text of the GNU Lesser *
  11. * General Public License is included with this library in the *
  12. * file LICENSE.TXT. *
  13. * (2) The BSD-style license that is included with this library in *
  14. * the file LICENSE-BSD.TXT. *
  15. * *
  16. * This library is distributed in the hope that it will be useful, *
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of *
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files *
  19. * LICENSE.TXT and LICENSE-BSD.TXT for more details. *
  20. * *
  21. *************************************************************************/
  22. /*
  23. collision tests. if this program is run without any arguments it will
  24. perform all the tests multiple times, with different random data for each
  25. test. if this program is given a test number it will run that test
  26. graphically/interactively, in which case the space bar can be used to
  27. change the random test conditions.
  28. */
  29. #include <ode/ode.h>
  30. #include <drawstuff/drawstuff.h>
  31. #include "texturepath.h"
  32. #ifdef _MSC_VER
  33. #pragma warning(disable:4244 4305) // for VC++, no precision loss complaints
  34. #endif
  35. // select correct drawing functions
  36. #ifdef dDOUBLE
  37. #define dsDrawSphere dsDrawSphereD
  38. #define dsDrawBox dsDrawBoxD
  39. #define dsDrawLine dsDrawLineD
  40. #define dsDrawCapsule dsDrawCapsuleD
  41. #endif
  42. //****************************************************************************
  43. // test infrastructure, including constants and macros
  44. #define TEST_REPS1 1000 // run each test this many times (first batch)
  45. #define TEST_REPS2 10000 // run each test this many times (second batch)
  46. const dReal tol = 1e-8; // tolerance used for numerical checks
  47. #define MAX_TESTS 1000 // maximum number of test slots
  48. #define Z_OFFSET 2 // z offset for drawing (to get above ground)
  49. //using namespace ode;
  50. // test function. returns 1 if the test passed or 0 if it failed
  51. typedef int test_function_t();
  52. struct TestSlot {
  53. int number; // number of test
  54. const char *name; // name of test
  55. int failcount;
  56. test_function_t *test_fn;
  57. int last_failed_line;
  58. };
  59. TestSlot testslot[MAX_TESTS];
  60. // globals used by the test functions
  61. int graphical_test=0; // show graphical results of this test, 0=none
  62. int current_test; // currently execiting test
  63. int draw_all_objects_called;
  64. #define MAKE_TEST(number,function) \
  65. if (testslot[number].name) dDebug (0,"test number already used"); \
  66. if (number <= 0 || number >= MAX_TESTS) dDebug (0,"bad test number"); \
  67. testslot[number].name = # function; \
  68. testslot[number].test_fn = function;
  69. #define FAILED() { if (graphical_test==0) { \
  70. testslot[current_test].last_failed_line=__LINE__; return 0; } }
  71. #define PASSED() { return 1; }
  72. //****************************************************************************
  73. // globals
  74. /* int dBoxBox (const dVector3 p1, const dMatrix3 R1,
  75. const dVector3 side1, const dVector3 p2,
  76. const dMatrix3 R2, const dVector3 side2,
  77. dVector3 normal, dReal *depth, int *code,
  78. int maxc, dContactGeom *contact, int skip); */
  79. void dLineClosestApproach (const dVector3 pa, const dVector3 ua,
  80. const dVector3 pb, const dVector3 ub,
  81. dReal *alpha, dReal *beta);
  82. //****************************************************************************
  83. // draw all objects in a space, and draw all the collision contact points
  84. void nearCallback (void *data, dGeomID o1, dGeomID o2)
  85. {
  86. int i,j,n;
  87. const int N = 100;
  88. dContactGeom contact[N];
  89. if (dGeomGetClass (o2) == dRayClass) {
  90. n = dCollide (o2,o1,N,&contact[0],sizeof(dContactGeom));
  91. }
  92. else {
  93. n = dCollide (o1,o2,N,&contact[0],sizeof(dContactGeom));
  94. }
  95. if (n > 0) {
  96. dMatrix3 RI;
  97. dRSetIdentity (RI);
  98. const dReal ss[3] = {0.01,0.01,0.01};
  99. for (i=0; i<n; i++) {
  100. contact[i].pos[2] += Z_OFFSET;
  101. dsDrawBox (contact[i].pos,RI,ss);
  102. dVector3 n;
  103. for (j=0; j<3; j++) n[j] = contact[i].pos[j] + 0.1*contact[i].normal[j];
  104. dsDrawLine (contact[i].pos,n);
  105. }
  106. }
  107. }
  108. void draw_all_objects (dSpaceID space)
  109. {
  110. int i, j;
  111. draw_all_objects_called = 1;
  112. if (!graphical_test) return;
  113. int n = dSpaceGetNumGeoms (space);
  114. // draw all contact points
  115. dsSetColor (0,1,1);
  116. dSpaceCollide (space,0,&nearCallback);
  117. // draw all rays
  118. for (i=0; i<n; i++) {
  119. dGeomID g = dSpaceGetGeom (space,i);
  120. if (dGeomGetClass (g) == dRayClass) {
  121. dsSetColor (1,1,1);
  122. dVector3 origin,dir;
  123. dGeomRayGet (g,origin,dir);
  124. origin[2] += Z_OFFSET;
  125. dReal length = dGeomRayGetLength (g);
  126. for (j=0; j<3; j++) dir[j] = dir[j]*length + origin[j];
  127. dsDrawLine (origin,dir);
  128. dsSetColor (0,0,1);
  129. dsDrawSphere (origin,dGeomGetRotation(g),0.01);
  130. }
  131. }
  132. // draw all other objects
  133. for (i=0; i<n; i++) {
  134. dGeomID g = dSpaceGetGeom (space,i);
  135. dVector3 pos;
  136. if (dGeomGetClass (g) != dPlaneClass) {
  137. memcpy (pos,dGeomGetPosition(g),sizeof(pos));
  138. pos[2] += Z_OFFSET;
  139. }
  140. switch (dGeomGetClass (g)) {
  141. case dSphereClass: {
  142. dsSetColorAlpha (1,0,0,0.8);
  143. dReal radius = dGeomSphereGetRadius (g);
  144. dsDrawSphere (pos,dGeomGetRotation(g),radius);
  145. break;
  146. }
  147. case dBoxClass: {
  148. dsSetColorAlpha (1,1,0,0.8);
  149. dVector3 sides;
  150. dGeomBoxGetLengths (g,sides);
  151. dsDrawBox (pos,dGeomGetRotation(g),sides);
  152. break;
  153. }
  154. case dCapsuleClass: {
  155. dsSetColorAlpha (0,1,0,0.8);
  156. dReal radius,length;
  157. dGeomCapsuleGetParams (g,&radius,&length);
  158. dsDrawCapsule (pos,dGeomGetRotation(g),length,radius);
  159. break;
  160. }
  161. case dPlaneClass: {
  162. dVector4 n;
  163. dMatrix3 R,sides;
  164. dVector3 pos2;
  165. dGeomPlaneGetParams (g,n);
  166. dRFromZAxis (R,n[0],n[1],n[2]);
  167. for (j=0; j<3; j++) pos[j] = n[j]*n[3];
  168. pos[2] += Z_OFFSET;
  169. sides[0] = 2;
  170. sides[1] = 2;
  171. sides[2] = 0.001;
  172. dsSetColor (1,0,1);
  173. for (j=0; j<3; j++) pos2[j] = pos[j] + 0.1*n[j];
  174. dsDrawLine (pos,pos2);
  175. dsSetColorAlpha (1,0,1,0.8);
  176. dsDrawBox (pos,R,sides);
  177. break;
  178. }
  179. }
  180. }
  181. }
  182. //****************************************************************************
  183. // point depth tests
  184. int test_sphere_point_depth()
  185. {
  186. int j;
  187. dVector3 p,q;
  188. dMatrix3 R;
  189. dReal r,d;
  190. dSimpleSpace space(0);
  191. dGeomID sphere = dCreateSphere (0,1);
  192. dSpaceAdd (space,sphere);
  193. // ********** make a random sphere of radius r at position p
  194. r = dRandReal()+0.1;
  195. dGeomSphereSetRadius (sphere,r);
  196. dMakeRandomVector (p,3,1.0);
  197. dGeomSetPosition (sphere,p[0],p[1],p[2]);
  198. dRFromAxisAndAngle (R,dRandReal()*2-1,dRandReal()*2-1,
  199. dRandReal()*2-1,dRandReal()*10-5);
  200. dGeomSetRotation (sphere,R);
  201. // ********** test center point has depth r
  202. if (dFabs(dGeomSpherePointDepth (sphere,p[0],p[1],p[2]) - r) > tol) FAILED();
  203. // ********** test point on surface has depth 0
  204. for (j=0; j<3; j++) q[j] = dRandReal()-0.5;
  205. dNormalize3 (q);
  206. for (j=0; j<3; j++) q[j] = q[j]*r + p[j];
  207. if (dFabs(dGeomSpherePointDepth (sphere,q[0],q[1],q[2])) > tol) FAILED();
  208. // ********** test point at random depth
  209. d = (dRandReal()*2-1) * r;
  210. for (j=0; j<3; j++) q[j] = dRandReal()-0.5;
  211. dNormalize3 (q);
  212. for (j=0; j<3; j++) q[j] = q[j]*(r-d) + p[j];
  213. if (dFabs(dGeomSpherePointDepth (sphere,q[0],q[1],q[2])-d) > tol) FAILED();
  214. PASSED();
  215. }
  216. int test_box_point_depth()
  217. {
  218. int i,j;
  219. dVector3 s,p,q,q2; // s = box sides
  220. dMatrix3 R;
  221. dReal ss,d; // ss = smallest side
  222. dSimpleSpace space(0);
  223. dGeomID box = dCreateBox (0,1,1,1);
  224. dSpaceAdd (space,box);
  225. // ********** make a random box
  226. for (j=0; j<3; j++) s[j] = dRandReal() + 0.1;
  227. dGeomBoxSetLengths (box,s[0],s[1],s[2]);
  228. dMakeRandomVector (p,3,1.0);
  229. dGeomSetPosition (box,p[0],p[1],p[2]);
  230. dRFromAxisAndAngle (R,dRandReal()*2-1,dRandReal()*2-1,
  231. dRandReal()*2-1,dRandReal()*10-5);
  232. dGeomSetRotation (box,R);
  233. // ********** test center point has depth of smallest side
  234. ss = 1e9;
  235. for (j=0; j<3; j++) if (s[j] < ss) ss = s[j];
  236. if (dFabs(dGeomBoxPointDepth (box,p[0],p[1],p[2]) - 0.5*ss) > tol)
  237. FAILED();
  238. // ********** test point on surface has depth 0
  239. for (j=0; j<3; j++) q[j] = (dRandReal()-0.5)*s[j];
  240. i = dRandInt (3);
  241. if (dRandReal() > 0.5) q[i] = 0.5*s[i]; else q[i] = -0.5*s[i];
  242. dMultiply0 (q2,dGeomGetRotation(box),q,3,3,1);
  243. for (j=0; j<3; j++) q2[j] += p[j];
  244. if (dFabs(dGeomBoxPointDepth (box,q2[0],q2[1],q2[2])) > tol) FAILED();
  245. // ********** test points outside box have -ve depth
  246. for (j=0; j<3; j++) {
  247. q[j] = 0.5*s[j] + dRandReal() + 0.01;
  248. if (dRandReal() > 0.5) q[j] = -q[j];
  249. }
  250. dMultiply0 (q2,dGeomGetRotation(box),q,3,3,1);
  251. for (j=0; j<3; j++) q2[j] += p[j];
  252. if (dGeomBoxPointDepth (box,q2[0],q2[1],q2[2]) >= 0) FAILED();
  253. // ********** test points inside box have +ve depth
  254. for (j=0; j<3; j++) q[j] = s[j] * 0.99 * (dRandReal()-0.5);
  255. dMultiply0 (q2,dGeomGetRotation(box),q,3,3,1);
  256. for (j=0; j<3; j++) q2[j] += p[j];
  257. if (dGeomBoxPointDepth (box,q2[0],q2[1],q2[2]) <= 0) FAILED();
  258. // ********** test random depth of point aligned along axis (up to ss deep)
  259. i = dRandInt (3);
  260. for (j=0; j<3; j++) q[j] = 0;
  261. d = (dRandReal()*(ss*0.5+1)-1);
  262. q[i] = s[i]*0.5 - d;
  263. if (dRandReal() > 0.5) q[i] = -q[i];
  264. dMultiply0 (q2,dGeomGetRotation(box),q,3,3,1);
  265. for (j=0; j<3; j++) q2[j] += p[j];
  266. if (dFabs(dGeomBoxPointDepth (box,q2[0],q2[1],q2[2]) - d) >= tol) FAILED();
  267. PASSED();
  268. }
  269. int test_ccylinder_point_depth()
  270. {
  271. int j;
  272. dVector3 p,a;
  273. dMatrix3 R;
  274. dReal r,l,beta,x,y,d;
  275. dSimpleSpace space(0);
  276. dGeomID ccyl = dCreateCapsule (0,1,1);
  277. dSpaceAdd (space,ccyl);
  278. // ********** make a random ccyl
  279. r = dRandReal()*0.5 + 0.01;
  280. l = dRandReal()*1 + 0.01;
  281. dGeomCapsuleSetParams (ccyl,r,l);
  282. dMakeRandomVector (p,3,1.0);
  283. dGeomSetPosition (ccyl,p[0],p[1],p[2]);
  284. dRFromAxisAndAngle (R,dRandReal()*2-1,dRandReal()*2-1,
  285. dRandReal()*2-1,dRandReal()*10-5);
  286. dGeomSetRotation (ccyl,R);
  287. // ********** test point on axis has depth of 'radius'
  288. beta = dRandReal()-0.5;
  289. for (j=0; j<3; j++) a[j] = p[j] + l*beta*R[j*4+2];
  290. if (dFabs(dGeomCapsulePointDepth (ccyl,a[0],a[1],a[2]) - r) >= tol)
  291. FAILED();
  292. // ********** test point on surface (excluding caps) has depth 0
  293. beta = dRandReal()*2*M_PI;
  294. x = r*sin(beta);
  295. y = r*cos(beta);
  296. beta = dRandReal()-0.5;
  297. for (j=0; j<3; j++) a[j] = p[j] + x*R[j*4+0] + y*R[j*4+1] + l*beta*R[j*4+2];
  298. if (dFabs(dGeomCapsulePointDepth (ccyl,a[0],a[1],a[2])) >= tol) FAILED();
  299. // ********** test point on surface of caps has depth 0
  300. for (j=0; j<3; j++) a[j] = dRandReal()-0.5;
  301. dNormalize3 (a);
  302. if (dCalcVectorDot3_14(a,R+2) > 0) {
  303. for (j=0; j<3; j++) a[j] = p[j] + a[j]*r + l*0.5*R[j*4+2];
  304. }
  305. else {
  306. for (j=0; j<3; j++) a[j] = p[j] + a[j]*r - l*0.5*R[j*4+2];
  307. }
  308. if (dFabs(dGeomCapsulePointDepth (ccyl,a[0],a[1],a[2])) >= tol) FAILED();
  309. // ********** test point inside ccyl has positive depth
  310. for (j=0; j<3; j++) a[j] = dRandReal()-0.5;
  311. dNormalize3 (a);
  312. beta = dRandReal()-0.5;
  313. for (j=0; j<3; j++) a[j] = p[j] + a[j]*r*0.99 + l*beta*R[j*4+2];
  314. if (dGeomCapsulePointDepth (ccyl,a[0],a[1],a[2]) < 0) FAILED();
  315. // ********** test point depth (1)
  316. d = (dRandReal()*2-1) * r;
  317. beta = dRandReal()*2*M_PI;
  318. x = (r-d)*sin(beta);
  319. y = (r-d)*cos(beta);
  320. beta = dRandReal()-0.5;
  321. for (j=0; j<3; j++) a[j] = p[j] + x*R[j*4+0] + y*R[j*4+1] + l*beta*R[j*4+2];
  322. if (dFabs(dGeomCapsulePointDepth (ccyl,a[0],a[1],a[2]) - d) >= tol)
  323. FAILED();
  324. // ********** test point depth (2)
  325. d = (dRandReal()*2-1) * r;
  326. for (j=0; j<3; j++) a[j] = dRandReal()-0.5;
  327. dNormalize3 (a);
  328. if (dCalcVectorDot3_14(a,R+2) > 0) {
  329. for (j=0; j<3; j++) a[j] = p[j] + a[j]*(r-d) + l*0.5*R[j*4+2];
  330. }
  331. else {
  332. for (j=0; j<3; j++) a[j] = p[j] + a[j]*(r-d) - l*0.5*R[j*4+2];
  333. }
  334. if (dFabs(dGeomCapsulePointDepth (ccyl,a[0],a[1],a[2]) - d) >= tol)
  335. FAILED();
  336. PASSED();
  337. }
  338. int test_plane_point_depth()
  339. {
  340. int j;
  341. dVector3 n,p,q,a,b; // n = plane normal
  342. dReal d;
  343. dSimpleSpace space(0);
  344. dGeomID plane = dCreatePlane (0,0,0,1,0);
  345. dSpaceAdd (space,plane);
  346. // ********** make a random plane
  347. for (j=0; j<3; j++) n[j] = dRandReal() - 0.5;
  348. dNormalize3 (n);
  349. d = dRandReal() - 0.5;
  350. dGeomPlaneSetParams (plane,n[0],n[1],n[2],d);
  351. dPlaneSpace (n,p,q);
  352. // ********** test point on plane has depth 0
  353. a[0] = dRandReal() - 0.5;
  354. a[1] = dRandReal() - 0.5;
  355. a[2] = 0;
  356. for (j=0; j<3; j++) b[j] = a[0]*p[j] + a[1]*q[j] + (a[2]+d)*n[j];
  357. if (dFabs(dGeomPlanePointDepth (plane,b[0],b[1],b[2])) >= tol) FAILED();
  358. // ********** test arbitrary depth point
  359. a[0] = dRandReal() - 0.5;
  360. a[1] = dRandReal() - 0.5;
  361. a[2] = dRandReal() - 0.5;
  362. for (j=0; j<3; j++) b[j] = a[0]*p[j] + a[1]*q[j] + (a[2]+d)*n[j];
  363. if (dFabs(dGeomPlanePointDepth (plane,b[0],b[1],b[2]) + a[2]) >= tol)
  364. FAILED();
  365. // ********** test depth-1 point
  366. a[0] = dRandReal() - 0.5;
  367. a[1] = dRandReal() - 0.5;
  368. a[2] = -1;
  369. for (j=0; j<3; j++) b[j] = a[0]*p[j] + a[1]*q[j] + (a[2]+d)*n[j];
  370. if (dFabs(dGeomPlanePointDepth (plane,b[0],b[1],b[2]) - 1) >= tol) FAILED();
  371. PASSED();
  372. }
  373. //****************************************************************************
  374. // ray tests
  375. int test_ray_and_sphere()
  376. {
  377. int j;
  378. dContactGeom contact;
  379. dVector3 p,q,q2,n,v1;
  380. dMatrix3 R;
  381. dReal r,k;
  382. dSimpleSpace space(0);
  383. dGeomID ray = dCreateRay (0,0);
  384. dGeomID sphere = dCreateSphere (0,1);
  385. dSpaceAdd (space,ray);
  386. dSpaceAdd (space,sphere);
  387. // ********** make a random sphere of radius r at position p
  388. r = dRandReal()+0.1;
  389. dGeomSphereSetRadius (sphere,r);
  390. dMakeRandomVector (p,3,1.0);
  391. dGeomSetPosition (sphere,p[0],p[1],p[2]);
  392. dRFromAxisAndAngle (R,dRandReal()*2-1,dRandReal()*2-1,
  393. dRandReal()*2-1,dRandReal()*10-5);
  394. dGeomSetRotation (sphere,R);
  395. // ********** test zero length ray just inside sphere
  396. dGeomRaySetLength (ray,0);
  397. dMakeRandomVector (q,3,1.0);
  398. dNormalize3 (q);
  399. for (j=0; j<3; j++) q[j] = 0.99*r * q[j] + p[j];
  400. dGeomSetPosition (ray,q[0],q[1],q[2]);
  401. dRFromAxisAndAngle (R,dRandReal()*2-1,dRandReal()*2-1,
  402. dRandReal()*2-1,dRandReal()*10-5);
  403. dGeomSetRotation (ray,R);
  404. if (dCollide (ray,sphere,1,&contact,sizeof(dContactGeom)) != 0) FAILED();
  405. // ********** test zero length ray just outside that sphere
  406. dGeomRaySetLength (ray,0);
  407. dMakeRandomVector (q,3,1.0);
  408. dNormalize3 (q);
  409. for (j=0; j<3; j++) q[j] = 1.01*r * q[j] + p[j];
  410. dGeomSetPosition (ray,q[0],q[1],q[2]);
  411. dRFromAxisAndAngle (R,dRandReal()*2-1,dRandReal()*2-1,
  412. dRandReal()*2-1,dRandReal()*10-5);
  413. dGeomSetRotation (ray,R);
  414. if (dCollide (ray,sphere,1,&contact,sizeof(dContactGeom)) != 0) FAILED();
  415. // ********** test finite length ray totally contained inside the sphere
  416. dMakeRandomVector (q,3,1.0);
  417. dNormalize3 (q);
  418. k = dRandReal();
  419. for (j=0; j<3; j++) q[j] = k*r*0.99 * q[j] + p[j];
  420. dMakeRandomVector (q2,3,1.0);
  421. dNormalize3 (q2);
  422. k = dRandReal();
  423. for (j=0; j<3; j++) q2[j] = k*r*0.99 * q2[j] + p[j];
  424. for (j=0; j<3; j++) n[j] = q2[j] - q[j];
  425. dNormalize3 (n);
  426. dGeomRaySet (ray,q[0],q[1],q[2],n[0],n[1],n[2]);
  427. dGeomRaySetLength (ray,dCalcPointsDistance3(q,q2));
  428. if (dCollide (ray,sphere,1,&contact,sizeof(dContactGeom)) != 0) FAILED();
  429. // ********** test finite length ray totally outside the sphere
  430. dMakeRandomVector (q,3,1.0);
  431. dNormalize3 (q);
  432. do {
  433. dMakeRandomVector (n,3,1.0);
  434. dNormalize3 (n);
  435. }
  436. while (dCalcVectorDot3(n,q) < 0); // make sure normal goes away from sphere
  437. for (j=0; j<3; j++) q[j] = 1.01*r * q[j] + p[j];
  438. dGeomRaySet (ray,q[0],q[1],q[2],n[0],n[1],n[2]);
  439. dGeomRaySetLength (ray,100);
  440. if (dCollide (ray,sphere,1,&contact,sizeof(dContactGeom)) != 0) FAILED();
  441. // ********** test ray from outside to just above surface
  442. dMakeRandomVector (q,3,1.0);
  443. dNormalize3 (q);
  444. for (j=0; j<3; j++) n[j] = -q[j];
  445. for (j=0; j<3; j++) q2[j] = 2*r * q[j] + p[j];
  446. dGeomRaySet (ray,q2[0],q2[1],q2[2],n[0],n[1],n[2]);
  447. dGeomRaySetLength (ray,0.99*r);
  448. if (dCollide (ray,sphere,1,&contact,sizeof(dContactGeom)) != 0) FAILED();
  449. // ********** test ray from outside to just below surface
  450. dGeomRaySetLength (ray,1.01*r);
  451. if (dCollide (ray,sphere,1,&contact,sizeof(dContactGeom)) != 1) FAILED();
  452. for (j=0; j<3; j++) q2[j] = r * q[j] + p[j];
  453. if (dCalcPointsDistance3 (contact.pos,q2) > tol) FAILED();
  454. // ********** test contact point distance for random rays
  455. dMakeRandomVector (q,3,1.0);
  456. dNormalize3 (q);
  457. k = dRandReal()+0.5;
  458. for (j=0; j<3; j++) q[j] = k*r * q[j] + p[j];
  459. dMakeRandomVector (n,3,1.0);
  460. dNormalize3 (n);
  461. dGeomRaySet (ray,q[0],q[1],q[2],n[0],n[1],n[2]);
  462. dGeomRaySetLength (ray,100);
  463. if (dCollide (ray,sphere,1,&contact,sizeof(dContactGeom))) {
  464. k = dCalcPointsDistance3 (contact.pos,dGeomGetPosition(sphere));
  465. if (dFabs(k - r) > tol) FAILED();
  466. // also check normal signs
  467. if (dCalcVectorDot3 (n,contact.normal) > 0) FAILED();
  468. // also check depth of contact point
  469. if (dFabs (dGeomSpherePointDepth
  470. (sphere,contact.pos[0],contact.pos[1],contact.pos[2])) > tol)
  471. FAILED();
  472. draw_all_objects (space);
  473. }
  474. // ********** test tangential grazing - miss
  475. dMakeRandomVector (q,3,1.0);
  476. dNormalize3 (q);
  477. dPlaneSpace (q,n,v1);
  478. for (j=0; j<3; j++) q[j] = 1.01*r * q[j] + p[j];
  479. for (j=0; j<3; j++) q[j] -= n[j];
  480. dGeomRaySet (ray,q[0],q[1],q[2],n[0],n[1],n[2]);
  481. dGeomRaySetLength (ray,2);
  482. if (dCollide (ray,sphere,1,&contact,sizeof(dContactGeom)) != 0) FAILED();
  483. // ********** test tangential grazing - hit
  484. dMakeRandomVector (q,3,1.0);
  485. dNormalize3 (q);
  486. dPlaneSpace (q,n,v1);
  487. for (j=0; j<3; j++) q[j] = 0.99*r * q[j] + p[j];
  488. for (j=0; j<3; j++) q[j] -= n[j];
  489. dGeomRaySet (ray,q[0],q[1],q[2],n[0],n[1],n[2]);
  490. dGeomRaySetLength (ray,2);
  491. if (dCollide (ray,sphere,1,&contact,sizeof(dContactGeom)) != 1) FAILED();
  492. PASSED();
  493. }
  494. int test_ray_and_box()
  495. {
  496. int i,j;
  497. dContactGeom contact;
  498. dVector3 s,p,q,n,q2,q3,q4; // s = box sides
  499. dMatrix3 R;
  500. dReal k;
  501. dSimpleSpace space(0);
  502. dGeomID ray = dCreateRay (0,0);
  503. dGeomID box = dCreateBox (0,1,1,1);
  504. dSpaceAdd (space,ray);
  505. dSpaceAdd (space,box);
  506. // ********** make a random box
  507. for (j=0; j<3; j++) s[j] = dRandReal() + 0.1;
  508. dGeomBoxSetLengths (box,s[0],s[1],s[2]);
  509. dMakeRandomVector (p,3,1.0);
  510. dGeomSetPosition (box,p[0],p[1],p[2]);
  511. dRFromAxisAndAngle (R,dRandReal()*2-1,dRandReal()*2-1,
  512. dRandReal()*2-1,dRandReal()*10-5);
  513. dGeomSetRotation (box,R);
  514. // ********** test zero length ray just inside box
  515. dGeomRaySetLength (ray,0);
  516. for (j=0; j<3; j++) q[j] = (dRandReal()-0.5)*s[j];
  517. i = dRandInt (3);
  518. if (dRandReal() > 0.5) q[i] = 0.99*0.5*s[i]; else q[i] = -0.99*0.5*s[i];
  519. dMultiply0 (q2,dGeomGetRotation(box),q,3,3,1);
  520. for (j=0; j<3; j++) q2[j] += p[j];
  521. dGeomSetPosition (ray,q2[0],q2[1],q2[2]);
  522. dRFromAxisAndAngle (R,dRandReal()*2-1,dRandReal()*2-1,
  523. dRandReal()*2-1,dRandReal()*10-5);
  524. dGeomSetRotation (ray,R);
  525. if (dCollide (ray,box,1,&contact,sizeof(dContactGeom)) != 0) FAILED();
  526. // ********** test zero length ray just outside box
  527. dGeomRaySetLength (ray,0);
  528. for (j=0; j<3; j++) q[j] = (dRandReal()-0.5)*s[j];
  529. i = dRandInt (3);
  530. if (dRandReal() > 0.5) q[i] = 1.01*0.5*s[i]; else q[i] = -1.01*0.5*s[i];
  531. dMultiply0 (q2,dGeomGetRotation(box),q,3,3,1);
  532. for (j=0; j<3; j++) q2[j] += p[j];
  533. dGeomSetPosition (ray,q2[0],q2[1],q2[2]);
  534. dRFromAxisAndAngle (R,dRandReal()*2-1,dRandReal()*2-1,
  535. dRandReal()*2-1,dRandReal()*10-5);
  536. dGeomSetRotation (ray,R);
  537. if (dCollide (ray,box,1,&contact,sizeof(dContactGeom)) != 0) FAILED();
  538. // ********** test finite length ray totally contained inside the box
  539. for (j=0; j<3; j++) q[j] = (dRandReal()-0.5)*0.99*s[j];
  540. dMultiply0 (q2,dGeomGetRotation(box),q,3,3,1);
  541. for (j=0; j<3; j++) q2[j] += p[j];
  542. for (j=0; j<3; j++) q3[j] = (dRandReal()-0.5)*0.99*s[j];
  543. dMultiply0 (q4,dGeomGetRotation(box),q3,3,3,1);
  544. for (j=0; j<3; j++) q4[j] += p[j];
  545. for (j=0; j<3; j++) n[j] = q4[j] - q2[j];
  546. dNormalize3 (n);
  547. dGeomRaySet (ray,q2[0],q2[1],q2[2],n[0],n[1],n[2]);
  548. dGeomRaySetLength (ray,dCalcPointsDistance3(q2,q4));
  549. if (dCollide (ray,box,1,&contact,sizeof(dContactGeom)) != 0) FAILED();
  550. // ********** test finite length ray totally outside the box
  551. for (j=0; j<3; j++) q[j] = (dRandReal()-0.5)*s[j];
  552. i = dRandInt (3);
  553. if (dRandReal() > 0.5) q[i] = 1.01*0.5*s[i]; else q[i] = -1.01*0.5*s[i];
  554. dMultiply0 (q2,dGeomGetRotation(box),q,3,3,1);
  555. for (j=0; j<3; j++) q3[j] = q2[j] + p[j];
  556. dNormalize3 (q2);
  557. dGeomRaySet (ray,q3[0],q3[1],q3[2],q2[0],q2[1],q2[2]);
  558. dGeomRaySetLength (ray,10);
  559. if (dCollide (ray,box,1,&contact,sizeof(dContactGeom)) != 0) FAILED();
  560. // ********** test ray from outside to just above surface
  561. for (j=0; j<3; j++) q[j] = (dRandReal()-0.5)*s[j];
  562. i = dRandInt (3);
  563. if (dRandReal() > 0.5) q[i] = 1.01*0.5*s[i]; else q[i] = -1.01*0.5*s[i];
  564. dMultiply0 (q2,dGeomGetRotation(box),q,3,3,1);
  565. for (j=0; j<3; j++) q3[j] = 2*q2[j] + p[j];
  566. k = dSqrt(q2[0]*q2[0] + q2[1]*q2[1] + q2[2]*q2[2]);
  567. for (j=0; j<3; j++) q2[j] = -q2[j];
  568. dGeomRaySet (ray,q3[0],q3[1],q3[2],q2[0],q2[1],q2[2]);
  569. dGeomRaySetLength (ray,k*0.99);
  570. if (dCollide (ray,box,1,&contact,sizeof(dContactGeom)) != 0) FAILED();
  571. // ********** test ray from outside to just below surface
  572. dGeomRaySetLength (ray,k*1.01);
  573. if (dCollide (ray,box,1,&contact,sizeof(dContactGeom)) != 1) FAILED();
  574. // ********** test contact point position for random rays
  575. for (j=0; j<3; j++) q[j] = dRandReal()*s[j];
  576. dMultiply0 (q2,dGeomGetRotation(box),q,3,3,1);
  577. for (j=0; j<3; j++) q2[j] += p[j];
  578. for (j=0; j<3; j++) q3[j] = dRandReal()-0.5;
  579. dNormalize3 (q3);
  580. dGeomRaySet (ray,q2[0],q2[1],q2[2],q3[0],q3[1],q3[2]);
  581. dGeomRaySetLength (ray,10);
  582. if (dCollide (ray,box,1,&contact,sizeof(dContactGeom))) {
  583. // check depth of contact point
  584. if (dFabs (dGeomBoxPointDepth
  585. (box,contact.pos[0],contact.pos[1],contact.pos[2])) > tol)
  586. FAILED();
  587. // check position of contact point
  588. for (j=0; j<3; j++) contact.pos[j] -= p[j];
  589. dMultiply1 (q,dGeomGetRotation(box),contact.pos,3,3,1);
  590. if ( dFabs(dFabs (q[0]) - 0.5*s[0]) > tol &&
  591. dFabs(dFabs (q[1]) - 0.5*s[1]) > tol &&
  592. dFabs(dFabs (q[2]) - 0.5*s[2]) > tol) {
  593. FAILED();
  594. }
  595. // also check normal signs
  596. if (dCalcVectorDot3 (q3,contact.normal) > 0) FAILED();
  597. draw_all_objects (space);
  598. }
  599. PASSED();
  600. }
  601. int test_ray_and_ccylinder()
  602. {
  603. int j;
  604. dContactGeom contact;
  605. dVector3 p,a,b,n;
  606. dMatrix3 R;
  607. dReal r,l,k,x,y;
  608. dSimpleSpace space(0);
  609. dGeomID ray = dCreateRay (0,0);
  610. dGeomID ccyl = dCreateCapsule (0,1,1);
  611. dSpaceAdd (space,ray);
  612. dSpaceAdd (space,ccyl);
  613. // ********** make a random capped cylinder
  614. r = dRandReal()*0.5 + 0.01;
  615. l = dRandReal()*1 + 0.01;
  616. dGeomCapsuleSetParams (ccyl,r,l);
  617. dMakeRandomVector (p,3,1.0);
  618. dGeomSetPosition (ccyl,p[0],p[1],p[2]);
  619. dRFromAxisAndAngle (R,dRandReal()*2-1,dRandReal()*2-1,
  620. dRandReal()*2-1,dRandReal()*10-5);
  621. dGeomSetRotation (ccyl,R);
  622. // ********** test ray completely within ccyl
  623. for (j=0; j<3; j++) a[j] = dRandReal()-0.5;
  624. dNormalize3 (a);
  625. k = (dRandReal()-0.5)*l;
  626. for (j=0; j<3; j++) a[j] = p[j] + r*0.99*a[j] + k*0.99*R[j*4+2];
  627. for (j=0; j<3; j++) b[j] = dRandReal()-0.5;
  628. dNormalize3 (b);
  629. k = (dRandReal()-0.5)*l;
  630. for (j=0; j<3; j++) b[j] = p[j] + r*0.99*b[j] + k*0.99*R[j*4+2];
  631. dGeomRaySetLength (ray,dCalcPointsDistance3(a,b));
  632. for (j=0; j<3; j++) b[j] -= a[j];
  633. dNormalize3 (b);
  634. dGeomRaySet (ray,a[0],a[1],a[2],b[0],b[1],b[2]);
  635. if (dCollide (ray,ccyl,1,&contact,sizeof(dContactGeom)) != 0) FAILED();
  636. // ********** test ray outside ccyl that just misses (between caps)
  637. k = dRandReal()*2*M_PI;
  638. x = sin(k);
  639. y = cos(k);
  640. for (j=0; j<3; j++) a[j] = x*R[j*4+0] + y*R[j*4+1];
  641. k = (dRandReal()-0.5)*l;
  642. for (j=0; j<3; j++) b[j] = -a[j]*r*2 + k*R[j*4+2] + p[j];
  643. dGeomRaySet (ray,b[0],b[1],b[2],a[0],a[1],a[2]);
  644. dGeomRaySetLength (ray,r*0.99);
  645. if (dCollide (ray,ccyl,1,&contact,sizeof(dContactGeom)) != 0) FAILED();
  646. // ********** test ray outside ccyl that just hits (between caps)
  647. dGeomRaySetLength (ray,r*1.01);
  648. if (dCollide (ray,ccyl,1,&contact,sizeof(dContactGeom)) != 1) FAILED();
  649. // check depth of contact point
  650. if (dFabs (dGeomCapsulePointDepth
  651. (ccyl,contact.pos[0],contact.pos[1],contact.pos[2])) > tol)
  652. FAILED();
  653. // ********** test ray outside ccyl that just misses (caps)
  654. for (j=0; j<3; j++) a[j] = dRandReal()-0.5;
  655. dNormalize3 (a);
  656. if (dCalcVectorDot3_14(a,R+2) < 0) {
  657. for (j=0; j<3; j++) b[j] = p[j] - a[j]*2*r + l*0.5*R[j*4+2];
  658. }
  659. else {
  660. for (j=0; j<3; j++) b[j] = p[j] - a[j]*2*r - l*0.5*R[j*4+2];
  661. }
  662. dGeomRaySet (ray,b[0],b[1],b[2],a[0],a[1],a[2]);
  663. dGeomRaySetLength (ray,r*0.99);
  664. if (dCollide (ray,ccyl,1,&contact,sizeof(dContactGeom)) != 0) FAILED();
  665. // ********** test ray outside ccyl that just hits (caps)
  666. dGeomRaySetLength (ray,r*1.01);
  667. if (dCollide (ray,ccyl,1,&contact,sizeof(dContactGeom)) != 1) FAILED();
  668. // check depth of contact point
  669. if (dFabs (dGeomCapsulePointDepth
  670. (ccyl,contact.pos[0],contact.pos[1],contact.pos[2])) > tol)
  671. FAILED();
  672. // ********** test random rays
  673. for (j=0; j<3; j++) a[j] = dRandReal()-0.5;
  674. for (j=0; j<3; j++) n[j] = dRandReal()-0.5;
  675. dNormalize3 (n);
  676. dGeomRaySet (ray,a[0],a[1],a[2],n[0],n[1],n[2]);
  677. dGeomRaySetLength (ray,10);
  678. if (dCollide (ray,ccyl,1,&contact,sizeof(dContactGeom))) {
  679. // check depth of contact point
  680. if (dFabs (dGeomCapsulePointDepth
  681. (ccyl,contact.pos[0],contact.pos[1],contact.pos[2])) > tol)
  682. FAILED();
  683. // check normal signs
  684. if (dCalcVectorDot3 (n,contact.normal) > 0) FAILED();
  685. draw_all_objects (space);
  686. }
  687. PASSED();
  688. }
  689. int test_ray_and_plane()
  690. {
  691. int j;
  692. dContactGeom contact;
  693. dVector3 n,p,q,a,b,g,h; // n,d = plane parameters
  694. dMatrix3 R;
  695. dReal d;
  696. dSimpleSpace space(0);
  697. dGeomID ray = dCreateRay (0,0);
  698. dGeomID plane = dCreatePlane (0,0,0,1,0);
  699. dSpaceAdd (space,ray);
  700. dSpaceAdd (space,plane);
  701. // ********** make a random plane
  702. for (j=0; j<3; j++) n[j] = dRandReal() - 0.5;
  703. dNormalize3 (n);
  704. d = dRandReal() - 0.5;
  705. dGeomPlaneSetParams (plane,n[0],n[1],n[2],d);
  706. dPlaneSpace (n,p,q);
  707. // ********** test finite length ray below plane
  708. dGeomRaySetLength (ray,0.09);
  709. a[0] = dRandReal()-0.5;
  710. a[1] = dRandReal()-0.5;
  711. a[2] = -dRandReal()*0.5 - 0.1;
  712. for (j=0; j<3; j++) b[j] = a[0]*p[j] + a[1]*q[j] + (a[2]+d)*n[j];
  713. dGeomSetPosition (ray,b[0],b[1],b[2]);
  714. dRFromAxisAndAngle (R,dRandReal()*2-1,dRandReal()*2-1,
  715. dRandReal()*2-1,dRandReal()*10-5);
  716. dGeomSetRotation (ray,R);
  717. if (dCollide (ray,plane,1,&contact,sizeof(dContactGeom)) != 0) FAILED();
  718. // ********** test finite length ray above plane
  719. a[0] = dRandReal()-0.5;
  720. a[1] = dRandReal()-0.5;
  721. a[2] = dRandReal()*0.5 + 0.01;
  722. for (j=0; j<3; j++) b[j] = a[0]*p[j] + a[1]*q[j] + (a[2]+d)*n[j];
  723. g[0] = dRandReal()-0.5;
  724. g[1] = dRandReal()-0.5;
  725. g[2] = dRandReal() + 0.01;
  726. for (j=0; j<3; j++) h[j] = g[0]*p[j] + g[1]*q[j] + g[2]*n[j];
  727. dNormalize3 (h);
  728. dGeomRaySet (ray,b[0],b[1],b[2],h[0],h[1],h[2]);
  729. dGeomRaySetLength (ray,10);
  730. if (dCollide (ray,plane,1,&contact,sizeof(dContactGeom)) != 0) FAILED();
  731. // ********** test finite length ray that intersects plane
  732. a[0] = dRandReal()-0.5;
  733. a[1] = dRandReal()-0.5;
  734. a[2] = dRandReal()-0.5;
  735. for (j=0; j<3; j++) b[j] = a[0]*p[j] + a[1]*q[j] + (a[2]+d)*n[j];
  736. g[0] = dRandReal()-0.5;
  737. g[1] = dRandReal()-0.5;
  738. g[2] = dRandReal()-0.5;
  739. for (j=0; j<3; j++) h[j] = g[0]*p[j] + g[1]*q[j] + g[2]*n[j];
  740. dNormalize3 (h);
  741. dGeomRaySet (ray,b[0],b[1],b[2],h[0],h[1],h[2]);
  742. dGeomRaySetLength (ray,10);
  743. if (dCollide (ray,plane,1,&contact,sizeof(dContactGeom))) {
  744. // test that contact is on plane surface
  745. if (dFabs (dCalcVectorDot3(contact.pos,n) - d) > tol) FAILED();
  746. // also check normal signs
  747. if (dCalcVectorDot3 (h,contact.normal) > 0) FAILED();
  748. // also check contact point depth
  749. if (dFabs (dGeomPlanePointDepth
  750. (plane,contact.pos[0],contact.pos[1],contact.pos[2])) > tol)
  751. FAILED();
  752. draw_all_objects (space);
  753. }
  754. // ********** test ray that just misses
  755. for (j=0; j<3; j++) b[j] = (1+d)*n[j];
  756. for (j=0; j<3; j++) h[j] = -n[j];
  757. dGeomRaySet (ray,b[0],b[1],b[2],h[0],h[1],h[2]);
  758. dGeomRaySetLength (ray,0.99);
  759. if (dCollide (ray,plane,1,&contact,sizeof(dContactGeom)) != 0) FAILED();
  760. // ********** test ray that just hits
  761. dGeomRaySetLength (ray,1.01);
  762. if (dCollide (ray,plane,1,&contact,sizeof(dContactGeom)) != 1) FAILED();
  763. // ********** test polarity with typical ground plane
  764. dGeomPlaneSetParams (plane,0,0,1,0);
  765. for (j=0; j<3; j++) a[j] = 0.1;
  766. for (j=0; j<3; j++) b[j] = 0;
  767. a[2] = 1;
  768. b[2] = -1;
  769. dGeomRaySet (ray,a[0],a[1],a[2],b[0],b[1],b[2]);
  770. dGeomRaySetLength (ray,2);
  771. if (dCollide (ray,plane,1,&contact,sizeof(dContactGeom)) != 1) FAILED();
  772. if (dFabs (contact.depth - 1) > tol) FAILED();
  773. a[2] = -1;
  774. b[2] = 1;
  775. dGeomRaySet (ray,a[0],a[1],a[2],b[0],b[1],b[2]);
  776. if (dCollide (ray,plane,1,&contact,sizeof(dContactGeom)) != 1) FAILED();
  777. if (dFabs (contact.depth - 1) > tol) FAILED();
  778. PASSED();
  779. }
  780. //****************************************************************************
  781. // a really inefficient, but hopefully correct implementation of
  782. // dBoxTouchesBox(), that does 144 edge-face tests.
  783. // return 1 if edge v1 -> v2 hits the rectangle described by p1,p2,p3
  784. static int edgeIntersectsRect (dVector3 v1, dVector3 v2,
  785. dVector3 p1, dVector3 p2, dVector3 p3)
  786. {
  787. int k;
  788. dVector3 u1,u2,n,tmp;
  789. for (k=0; k<3; k++) u1[k] = p3[k]-p1[k];
  790. for (k=0; k<3; k++) u2[k] = p2[k]-p1[k];
  791. dReal d1 = dSqrt(dCalcVectorDot3(u1,u1));
  792. dReal d2 = dSqrt(dCalcVectorDot3(u2,u2));
  793. dNormalize3 (u1);
  794. dNormalize3 (u2);
  795. if (dFabs(dCalcVectorDot3(u1,u2)) > 1e-6) dDebug (0,"bad u1/u2");
  796. dCalcVectorCross3(n,u1,u2);
  797. for (k=0; k<3; k++) tmp[k] = v2[k]-v1[k];
  798. dReal d = -dCalcVectorDot3(n,p1);
  799. if (dFabs(dCalcVectorDot3(n,p1)+d) > 1e-8) dDebug (0,"bad n wrt p1");
  800. if (dFabs(dCalcVectorDot3(n,p2)+d) > 1e-8) dDebug (0,"bad n wrt p2");
  801. if (dFabs(dCalcVectorDot3(n,p3)+d) > 1e-8) dDebug (0,"bad n wrt p3");
  802. dReal alpha = -(d+dCalcVectorDot3(n,v1))/dCalcVectorDot3(n,tmp);
  803. for (k=0; k<3; k++) tmp[k] = v1[k]+alpha*(v2[k]-v1[k]);
  804. if (dFabs(dCalcVectorDot3(n,tmp)+d) > 1e-6) dDebug (0,"bad tmp");
  805. if (alpha < 0) return 0;
  806. if (alpha > 1) return 0;
  807. for (k=0; k<3; k++) tmp[k] -= p1[k];
  808. dReal a1 = dCalcVectorDot3(u1,tmp);
  809. dReal a2 = dCalcVectorDot3(u2,tmp);
  810. if (a1<0 || a2<0 || a1>d1 || a2>d2) return 0;
  811. return 1;
  812. }
  813. // return 1 if box 1 is completely inside box 2
  814. static int box1inside2 (const dVector3 p1, const dMatrix3 R1,
  815. const dVector3 side1, const dVector3 p2,
  816. const dMatrix3 R2, const dVector3 side2)
  817. {
  818. for (int i=-1; i<=1; i+=2) {
  819. for (int j=-1; j<=1; j+=2) {
  820. for (int k=-1; k<=1; k+=2) {
  821. dVector3 v,vv;
  822. v[0] = i*0.5*side1[0];
  823. v[1] = j*0.5*side1[1];
  824. v[2] = k*0.5*side1[2];
  825. dMultiply0_331 (vv,R1,v);
  826. vv[0] += p1[0] - p2[0];
  827. vv[1] += p1[1] - p2[1];
  828. vv[2] += p1[2] - p2[2];
  829. for (int axis=0; axis < 3; axis++) {
  830. dReal z = dCalcVectorDot3_14(vv,R2+axis);
  831. if (z < (-side2[axis]*0.5) || z > (side2[axis]*0.5)) return 0;
  832. }
  833. }
  834. }
  835. }
  836. return 1;
  837. }
  838. // test if any edge from box 1 hits a face from box 2
  839. static int testBoxesTouch2 (const dVector3 p1, const dMatrix3 R1,
  840. const dVector3 side1, const dVector3 p2,
  841. const dMatrix3 R2, const dVector3 side2)
  842. {
  843. int j,k,j1,j2;
  844. // for 6 faces from box 2
  845. for (int fd=0; fd<3; fd++) { // direction for face
  846. for (int fo=0; fo<2; fo++) { // offset of face
  847. // get four points on the face. first get 2 indexes that are not fd
  848. int k1=0,k2=0;
  849. if (fd==0) { k1 = 1; k2 = 2; }
  850. if (fd==1) { k1 = 0; k2 = 2; }
  851. if (fd==2) { k1 = 0; k2 = 1; }
  852. dVector3 fp[4],tmp;
  853. k=0;
  854. for (j1=-1; j1<=1; j1+=2) {
  855. for (j2=-1; j2<=1; j2+=2) {
  856. fp[k][k1] = j1;
  857. fp[k][k2] = j2;
  858. fp[k][fd] = fo*2-1;
  859. k++;
  860. }
  861. }
  862. for (j=0; j<4; j++) {
  863. for (k=0; k<3; k++) fp[j][k] *= 0.5*side2[k];
  864. dMultiply0_331 (tmp,R2,fp[j]);
  865. for (k=0; k<3; k++) fp[j][k] = tmp[k] + p2[k];
  866. }
  867. // for 8 vertices
  868. dReal v1[3];
  869. for (v1[0]=-1; v1[0] <= 1; v1[0] += 2) {
  870. for (v1[1]=-1; v1[1] <= 1; v1[1] += 2) {
  871. for (v1[2]=-1; v1[2] <= 1; v1[2] += 2) {
  872. // for all possible +ve leading edges from those vertices
  873. for (int ei=0; ei < 3; ei ++) {
  874. if (v1[ei] < 0) {
  875. // get vertex1 -> vertex2 = an edge from box 1
  876. dVector3 vv1,vv2;
  877. for (k=0; k<3; k++) vv1[k] = v1[k] * 0.5*side1[k];
  878. for (k=0; k<3; k++) vv2[k] = (v1[k] + (k==ei)*2)*0.5*side1[k];
  879. dVector3 vertex1,vertex2;
  880. dMultiply0_331 (vertex1,R1,vv1);
  881. dMultiply0_331 (vertex2,R1,vv2);
  882. for (k=0; k<3; k++) vertex1[k] += p1[k];
  883. for (k=0; k<3; k++) vertex2[k] += p1[k];
  884. // see if vertex1 -> vertex2 interesects face
  885. if (edgeIntersectsRect (vertex1,vertex2,fp[0],fp[1],fp[2]))
  886. return 1;
  887. }
  888. }
  889. }
  890. }
  891. }
  892. }
  893. }
  894. if (box1inside2 (p1,R1,side1,p2,R2,side2)) return 1;
  895. if (box1inside2 (p2,R2,side2,p1,R1,side1)) return 1;
  896. return 0;
  897. }
  898. //****************************************************************************
  899. // dBoxTouchesBox() test
  900. int test_dBoxTouchesBox()
  901. {
  902. int k,bt1,bt2;
  903. dVector3 p1,p2,side1,side2;
  904. dMatrix3 R1,R2;
  905. dSimpleSpace space(0);
  906. dGeomID box1 = dCreateBox (0,1,1,1);
  907. dSpaceAdd (space,box1);
  908. dGeomID box2 = dCreateBox (0,1,1,1);
  909. dSpaceAdd (space,box2);
  910. dMakeRandomVector (p1,3,0.5);
  911. dMakeRandomVector (p2,3,0.5);
  912. for (k=0; k<3; k++) side1[k] = dRandReal() + 0.01;
  913. for (k=0; k<3; k++) side2[k] = dRandReal() + 0.01;
  914. dRFromAxisAndAngle (R1,dRandReal()*2.0-1.0,dRandReal()*2.0-1.0,
  915. dRandReal()*2.0-1.0,dRandReal()*10.0-5.0);
  916. dRFromAxisAndAngle (R2,dRandReal()*2.0-1.0,dRandReal()*2.0-1.0,
  917. dRandReal()*2.0-1.0,dRandReal()*10.0-5.0);
  918. dGeomBoxSetLengths (box1,side1[0],side1[1],side1[2]);
  919. dGeomBoxSetLengths (box2,side2[0],side2[1],side2[2]);
  920. dGeomSetPosition (box1,p1[0],p1[1],p1[2]);
  921. dGeomSetRotation (box1,R1);
  922. dGeomSetPosition (box2,p2[0],p2[1],p2[2]);
  923. dGeomSetRotation (box2,R2);
  924. draw_all_objects (space);
  925. int t1 = testBoxesTouch2 (p1,R1,side1,p2,R2,side2);
  926. int t2 = testBoxesTouch2 (p2,R2,side2,p1,R1,side1);
  927. bt1 = t1 || t2;
  928. bt2 = dBoxTouchesBox (p1,R1,side1,p2,R2,side2);
  929. if (bt1 != bt2) FAILED();
  930. /*
  931. // some more debugging info if necessary
  932. if (bt1 && bt2) printf ("agree - boxes touch\n");
  933. if (!bt1 && !bt2) printf ("agree - boxes don't touch\n");
  934. if (bt1 && !bt2) printf ("disagree - boxes touch but dBoxTouchesBox "
  935. "says no\n");
  936. if (!bt1 && bt2) printf ("disagree - boxes don't touch but dBoxTouchesBox "
  937. "says yes\n");
  938. */
  939. PASSED();
  940. }
  941. //****************************************************************************
  942. // test box-box collision
  943. int test_dBoxBox()
  944. {
  945. int k,bt;
  946. dVector3 p1,p2,side1,side2,normal,normal2;
  947. dMatrix3 R1,R2;
  948. dReal depth,depth2;
  949. int code;
  950. dContactGeom contact[48];
  951. dSimpleSpace space(0);
  952. dGeomID box1 = dCreateBox (0,1,1,1);
  953. dSpaceAdd (space,box1);
  954. dGeomID box2 = dCreateBox (0,1,1,1);
  955. dSpaceAdd (space,box2);
  956. dMakeRandomVector (p1,3,0.5);
  957. dMakeRandomVector (p2,3,0.5);
  958. for (k=0; k<3; k++) side1[k] = dRandReal() + 0.01;
  959. for (k=0; k<3; k++) side2[k] = dRandReal() + 0.01;
  960. dRFromAxisAndAngle (R1,dRandReal()*2.0-1.0,dRandReal()*2.0-1.0,
  961. dRandReal()*2.0-1.0,dRandReal()*10.0-5.0);
  962. dRFromAxisAndAngle (R2,dRandReal()*2.0-1.0,dRandReal()*2.0-1.0,
  963. dRandReal()*2.0-1.0,dRandReal()*10.0-5.0);
  964. // dRSetIdentity (R1); // we can also try this
  965. // dRSetIdentity (R2);
  966. dGeomBoxSetLengths (box1,side1[0],side1[1],side1[2]);
  967. dGeomBoxSetLengths (box2,side2[0],side2[1],side2[2]);
  968. dGeomSetPosition (box1,p1[0],p1[1],p1[2]);
  969. dGeomSetRotation (box1,R1);
  970. dGeomSetPosition (box2,p2[0],p2[1],p2[2]);
  971. dGeomSetRotation (box2,R2);
  972. code = 0;
  973. depth = 0;
  974. bt = dBoxBox (p1,R1,side1,p2,R2,side2,normal,&depth,&code,8,contact,
  975. sizeof(dContactGeom));
  976. if (bt==1) {
  977. p2[0] += normal[0] * 0.96 * depth;
  978. p2[1] += normal[1] * 0.96 * depth;
  979. p2[2] += normal[2] * 0.96 * depth;
  980. bt = dBoxBox (p1,R1,side1,p2,R2,side2,normal2,&depth2,&code,8,contact,
  981. sizeof(dContactGeom));
  982. /*
  983. dGeomSetPosition (box2,p2[0],p2[1],p2[2]);
  984. draw_all_objects (space);
  985. */
  986. if (bt != 1) {
  987. FAILED();
  988. dGeomSetPosition (box2,p2[0],p2[1],p2[2]);
  989. draw_all_objects (space);
  990. }
  991. p2[0] += normal[0] * 0.08 * depth;
  992. p2[1] += normal[1] * 0.08 * depth;
  993. p2[2] += normal[2] * 0.08 * depth;
  994. bt = dBoxBox (p1,R1,side1,p2,R2,side2,normal2,&depth2,&code,8,contact,
  995. sizeof(dContactGeom));
  996. if (bt != 0) FAILED();
  997. // dGeomSetPosition (box2,p2[0],p2[1],p2[2]);
  998. // draw_all_objects (space);
  999. }
  1000. // printf ("code=%2d depth=%.4f ",code,depth);
  1001. PASSED();
  1002. }
  1003. //****************************************************************************
  1004. // graphics
  1005. int space_pressed = 0;
  1006. // start simulation - set viewpoint
  1007. static void start()
  1008. {
  1009. dAllocateODEDataForThread(dAllocateMaskAll);
  1010. static float xyz[3] = {2.4807,-1.8023,2.7600};
  1011. static float hpr[3] = {141.5000,-18.5000,0.0000};
  1012. dsSetViewpoint (xyz,hpr);
  1013. }
  1014. // called when a key pressed
  1015. static void command (int cmd)
  1016. {
  1017. if (cmd == ' ') space_pressed = 1;
  1018. }
  1019. // simulation loop
  1020. static void simLoop (int pause)
  1021. {
  1022. do {
  1023. draw_all_objects_called = 0;
  1024. unsigned long seed = dRandGetSeed();
  1025. testslot[graphical_test].test_fn();
  1026. if (draw_all_objects_called) {
  1027. if (space_pressed) space_pressed = 0; else dRandSetSeed (seed);
  1028. }
  1029. }
  1030. while (!draw_all_objects_called);
  1031. }
  1032. //****************************************************************************
  1033. // do all the tests
  1034. void do_tests (int argc, char **argv)
  1035. {
  1036. int i,j;
  1037. // process command line arguments
  1038. if (argc >= 2) {
  1039. graphical_test = atoi (argv[1]);
  1040. }
  1041. if (graphical_test) {
  1042. // do one test gaphically and interactively
  1043. if (graphical_test < 1 || graphical_test >= MAX_TESTS ||
  1044. !testslot[graphical_test].name) {
  1045. dError (0,"invalid test number");
  1046. }
  1047. printf ("performing test: %s\n",testslot[graphical_test].name);
  1048. // setup pointers to drawstuff callback functions
  1049. dsFunctions fn;
  1050. fn.version = DS_VERSION;
  1051. fn.start = &start;
  1052. fn.step = &simLoop;
  1053. fn.command = &command;
  1054. fn.stop = 0;
  1055. fn.path_to_textures = DRAWSTUFF_TEXTURE_PATH;
  1056. dsSetSphereQuality (3);
  1057. dsSetCapsuleQuality (8);
  1058. dsSimulationLoop (argc,argv,1280,900,&fn);
  1059. }
  1060. else {
  1061. // do all tests noninteractively
  1062. for (i=0; i<MAX_TESTS; i++) testslot[i].number = i;
  1063. // first put the active tests into a separate array
  1064. int n=0;
  1065. for (i=0; i<MAX_TESTS; i++) if (testslot[i].name) n++;
  1066. TestSlot **ts = (TestSlot**) malloc (n * sizeof(TestSlot*));
  1067. j = 0;
  1068. for (i=0; i<MAX_TESTS; i++) if (testslot[i].name) ts[j++] = testslot+i;
  1069. if (j != n) dDebug (0,"internal");
  1070. // do two test batches. the first test batch has far fewer reps and will
  1071. // catch problems quickly. if all tests in the first batch passes, the
  1072. // second batch is run.
  1073. for (i=0; i<n; i++) ts[i]->failcount = 0;
  1074. int total_reps=0;
  1075. for (int batch=0; batch<2; batch++) {
  1076. int reps = (batch==0) ? TEST_REPS1 : TEST_REPS2;
  1077. total_reps += reps;
  1078. printf ("testing batch %d (%d reps)...\n",batch+1,reps);
  1079. // run tests
  1080. for (j=0; j<reps; j++) {
  1081. for (i=0; i<n; i++) {
  1082. current_test = ts[i]->number;
  1083. if (ts[i]->test_fn() != 1) ts[i]->failcount++;
  1084. }
  1085. }
  1086. // check for failures
  1087. int total_fail_count=0;
  1088. for (i=0; i<n; i++) total_fail_count += ts[i]->failcount;
  1089. if (total_fail_count) break;
  1090. }
  1091. // print results
  1092. for (i=0; i<n; i++) {
  1093. printf ("%3d: %-30s: ",ts[i]->number,ts[i]->name);
  1094. if (ts[i]->failcount) {
  1095. printf ("FAILED (%.2f%%) at line %d\n",
  1096. double(ts[i]->failcount)/double(total_reps)*100.0,
  1097. ts[i]->last_failed_line);
  1098. }
  1099. else {
  1100. printf ("ok\n");
  1101. }
  1102. }
  1103. }
  1104. }
  1105. //****************************************************************************
  1106. int main (int argc, char **argv)
  1107. {
  1108. // setup all tests
  1109. memset (testslot,0,sizeof(testslot));
  1110. dInitODE2(0);
  1111. MAKE_TEST(1,test_sphere_point_depth);
  1112. MAKE_TEST(2,test_box_point_depth);
  1113. MAKE_TEST(3,test_ccylinder_point_depth);
  1114. MAKE_TEST(4,test_plane_point_depth);
  1115. MAKE_TEST(10,test_ray_and_sphere);
  1116. MAKE_TEST(11,test_ray_and_box);
  1117. MAKE_TEST(12,test_ray_and_ccylinder);
  1118. MAKE_TEST(13,test_ray_and_plane);
  1119. MAKE_TEST(100,test_dBoxTouchesBox);
  1120. MAKE_TEST(101,test_dBoxBox);
  1121. do_tests (argc,argv);
  1122. dCloseODE();
  1123. return 0;
  1124. }