ray.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735
  1. /*************************************************************************
  2. * *
  3. * Open Dynamics Engine, Copyright (C) 2001-2003 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. standard ODE geometry primitives: public API and pairwise collision functions.
  24. the rule is that only the low level primitive collision functions should set
  25. dContactGeom::g1 and dContactGeom::g2.
  26. */
  27. #include <ode/common.h>
  28. #include <ode/collision.h>
  29. #include <ode/rotation.h>
  30. #include "config.h"
  31. #include "matrix.h"
  32. #include "odemath.h"
  33. #include "collision_kernel.h"
  34. #include "collision_std.h"
  35. #include "collision_util.h"
  36. #ifdef _MSC_VER
  37. #pragma warning(disable:4291) // for VC++, no complaints about "no matching operator delete found"
  38. #endif
  39. //****************************************************************************
  40. // ray public API
  41. dxRay::dxRay (dSpaceID space, dReal _length) : dxGeom (space,1)
  42. {
  43. type = dRayClass;
  44. length = _length;
  45. }
  46. void dxRay::computeAABB()
  47. {
  48. dVector3 e;
  49. e[0] = final_posr->pos[0] + final_posr->R[0*4+2]*length;
  50. e[1] = final_posr->pos[1] + final_posr->R[1*4+2]*length;
  51. e[2] = final_posr->pos[2] + final_posr->R[2*4+2]*length;
  52. if (final_posr->pos[0] < e[0]){
  53. aabb[0] = final_posr->pos[0];
  54. aabb[1] = e[0];
  55. }
  56. else{
  57. aabb[0] = e[0];
  58. aabb[1] = final_posr->pos[0];
  59. }
  60. if (final_posr->pos[1] < e[1]){
  61. aabb[2] = final_posr->pos[1];
  62. aabb[3] = e[1];
  63. }
  64. else{
  65. aabb[2] = e[1];
  66. aabb[3] = final_posr->pos[1];
  67. }
  68. if (final_posr->pos[2] < e[2]){
  69. aabb[4] = final_posr->pos[2];
  70. aabb[5] = e[2];
  71. }
  72. else{
  73. aabb[4] = e[2];
  74. aabb[5] = final_posr->pos[2];
  75. }
  76. }
  77. dGeomID dCreateRay (dSpaceID space, dReal length)
  78. {
  79. return new dxRay (space,length);
  80. }
  81. void dGeomRaySetLength (dGeomID g, dReal length)
  82. {
  83. dUASSERT (g && g->type == dRayClass,"argument not a ray");
  84. dxRay *r = (dxRay*) g;
  85. r->length = length;
  86. dGeomMoved (g);
  87. }
  88. dReal dGeomRayGetLength (dGeomID g)
  89. {
  90. dUASSERT (g && g->type == dRayClass,"argument not a ray");
  91. dxRay *r = (dxRay*) g;
  92. return r->length;
  93. }
  94. void dGeomRaySet (dGeomID g, dReal px, dReal py, dReal pz,
  95. dReal dx, dReal dy, dReal dz)
  96. {
  97. dUASSERT (g && g->type == dRayClass,"argument not a ray");
  98. g->recomputePosr();
  99. dReal* rot = g->final_posr->R;
  100. dReal* pos = g->final_posr->pos;
  101. dVector3 n;
  102. pos[0] = px;
  103. pos[1] = py;
  104. pos[2] = pz;
  105. n[0] = dx;
  106. n[1] = dy;
  107. n[2] = dz;
  108. dNormalize3(n);
  109. rot[0*4+2] = n[0];
  110. rot[1*4+2] = n[1];
  111. rot[2*4+2] = n[2];
  112. dGeomMoved (g);
  113. }
  114. void dGeomRayGet (dGeomID g, dVector3 start, dVector3 dir)
  115. {
  116. dUASSERT (g && g->type == dRayClass,"argument not a ray");
  117. g->recomputePosr();
  118. start[0] = g->final_posr->pos[0];
  119. start[1] = g->final_posr->pos[1];
  120. start[2] = g->final_posr->pos[2];
  121. dir[0] = g->final_posr->R[0*4+2];
  122. dir[1] = g->final_posr->R[1*4+2];
  123. dir[2] = g->final_posr->R[2*4+2];
  124. }
  125. void dGeomRaySetParams (dxGeom *g, int FirstContact, int BackfaceCull)
  126. {
  127. dUASSERT (g && g->type == dRayClass,"argument not a ray");
  128. dGeomRaySetFirstContact(g, FirstContact);
  129. dGeomRaySetBackfaceCull(g, BackfaceCull);
  130. }
  131. void dGeomRayGetParams (dxGeom *g, int *FirstContact, int *BackfaceCull)
  132. {
  133. dUASSERT (g && g->type == dRayClass,"argument not a ray");
  134. (*FirstContact) = ((g->gflags & RAY_FIRSTCONTACT) != 0);
  135. (*BackfaceCull) = ((g->gflags & RAY_BACKFACECULL) != 0);
  136. }
  137. // set/get backface culling flag
  138. void dGeomRaySetBackfaceCull (dxGeom *g, int backfaceCull)
  139. {
  140. dUASSERT (g && g->type == dRayClass,"argument not a ray");
  141. if (backfaceCull) {
  142. g->gflags |= RAY_BACKFACECULL;
  143. } else {
  144. g->gflags &= ~RAY_BACKFACECULL;
  145. }
  146. }
  147. int dGeomRayGetBackfaceCull (dxGeom *g)
  148. {
  149. dUASSERT (g && g->type == dRayClass,"argument not a ray");
  150. return ((g->gflags & RAY_BACKFACECULL) != 0);
  151. }
  152. // set/get first contact flag
  153. void dGeomRaySetFirstContact (dxGeom *g, int firstContact)
  154. {
  155. dUASSERT (g && g->type == dRayClass,"argument not a ray");
  156. if (firstContact) {
  157. g->gflags |= RAY_FIRSTCONTACT;
  158. } else {
  159. g->gflags &= ~RAY_FIRSTCONTACT;
  160. }
  161. }
  162. int dGeomRayGetFirstContact (dxGeom *g)
  163. {
  164. dUASSERT (g && g->type == dRayClass,"argument not a ray");
  165. return ((g->gflags & RAY_FIRSTCONTACT) != 0);
  166. }
  167. void dGeomRaySetClosestHit (dxGeom *g, int closestHit)
  168. {
  169. dUASSERT (g && g->type == dRayClass,"argument not a ray");
  170. if (closestHit){
  171. g->gflags |= RAY_CLOSEST_HIT;
  172. }
  173. else g->gflags &= ~RAY_CLOSEST_HIT;
  174. }
  175. int dGeomRayGetClosestHit (dxGeom *g)
  176. {
  177. dUASSERT (g && g->type == dRayClass,"argument not a ray");
  178. return ((g->gflags & RAY_CLOSEST_HIT) != 0);
  179. }
  180. // if mode==1 then use the sphere exit contact, not the entry contact
  181. static int ray_sphere_helper (dxRay *ray, dVector3 sphere_pos, dReal radius,
  182. dContactGeom *contact, int mode)
  183. {
  184. dVector3 q;
  185. q[0] = ray->final_posr->pos[0] - sphere_pos[0];
  186. q[1] = ray->final_posr->pos[1] - sphere_pos[1];
  187. q[2] = ray->final_posr->pos[2] - sphere_pos[2];
  188. dReal B = dCalcVectorDot3_14(q,ray->final_posr->R+2);
  189. dReal C = dCalcVectorDot3(q,q) - radius*radius;
  190. // note: if C <= 0 then the start of the ray is inside the sphere
  191. dReal k = B*B - C;
  192. if (k < 0) return 0;
  193. k = dSqrt(k);
  194. dReal alpha;
  195. if (mode && C >= 0) {
  196. alpha = -B + k;
  197. if (alpha < 0) return 0;
  198. }
  199. else {
  200. alpha = -B - k;
  201. if (alpha < 0) {
  202. alpha = -B + k;
  203. if (alpha < 0) return 0;
  204. }
  205. }
  206. if (alpha > ray->length) return 0;
  207. contact->pos[0] = ray->final_posr->pos[0] + alpha*ray->final_posr->R[0*4+2];
  208. contact->pos[1] = ray->final_posr->pos[1] + alpha*ray->final_posr->R[1*4+2];
  209. contact->pos[2] = ray->final_posr->pos[2] + alpha*ray->final_posr->R[2*4+2];
  210. dReal nsign = (C < 0 || mode) ? REAL(-1.0) : REAL(1.0);
  211. contact->normal[0] = nsign*(contact->pos[0] - sphere_pos[0]);
  212. contact->normal[1] = nsign*(contact->pos[1] - sphere_pos[1]);
  213. contact->normal[2] = nsign*(contact->pos[2] - sphere_pos[2]);
  214. dNormalize3 (contact->normal);
  215. contact->depth = alpha;
  216. return 1;
  217. }
  218. int dCollideRaySphere (dxGeom *o1, dxGeom *o2, int flags,
  219. dContactGeom *contact, int skip)
  220. {
  221. dIASSERT (skip >= (int)sizeof(dContactGeom));
  222. dIASSERT (o1->type == dRayClass);
  223. dIASSERT (o2->type == dSphereClass);
  224. dIASSERT ((flags & NUMC_MASK) >= 1);
  225. dxRay *ray = (dxRay*) o1;
  226. dxSphere *sphere = (dxSphere*) o2;
  227. contact->g1 = ray;
  228. contact->g2 = sphere;
  229. contact->side1 = -1;
  230. contact->side2 = -1;
  231. return ray_sphere_helper (ray,sphere->final_posr->pos,sphere->radius,contact,0);
  232. }
  233. int dCollideRayBox (dxGeom *o1, dxGeom *o2, int flags,
  234. dContactGeom *contact, int skip)
  235. {
  236. dIASSERT (skip >= (int)sizeof(dContactGeom));
  237. dIASSERT (o1->type == dRayClass);
  238. dIASSERT (o2->type == dBoxClass);
  239. dIASSERT ((flags & NUMC_MASK) >= 1);
  240. dxRay *ray = (dxRay*) o1;
  241. dxBox *box = (dxBox*) o2;
  242. contact->g1 = ray;
  243. contact->g2 = box;
  244. contact->side1 = -1;
  245. contact->side2 = -1;
  246. int i;
  247. // compute the start and delta of the ray relative to the box.
  248. // we will do all subsequent computations in this box-relative coordinate
  249. // system. we have to do a translation and rotation for each point.
  250. dVector3 tmp,s,v;
  251. tmp[0] = ray->final_posr->pos[0] - box->final_posr->pos[0];
  252. tmp[1] = ray->final_posr->pos[1] - box->final_posr->pos[1];
  253. tmp[2] = ray->final_posr->pos[2] - box->final_posr->pos[2];
  254. dMultiply1_331 (s,box->final_posr->R,tmp);
  255. tmp[0] = ray->final_posr->R[0*4+2];
  256. tmp[1] = ray->final_posr->R[1*4+2];
  257. tmp[2] = ray->final_posr->R[2*4+2];
  258. dMultiply1_331 (v,box->final_posr->R,tmp);
  259. // mirror the line so that v has all components >= 0
  260. dVector3 sign;
  261. for (i=0; i<3; i++) {
  262. if (v[i] < 0) {
  263. s[i] = -s[i];
  264. v[i] = -v[i];
  265. sign[i] = 1;
  266. }
  267. else sign[i] = -1;
  268. }
  269. // compute the half-sides of the box
  270. dReal h[3];
  271. h[0] = REAL(0.5) * box->side[0];
  272. h[1] = REAL(0.5) * box->side[1];
  273. h[2] = REAL(0.5) * box->side[2];
  274. // do a few early exit tests
  275. if ((s[0] < -h[0] && v[0] <= 0) || s[0] > h[0] ||
  276. (s[1] < -h[1] && v[1] <= 0) || s[1] > h[1] ||
  277. (s[2] < -h[2] && v[2] <= 0) || s[2] > h[2] ||
  278. (v[0] == 0 && v[1] == 0 && v[2] == 0)) {
  279. return 0;
  280. }
  281. // compute the t=[lo..hi] range for where s+v*t intersects the box
  282. dReal lo = -dInfinity;
  283. dReal hi = dInfinity;
  284. int nlo = 0, nhi = 0;
  285. for (i=0; i<3; i++) {
  286. if (v[i] != 0) {
  287. dReal k = (-h[i] - s[i])/v[i];
  288. if (k > lo) {
  289. lo = k;
  290. nlo = i;
  291. }
  292. k = (h[i] - s[i])/v[i];
  293. if (k < hi) {
  294. hi = k;
  295. nhi = i;
  296. }
  297. }
  298. }
  299. // check if the ray intersects
  300. if (lo > hi) return 0;
  301. dReal alpha;
  302. int n;
  303. if (lo >= 0) {
  304. alpha = lo;
  305. n = nlo;
  306. }
  307. else {
  308. alpha = hi;
  309. n = nhi;
  310. }
  311. if (alpha < 0 || alpha > ray->length) return 0;
  312. contact->pos[0] = ray->final_posr->pos[0] + alpha*ray->final_posr->R[0*4+2];
  313. contact->pos[1] = ray->final_posr->pos[1] + alpha*ray->final_posr->R[1*4+2];
  314. contact->pos[2] = ray->final_posr->pos[2] + alpha*ray->final_posr->R[2*4+2];
  315. contact->normal[0] = box->final_posr->R[0*4+n] * sign[n];
  316. contact->normal[1] = box->final_posr->R[1*4+n] * sign[n];
  317. contact->normal[2] = box->final_posr->R[2*4+n] * sign[n];
  318. contact->depth = alpha;
  319. return 1;
  320. }
  321. int dCollideRayCapsule (dxGeom *o1, dxGeom *o2,
  322. int flags, dContactGeom *contact, int skip)
  323. {
  324. dIASSERT (skip >= (int)sizeof(dContactGeom));
  325. dIASSERT (o1->type == dRayClass);
  326. dIASSERT (o2->type == dCapsuleClass);
  327. dIASSERT ((flags & NUMC_MASK) >= 1);
  328. dxRay *ray = (dxRay*) o1;
  329. dxCapsule *ccyl = (dxCapsule*) o2;
  330. contact->g1 = ray;
  331. contact->g2 = ccyl;
  332. contact->side1 = -1;
  333. contact->side2 = -1;
  334. dReal lz2 = ccyl->lz * REAL(0.5);
  335. // compute some useful info
  336. dVector3 cs,q,r;
  337. dReal C,k;
  338. cs[0] = ray->final_posr->pos[0] - ccyl->final_posr->pos[0];
  339. cs[1] = ray->final_posr->pos[1] - ccyl->final_posr->pos[1];
  340. cs[2] = ray->final_posr->pos[2] - ccyl->final_posr->pos[2];
  341. k = dCalcVectorDot3_41(ccyl->final_posr->R+2,cs); // position of ray start along ccyl axis
  342. q[0] = k*ccyl->final_posr->R[0*4+2] - cs[0];
  343. q[1] = k*ccyl->final_posr->R[1*4+2] - cs[1];
  344. q[2] = k*ccyl->final_posr->R[2*4+2] - cs[2];
  345. C = dCalcVectorDot3(q,q) - ccyl->radius*ccyl->radius;
  346. // if C < 0 then ray start position within infinite extension of cylinder
  347. // see if ray start position is inside the capped cylinder
  348. int inside_ccyl = 0;
  349. if (C < 0) {
  350. if (k < -lz2) k = -lz2;
  351. else if (k > lz2) k = lz2;
  352. r[0] = ccyl->final_posr->pos[0] + k*ccyl->final_posr->R[0*4+2];
  353. r[1] = ccyl->final_posr->pos[1] + k*ccyl->final_posr->R[1*4+2];
  354. r[2] = ccyl->final_posr->pos[2] + k*ccyl->final_posr->R[2*4+2];
  355. if ((ray->final_posr->pos[0]-r[0])*(ray->final_posr->pos[0]-r[0]) +
  356. (ray->final_posr->pos[1]-r[1])*(ray->final_posr->pos[1]-r[1]) +
  357. (ray->final_posr->pos[2]-r[2])*(ray->final_posr->pos[2]-r[2]) < ccyl->radius*ccyl->radius) {
  358. inside_ccyl = 1;
  359. }
  360. }
  361. // compute ray collision with infinite cylinder, except for the case where
  362. // the ray is outside the capped cylinder but within the infinite cylinder
  363. // (it that case the ray can only hit endcaps)
  364. if (!inside_ccyl && C < 0) {
  365. // set k to cap position to check
  366. if (k < 0) k = -lz2; else k = lz2;
  367. }
  368. else {
  369. dReal uv = dCalcVectorDot3_44(ccyl->final_posr->R+2,ray->final_posr->R+2);
  370. r[0] = uv*ccyl->final_posr->R[0*4+2] - ray->final_posr->R[0*4+2];
  371. r[1] = uv*ccyl->final_posr->R[1*4+2] - ray->final_posr->R[1*4+2];
  372. r[2] = uv*ccyl->final_posr->R[2*4+2] - ray->final_posr->R[2*4+2];
  373. dReal A = dCalcVectorDot3(r,r);
  374. // A == 0 means that the ray and ccylinder axes are parallel
  375. if (A == 0) { // There is a division by A below...
  376. // set k to cap position to check
  377. if (uv < 0) k = -lz2; else k = lz2;
  378. }
  379. else {
  380. dReal B = 2*dCalcVectorDot3(q,r);
  381. k = B*B-4*A*C;
  382. if (k < 0) {
  383. // the ray does not intersect the infinite cylinder, but if the ray is
  384. // inside and parallel to the cylinder axis it may intersect the end
  385. // caps. set k to cap position to check.
  386. if (!inside_ccyl) return 0;
  387. if (uv < 0) k = -lz2; else k = lz2;
  388. }
  389. else {
  390. k = dSqrt(k);
  391. A = dRecip (2*A);
  392. dReal alpha = (-B-k)*A;
  393. if (alpha < 0) {
  394. alpha = (-B+k)*A;
  395. if (alpha < 0) return 0;
  396. }
  397. if (alpha > ray->length) return 0;
  398. // the ray intersects the infinite cylinder. check to see if the
  399. // intersection point is between the caps
  400. contact->pos[0] = ray->final_posr->pos[0] + alpha*ray->final_posr->R[0*4+2];
  401. contact->pos[1] = ray->final_posr->pos[1] + alpha*ray->final_posr->R[1*4+2];
  402. contact->pos[2] = ray->final_posr->pos[2] + alpha*ray->final_posr->R[2*4+2];
  403. q[0] = contact->pos[0] - ccyl->final_posr->pos[0];
  404. q[1] = contact->pos[1] - ccyl->final_posr->pos[1];
  405. q[2] = contact->pos[2] - ccyl->final_posr->pos[2];
  406. k = dCalcVectorDot3_14(q,ccyl->final_posr->R+2);
  407. dReal nsign = inside_ccyl ? REAL(-1.0) : REAL(1.0);
  408. if (k >= -lz2 && k <= lz2) {
  409. contact->normal[0] = nsign * (contact->pos[0] -
  410. (ccyl->final_posr->pos[0] + k*ccyl->final_posr->R[0*4+2]));
  411. contact->normal[1] = nsign * (contact->pos[1] -
  412. (ccyl->final_posr->pos[1] + k*ccyl->final_posr->R[1*4+2]));
  413. contact->normal[2] = nsign * (contact->pos[2] -
  414. (ccyl->final_posr->pos[2] + k*ccyl->final_posr->R[2*4+2]));
  415. dNormalize3 (contact->normal);
  416. contact->depth = alpha;
  417. return 1;
  418. }
  419. // the infinite cylinder intersection point is not between the caps.
  420. // set k to cap position to check.
  421. if (k < 0) k = -lz2; else k = lz2;
  422. }
  423. }
  424. }
  425. // check for ray intersection with the caps. k must indicate the cap
  426. // position to check
  427. q[0] = ccyl->final_posr->pos[0] + k*ccyl->final_posr->R[0*4+2];
  428. q[1] = ccyl->final_posr->pos[1] + k*ccyl->final_posr->R[1*4+2];
  429. q[2] = ccyl->final_posr->pos[2] + k*ccyl->final_posr->R[2*4+2];
  430. return ray_sphere_helper (ray,q,ccyl->radius,contact, inside_ccyl);
  431. }
  432. int dCollideRayPlane (dxGeom *o1, dxGeom *o2, int flags,
  433. dContactGeom *contact, int skip)
  434. {
  435. dIASSERT (skip >= (int)sizeof(dContactGeom));
  436. dIASSERT (o1->type == dRayClass);
  437. dIASSERT (o2->type == dPlaneClass);
  438. dIASSERT ((flags & NUMC_MASK) >= 1);
  439. dxRay *ray = (dxRay*) o1;
  440. dxPlane *plane = (dxPlane*) o2;
  441. dReal alpha = plane->p[3] - dCalcVectorDot3 (plane->p,ray->final_posr->pos);
  442. // note: if alpha > 0 the starting point is below the plane
  443. dReal nsign = (alpha > 0) ? REAL(-1.0) : REAL(1.0);
  444. dReal k = dCalcVectorDot3_14(plane->p,ray->final_posr->R+2);
  445. if (k==0) return 0; // ray parallel to plane
  446. alpha /= k;
  447. if (alpha < 0 || alpha > ray->length) return 0;
  448. contact->pos[0] = ray->final_posr->pos[0] + alpha*ray->final_posr->R[0*4+2];
  449. contact->pos[1] = ray->final_posr->pos[1] + alpha*ray->final_posr->R[1*4+2];
  450. contact->pos[2] = ray->final_posr->pos[2] + alpha*ray->final_posr->R[2*4+2];
  451. contact->normal[0] = nsign*plane->p[0];
  452. contact->normal[1] = nsign*plane->p[1];
  453. contact->normal[2] = nsign*plane->p[2];
  454. contact->depth = alpha;
  455. contact->g1 = ray;
  456. contact->g2 = plane;
  457. contact->side1 = -1;
  458. contact->side2 = -1;
  459. return 1;
  460. }
  461. // Ray-Cylinder collider by Joseph Cooper (2011)
  462. int dCollideRayCylinder( dxGeom *o1, dxGeom *o2, int flags, dContactGeom *contact, int skip )
  463. {
  464. dIASSERT( skip >= (int)sizeof( dContactGeom ) );
  465. dIASSERT( o1->type == dRayClass );
  466. dIASSERT( o2->type == dCylinderClass );
  467. dIASSERT( (flags & NUMC_MASK) >= 1 );
  468. dxRay* ray = (dxRay*)( o1 );
  469. dxCylinder* cyl = (dxCylinder*)( o2 );
  470. // Fill in contact information.
  471. contact->g1 = ray;
  472. contact->g2 = cyl;
  473. contact->side1 = -1;
  474. contact->side2 = -1;
  475. const dReal half_length = cyl->lz * REAL( 0.5 );
  476. /* Possible collision cases:
  477. * Ray origin between/outside caps
  478. * Ray origin within/outside radius
  479. * Ray direction left/right/perpendicular
  480. * Ray direction parallel/perpendicular/other
  481. *
  482. * Ray origin cases (ignoring origin on surface)
  483. *
  484. * A B
  485. * /-\-----------\
  486. * C ( ) D )
  487. * \_/___________/
  488. *
  489. * Cases A and D can collide with caps or cylinder
  490. * Case C can only collide with the caps
  491. * Case B can only collide with the cylinder
  492. * Case D will produce inverted normals
  493. * If the ray is perpendicular, only check the cylinder
  494. * If the ray is parallel to cylinder axis,
  495. * we can only check caps
  496. * If the ray points right,
  497. * Case A,C Check left cap
  498. * Case D Check right cap
  499. * If the ray points left
  500. * Case A,C Check right cap
  501. * Case D Check left cap
  502. * Case B, check only first possible cylinder collision
  503. * Case D, check only second possible cylinder collision
  504. */
  505. // Find the ray in the cylinder coordinate frame:
  506. dVector3 tmp;
  507. dVector3 pos; // Ray origin in cylinder frame
  508. dVector3 dir; // Ray direction in cylinder frame
  509. // Translate ray start by inverse cyl
  510. dSubtractVectors3(tmp,ray->final_posr->pos,cyl->final_posr->pos);
  511. // Rotate ray start by inverse cyl
  512. dMultiply1_331(pos,cyl->final_posr->R,tmp);
  513. // Get the ray's direction
  514. tmp[0] = ray->final_posr->R[2];
  515. tmp[1] = ray->final_posr->R[6];
  516. tmp[2] = ray->final_posr->R[10];
  517. // Rotate the ray direction by inverse cyl
  518. dMultiply1_331(dir,cyl->final_posr->R,tmp);
  519. // Is the ray origin inside of the (extended) cylinder?
  520. dReal r2 = cyl->radius*cyl->radius;
  521. dReal C = pos[0]*pos[0] + pos[1]*pos[1] - r2;
  522. // Find the different cases
  523. // Is ray parallel to the cylinder length?
  524. int parallel = (dir[0]==0 && dir[1]==0);
  525. // Is ray perpendicular to the cylinder length?
  526. int perpendicular = (dir[2]==0);
  527. // Is ray origin within the radius of the caps?
  528. int inRadius = (C<=0);
  529. // Is ray origin between the top and bottom caps?
  530. int inCaps = (dFabs(pos[2])<=half_length);
  531. int checkCaps = (!perpendicular && (!inCaps || inRadius));
  532. int checkCyl = (!parallel && (!inRadius || inCaps));
  533. int flipNormals = (inCaps&&inRadius);
  534. dReal tt=-dInfinity; // Depth to intersection
  535. dVector3 tmpNorm = {dNaN, dNaN, dNaN}; // ensure we don't leak garbage
  536. if (checkCaps) {
  537. // Make it so we only need to check one cap
  538. int flipDir = 0;
  539. // Wish c had logical xor...
  540. if ((dir[2]<0 && flipNormals) || (dir[2]>0 && !flipNormals)) {
  541. flipDir = 1;
  542. dir[2]=-dir[2];
  543. pos[2]=-pos[2];
  544. }
  545. // The cap is half the cylinder's length
  546. // from the cylinder's origin
  547. // We only checkCaps if dir[2]!=0
  548. tt = (half_length-pos[2])/dir[2];
  549. if (tt>=0 && tt<=ray->length) {
  550. tmp[0] = pos[0] + tt*dir[0];
  551. tmp[1] = pos[1] + tt*dir[1];
  552. // Ensure collision point is within cap circle
  553. if (tmp[0]*tmp[0] + tmp[1]*tmp[1] <= r2) {
  554. // Successful collision
  555. tmp[2] = (flipDir)?-half_length:half_length;
  556. tmpNorm[0]=0;
  557. tmpNorm[1]=0;
  558. tmpNorm[2]=(flipDir!=flipNormals)?-REAL(1.0):REAL(1.0);
  559. checkCyl = 0; // Short circuit cylinder check
  560. } else {
  561. // Ray hits cap plane outside of cap circle
  562. tt=-dInfinity; // No collision yet
  563. }
  564. } else {
  565. // The cap plane is beyond (or behind) the ray length
  566. tt=-dInfinity; // No collision yet
  567. }
  568. if (flipDir) {
  569. // Flip back
  570. dir[2]=-dir[2];
  571. pos[2]=-pos[2];
  572. }
  573. }
  574. if (checkCyl) {
  575. // Compute quadratic formula for parametric ray equation
  576. dReal A = dir[0]*dir[0] + dir[1]*dir[1];
  577. dReal B = 2*(pos[0]*dir[0] + pos[1]*dir[1]);
  578. // Already computed C
  579. dReal k = B*B - 4*A*C;
  580. // Check collision with infinite cylinder
  581. // k<0 means the ray passes outside the cylinder
  582. // k==0 means ray is tangent to cylinder (or parallel)
  583. //
  584. // Our quadratic formula: tt = (-B +- sqrt(k))/(2*A)
  585. //
  586. // A must be positive (otherwise we wouldn't be checking
  587. // cylinder because ray is parallel)
  588. // if (k<0) ray doesn't collide with sphere
  589. // if (B > sqrt(k)) then both times are negative
  590. // -- don't calculate
  591. // if (B<-sqrt(k)) then both times are positive (Case A or B)
  592. // -- only calculate first, if first isn't valid
  593. // -- second can't be without first going through a cap
  594. // otherwise (fabs(B)<=sqrt(k)) then C<=0 (ray-origin inside/on cylinder)
  595. // -- only calculate second collision
  596. if (k>=0 && (B<0 || B*B<=k)) {
  597. k = dSqrt(k);
  598. A = dRecip(2*A);
  599. if (dFabs(B)<=k) {
  600. tt = (-B + k)*A; // Second solution
  601. // If ray origin is on surface and pointed out, we
  602. // can get a tt=0 solution...
  603. } else {
  604. tt = (-B - k)*A; // First solution
  605. }
  606. if (tt<=ray->length) {
  607. tmp[2] = pos[2] + tt*dir[2];
  608. if (dFabs(tmp[2])<=half_length) {
  609. // Valid solution
  610. tmp[0] = pos[0] + tt*dir[0];
  611. tmp[1] = pos[1] + tt*dir[1];
  612. tmpNorm[0] = tmp[0]/cyl->radius;
  613. tmpNorm[1] = tmp[1]/cyl->radius;
  614. tmpNorm[2] = 0;
  615. if (flipNormals) {
  616. // Ray origin was inside cylinder
  617. tmpNorm[0] = -tmpNorm[0];
  618. tmpNorm[1] = -tmpNorm[1];
  619. }
  620. } else {
  621. // Ray hits cylinder outside of caps
  622. tt=-dInfinity;
  623. }
  624. } else {
  625. // Ray doesn't reach the cylinder
  626. tt=-dInfinity;
  627. }
  628. }
  629. }
  630. if (tt>0) {
  631. contact->depth = tt;
  632. // Transform the point back to world coordinates
  633. tmpNorm[3]=0;
  634. tmp[3] = 0;
  635. dMultiply0_331(contact->normal,cyl->final_posr->R,tmpNorm);
  636. dMultiply0_331(contact->pos,cyl->final_posr->R,tmp);
  637. contact->pos[0]+=cyl->final_posr->pos[0];
  638. contact->pos[1]+=cyl->final_posr->pos[1];
  639. contact->pos[2]+=cyl->final_posr->pos[2];
  640. return 1;
  641. }
  642. // No contact with anything.
  643. return 0;
  644. }