demo_collision.cpp 48 KB

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