IntrSphere3Triangle3.h 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599
  1. // David Eberly, Geometric Tools, Redmond WA 98052
  2. // Copyright (c) 1998-2020
  3. // Distributed under the Boost Software License, Version 1.0.
  4. // https://www.boost.org/LICENSE_1_0.txt
  5. // https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
  6. // Version: 4.0.2019.08.13
  7. #pragma once
  8. #include <Mathematics/FIQuery.h>
  9. #include <Mathematics/DistPointTriangle.h>
  10. #include <Mathematics/Hypersphere.h>
  11. #include <Mathematics/Vector3.h>
  12. #include <Mathematics/QFNumber.h>
  13. namespace WwiseGTE
  14. {
  15. // Currently, only a dynamic query is supported. A static query will
  16. // need to compute the intersection set of triangle and sphere.
  17. template <typename Real>
  18. class FIQuery<Real, Sphere3<Real>, Triangle3<Real>>
  19. {
  20. public:
  21. // The implementation for floating-point types.
  22. struct Result
  23. {
  24. // The cases are
  25. // 1. Objects initially overlapping. The contactPoint is only one
  26. // of infinitely many points in the overlap.
  27. // intersectionType = -1
  28. // contactTime = 0
  29. // contactPoint = triangle point closest to sphere.center
  30. // 2. Objects initially separated but do not intersect later. The
  31. // contactTime and contactPoint are invalid.
  32. // intersectionType = 0
  33. // contactTime = 0
  34. // contactPoint = (0,0,0)
  35. // 3. Objects initially separated but intersect later.
  36. // intersectionType = +1
  37. // contactTime = first time T > 0
  38. // contactPoint = corresponding first contact
  39. int intersectionType;
  40. Real contactTime;
  41. Vector3<Real> contactPoint;
  42. };
  43. template <typename Dummy = Real>
  44. typename std::enable_if<!is_arbitrary_precision<Dummy>::value, Result>::type
  45. operator()(Sphere3<Real> const& sphere, Vector3<Real> const& sphereVelocity,
  46. Triangle3<Real> const& triangle, Vector3<Real> const& triangleVelocity)
  47. {
  48. Result result = { 0, (Real)0, { (Real)0, (Real)0, (Real)0 } };
  49. // Test for initial overlap or contact.
  50. DCPQuery<Real, Vector3<Real>, Triangle3<Real>> ptQuery;
  51. auto ptResult = ptQuery(sphere.center, triangle);
  52. Real rsqr = sphere.radius * sphere.radius;
  53. if (ptResult.sqrDistance <= rsqr)
  54. {
  55. result.intersectionType = (ptResult.sqrDistance < rsqr ? -1 : +1);
  56. result.contactTime = (Real)0;
  57. result.contactPoint = ptResult.closest;
  58. return result;
  59. }
  60. // To reach here, the sphere and triangle are initially separated.
  61. // Compute the velocity of the sphere relative to the triangle.
  62. Vector3<Real> V = sphereVelocity - triangleVelocity;
  63. Real sqrLenV = Dot(V, V);
  64. if (sqrLenV == (Real)0)
  65. {
  66. // The sphere and triangle are separated and the sphere is not
  67. // moving relative to the triangle, so there is no contact.
  68. // The 'result' is already set to the correct state for this
  69. // case.
  70. return result;
  71. }
  72. // Compute the triangle edge directions E[], the vector U normal
  73. // to the plane of the triangle, and compute the normals to the
  74. // edges in the plane of the triangle. TODO: For a nondeforming
  75. // triangle (or mesh of triangles), these quantities can all be
  76. // precomputed to reduce the computational cost of the query. Add
  77. // another operator()-query that accepts the precomputed values.
  78. // TODO: When the triangle is deformable, these quantities must be
  79. // computed, either by the caller or here. Optimize the code to
  80. // compute the quantities on-demand (i.e. only when they are
  81. // needed, but cache them for later use).
  82. Vector3<Real> E[3] =
  83. {
  84. triangle.v[1] - triangle.v[0],
  85. triangle.v[2] - triangle.v[1],
  86. triangle.v[0] - triangle.v[2]
  87. };
  88. Real sqrLenE[3] = { Dot(E[0], E[0]), Dot(E[1], E[1]), Dot(E[2], E[2]) };
  89. Vector3<Real> U = UnitCross(E[0], E[1]);
  90. Vector3<Real> ExU[3] =
  91. {
  92. Cross(E[0], U),
  93. Cross(E[1], U),
  94. Cross(E[2], U)
  95. };
  96. // Compute the vectors from the triangle vertices to the sphere
  97. // center.
  98. Vector3<Real> Delta[3] =
  99. {
  100. sphere.center - triangle.v[0],
  101. sphere.center - triangle.v[1],
  102. sphere.center - triangle.v[2]
  103. };
  104. // Determine where the sphere center is located relative to the
  105. // planes of the triangle offset faces of the sphere-swept volume.
  106. Real dotUDelta0 = Dot(U, Delta[0]);
  107. if (dotUDelta0 >= sphere.radius)
  108. {
  109. // The sphere is on the positive side of Dot(U,X-C) = r. If
  110. // the sphere will contact the sphere-swept volume at a
  111. // triangular face, it can do so only on the face of the
  112. // aforementioned plane.
  113. Real dotUV = Dot(U, V);
  114. if (dotUV >= (Real)0)
  115. {
  116. // The sphere is moving away from, or parallel to, the
  117. // plane of the triangle. The 'result' is already set to
  118. // the correct state for this case.
  119. return result;
  120. }
  121. Real tbar = (sphere.radius - dotUDelta0) / dotUV;
  122. bool foundContact = true;
  123. for (int i = 0; i < 3; ++i)
  124. {
  125. Real phi = Dot(ExU[i], Delta[i]);
  126. Real psi = Dot(ExU[i], V);
  127. if (phi + psi * tbar > (Real)0)
  128. {
  129. foundContact = false;
  130. break;
  131. }
  132. }
  133. if (foundContact)
  134. {
  135. result.intersectionType = 1;
  136. result.contactTime = tbar;
  137. result.contactPoint = sphere.center + tbar * sphereVelocity;
  138. return result;
  139. }
  140. }
  141. else if (dotUDelta0 <= -sphere.radius)
  142. {
  143. // The sphere is on the positive side of Dot(-U,X-C) = r. If
  144. // the sphere will contact the sphere-swept volume at a
  145. // triangular face, it can do so only on the face of the
  146. // aforementioned plane.
  147. Real dotUV = Dot(U, V);
  148. if (dotUV <= (Real)0)
  149. {
  150. // The sphere is moving away from, or parallel to, the
  151. // plane of the triangle. The 'result' is already set to
  152. // the correct state for this case.
  153. return result;
  154. }
  155. Real tbar = (-sphere.radius - dotUDelta0) / dotUV;
  156. bool foundContact = true;
  157. for (int i = 0; i < 3; ++i)
  158. {
  159. Real phi = Dot(ExU[i], Delta[i]);
  160. Real psi = Dot(ExU[i], V);
  161. if (phi + psi * tbar > (Real)0)
  162. {
  163. foundContact = false;
  164. break;
  165. }
  166. }
  167. if (foundContact)
  168. {
  169. result.intersectionType = 1;
  170. result.contactTime = tbar;
  171. result.contactPoint = sphere.center + tbar * sphereVelocity;
  172. return result;
  173. }
  174. }
  175. // else: The ray-sphere-swept-volume contact point (if any) cannot
  176. // be on a triangular face of the sphere-swept-volume.
  177. // The sphere is moving towards the slab between the two planes
  178. // of the sphere-swept volume triangular faces. Determine whether
  179. // the ray intersects the half cylinders or sphere wedges of the
  180. // sphere-swept volume.
  181. // Test for contact with half cylinders of the sphere-swept
  182. // volume. First, precompute some dot products required in the
  183. // computations. TODO: Optimize the code to compute the quantities
  184. // on-demand (i.e. only when they are needed, but cache them for
  185. // later use).
  186. Real del[3], delp[3], nu[3];
  187. for (int im1 = 2, i = 0; i < 3; im1 = i++)
  188. {
  189. del[i] = Dot(E[i], Delta[i]);
  190. delp[im1] = Dot(E[im1], Delta[i]);
  191. nu[i] = Dot(E[i], V);
  192. }
  193. for (int i = 2, ip1 = 0; ip1 < 3; i = ip1++)
  194. {
  195. Vector3<Real> hatV = V - E[i] * nu[i] / sqrLenE[i];
  196. Real sqrLenHatV = Dot(hatV, hatV);
  197. if (sqrLenHatV > (Real)0)
  198. {
  199. Vector3<Real> hatDelta = Delta[i] - E[i] * del[i] / sqrLenE[i];
  200. Real alpha = -Dot(hatV, hatDelta);
  201. if (alpha >= (Real)0)
  202. {
  203. Real sqrLenHatDelta = Dot(hatDelta, hatDelta);
  204. Real beta = alpha * alpha - sqrLenHatV * (sqrLenHatDelta - rsqr);
  205. if (beta >= (Real)0)
  206. {
  207. Real tbar = (alpha - std::sqrt(beta)) / sqrLenHatV;
  208. Real mu = Dot(ExU[i], Delta[i]);
  209. Real omega = Dot(ExU[i], hatV);
  210. if (mu + omega * tbar >= (Real)0)
  211. {
  212. if (del[i] + nu[i] * tbar >= (Real)0)
  213. {
  214. if (delp[i] + nu[i] * tbar <= (Real)0)
  215. {
  216. // The constraints are satisfied, so
  217. // tbar is the first time of contact.
  218. result.intersectionType = 1;
  219. result.contactTime = tbar;
  220. result.contactPoint = sphere.center + tbar * sphereVelocity;
  221. return result;
  222. }
  223. }
  224. }
  225. }
  226. }
  227. }
  228. }
  229. // Test for contact with sphere wedges of the sphere-swept
  230. // volume. We know that |V|^2 > 0 because of a previous
  231. // early-exit test.
  232. for (int im1 = 2, i = 0; i < 3; im1 = i++)
  233. {
  234. Real alpha = -Dot(V, Delta[i]);
  235. if (alpha >= (Real)0)
  236. {
  237. Real sqrLenDelta = Dot(Delta[i], Delta[i]);
  238. Real beta = alpha * alpha - sqrLenV * (sqrLenDelta - rsqr);
  239. if (beta >= (Real)0)
  240. {
  241. Real tbar = (alpha - std::sqrt(beta)) / sqrLenV;
  242. if (delp[im1] + nu[im1] * tbar >= (Real)0)
  243. {
  244. if (del[i] + nu[i] * tbar <= (Real)0)
  245. {
  246. // The constraints are satisfied, so tbar
  247. // is the first time of contact.
  248. result.intersectionType = 1;
  249. result.contactTime = tbar;
  250. result.contactPoint = sphere.center + tbar * sphereVelocity;
  251. return result;
  252. }
  253. }
  254. }
  255. }
  256. }
  257. // The ray and sphere-swept volume do not intersect, so the sphere
  258. // and triangle do not come into contact. The 'result' is already
  259. // set to the correct state for this case.
  260. return result;
  261. }
  262. // The implementation for arbitrary-precision types.
  263. using QFN1 = QFNumber<Real, 1>;
  264. struct ExactResult
  265. {
  266. // The cases are
  267. // 1. Objects initially overlapping. The contactPoint is only one
  268. // of infinitely many points in the overlap.
  269. // intersectionType = -1
  270. // contactTime = 0
  271. // contactPoint = triangle point closest to sphere.center
  272. // 2. Objects initially separated but do not intersect later. The
  273. // contactTime and contactPoint are invalid.
  274. // intersectionType = 0
  275. // contactTime = 0
  276. // contactPoint = (0,0,0)
  277. // 3. Objects initially separated but intersect later.
  278. // intersectionType = +1
  279. // contactTime = first time T > 0
  280. // contactPoint = corresponding first contact
  281. int intersectionType;
  282. // The exact representation of the contact time and point. To
  283. // convert to a floating-point type, use
  284. // FloatType contactTime;
  285. // Vector3<FloatType> contactPoint;
  286. // Result::Convert(result.contactTime, contactTime);
  287. // Result::Convert(result.contactPoint, contactPoint);
  288. template <typename OutputType>
  289. static void Convert(QFN1 const& input, OutputType& output)
  290. {
  291. output = static_cast<Real>(input);
  292. }
  293. template <typename OutputType>
  294. static void Convert(Vector3<QFN1> const& input, Vector3<OutputType>& output)
  295. {
  296. for (int i = 0; i < 3; ++i)
  297. {
  298. output[i] = static_cast<Real>(input[i]);
  299. }
  300. }
  301. QFN1 contactTime;
  302. Vector3<QFN1> contactPoint;
  303. };
  304. template <typename Dummy = Real>
  305. typename std::enable_if<is_arbitrary_precision<Dummy>::value, ExactResult>::type
  306. operator()(Sphere3<Real> const& sphere, Vector3<Real> const& sphereVelocity,
  307. Triangle3<Real> const& triangle, Vector3<Real> const& triangleVelocity)
  308. {
  309. // The default constructors for the members of 'result' set their
  310. // own members to zero.
  311. ExactResult result;
  312. // Test for initial overlap or contact.
  313. DCPQuery<Real, Vector3<Real>, Triangle3<Real>> ptQuery;
  314. auto ptResult = ptQuery(sphere.center, triangle);
  315. Real rsqr = sphere.radius * sphere.radius;
  316. if (ptResult.sqrDistance <= rsqr)
  317. {
  318. // The values result.contactTime and result.contactPoint[]
  319. // are both zero, so we need only set the
  320. // result.contactPoint[].x values.
  321. result.intersectionType = (ptResult.sqrDistance < rsqr ? -1 : +1);
  322. for (int j = 0; j < 3; ++j)
  323. {
  324. result.contactPoint[j].x = ptResult.closest[j];
  325. }
  326. return result;
  327. }
  328. // To reach here, the sphere and triangle are initially separated.
  329. // Compute the velocity of the sphere relative to the triangle.
  330. Vector3<Real> V = sphereVelocity - triangleVelocity;
  331. Real sqrLenV = Dot(V, V);
  332. if (sqrLenV == (Real)0)
  333. {
  334. // The sphere and triangle are separated and the sphere is not
  335. // moving relative to the triangle, so there is no contact.
  336. // The 'result' is already set to the correct state for this
  337. // case.
  338. return result;
  339. }
  340. // Compute the triangle edge directions E[], the vector U normal
  341. // to the plane of the triangle, and compute the normals to the
  342. // edges in the plane of the triangle. TODO: For a nondeforming
  343. // triangle (or mesh of triangles), these quantities can all be
  344. // precomputed to reduce the computational cost of the query. Add
  345. // another operator()-query that accepts the precomputed values.
  346. // TODO: When the triangle is deformable, these quantities must be
  347. // computed, either by the caller or here. Optimize the code to
  348. // compute the quantities on-demand (i.e. only when they are
  349. // needed, but cache them for later use).
  350. Vector3<Real> E[3] =
  351. {
  352. triangle.v[1] - triangle.v[0],
  353. triangle.v[2] - triangle.v[1],
  354. triangle.v[0] - triangle.v[2]
  355. };
  356. Real sqrLenE[3] = { Dot(E[0], E[0]), Dot(E[1], E[1]), Dot(E[2], E[2]) };
  357. // Use an unnormalized U for the plane of the triangle. This
  358. // allows us to use quadratic fields for the comparisons of the
  359. // constraints.
  360. Vector3<Real> U = Cross(E[0], E[1]);
  361. Real sqrLenU = Dot(U, U);
  362. Vector3<Real> ExU[3] =
  363. {
  364. Cross(E[0], U),
  365. Cross(E[1], U),
  366. Cross(E[2], U)
  367. };
  368. // Compute the vectors from the triangle vertices to the sphere
  369. // center.
  370. Vector3<Real> Delta[3] =
  371. {
  372. sphere.center - triangle.v[0],
  373. sphere.center - triangle.v[1],
  374. sphere.center - triangle.v[2]
  375. };
  376. // Determine where the sphere center is located relative to the
  377. // planes of the triangle offset faces of the sphere-swept volume.
  378. QFN1 const qfzero((Real)0, (Real)0, sqrLenU);
  379. QFN1 element(Dot(U, Delta[0]), -sphere.radius, sqrLenU);
  380. if (element >= qfzero)
  381. {
  382. // The sphere is on the positive side of Dot(U,X-C) = r|U|.
  383. // If the sphere will contact the sphere-swept volume at a
  384. // triangular face, it can do so only on the face of the
  385. // aforementioned plane.
  386. Real dotUV = Dot(U, V);
  387. if (dotUV >= (Real)0)
  388. {
  389. // The sphere is moving away from, or parallel to, the
  390. // plane of the triangle. The 'result' is already set
  391. // to the correct state for this case.
  392. return result;
  393. }
  394. bool foundContact = true;
  395. for (int i = 0; i < 3; ++i)
  396. {
  397. Real phi = Dot(ExU[i], Delta[i]);
  398. Real psi = Dot(ExU[i], V);
  399. QFN1 arg(psi * element.x - phi * dotUV, psi * element.y, sqrLenU);
  400. if (arg > qfzero)
  401. {
  402. foundContact = false;
  403. break;
  404. }
  405. }
  406. if (foundContact)
  407. {
  408. result.intersectionType = 1;
  409. result.contactTime.x = -element.x / dotUV;
  410. result.contactTime.y = -element.y / dotUV;
  411. for (int j = 0; j < 3; ++j)
  412. {
  413. result.contactPoint[j].x = sphere.center[j] + result.contactTime.x * sphereVelocity[j];
  414. result.contactPoint[j].y = result.contactTime.y * sphereVelocity[j];
  415. }
  416. return result;
  417. }
  418. }
  419. else
  420. {
  421. element.y = -element.y;
  422. if (element <= qfzero)
  423. {
  424. // The sphere is on the positive side of Dot(-U,X-C) = r|U|.
  425. // If the sphere will contact the sphere-swept volume at a
  426. // triangular face, it can do so only on the face of the
  427. // aforementioned plane.
  428. Real dotUV = Dot(U, V);
  429. if (dotUV <= (Real)0)
  430. {
  431. // The sphere is moving away from, or parallel to, the
  432. // plane of the triangle. The 'result' is already set
  433. // to the correct state for this case.
  434. return result;
  435. }
  436. bool foundContact = true;
  437. for (int i = 0; i < 3; ++i)
  438. {
  439. Real phi = Dot(ExU[i], Delta[i]);
  440. Real psi = Dot(ExU[i], V);
  441. QFN1 arg(phi * dotUV - psi * element.x, -psi * element.y, sqrLenU);
  442. if (arg > qfzero)
  443. {
  444. foundContact = false;
  445. break;
  446. }
  447. }
  448. if (foundContact)
  449. {
  450. result.intersectionType = 1;
  451. result.contactTime.x = -element.x / dotUV;
  452. result.contactTime.y = -element.y / dotUV;
  453. for (int j = 0; j < 3; ++j)
  454. {
  455. result.contactPoint[j].x = sphere.center[j] + result.contactTime.x * sphereVelocity[j];
  456. result.contactPoint[j].y = result.contactTime.y * sphereVelocity[j];
  457. }
  458. return result;
  459. }
  460. }
  461. // else: The ray-sphere-swept-volume contact point (if any)
  462. // cannot be on a triangular face of the sphere-swept-volume.
  463. }
  464. // The sphere is moving towards the slab between the two planes
  465. // of the sphere-swept volume triangular faces. Determine whether
  466. // the ray intersects the half cylinders or sphere wedges of the
  467. // sphere-swept volume.
  468. // Test for contact with half cylinders of the sphere-swept
  469. // volume. First, precompute some dot products required in the
  470. // computations. TODO: Optimize the code to compute the quantities
  471. // on-demand (i.e. only when they are needed, but cache them for
  472. // later use).
  473. Real del[3], delp[3], nu[3];
  474. for (int im1 = 2, i = 0; i < 3; im1 = i++)
  475. {
  476. del[i] = Dot(E[i], Delta[i]);
  477. delp[im1] = Dot(E[im1], Delta[i]);
  478. nu[i] = Dot(E[i], V);
  479. }
  480. for (int i = 2, ip1 = 0; ip1 < 3; i = ip1++)
  481. {
  482. Vector3<Real> hatV = V - E[i] * nu[i] / sqrLenE[i];
  483. Real sqrLenHatV = Dot(hatV, hatV);
  484. if (sqrLenHatV > (Real)0)
  485. {
  486. Vector3<Real> hatDelta = Delta[i] - E[i] * del[i] / sqrLenE[i];
  487. Real alpha = -Dot(hatV, hatDelta);
  488. if (alpha >= (Real)0)
  489. {
  490. Real sqrLenHatDelta = Dot(hatDelta, hatDelta);
  491. Real beta = alpha * alpha - sqrLenHatV * (sqrLenHatDelta - rsqr);
  492. if (beta >= (Real)0)
  493. {
  494. QFN1 const qfzero((Real)0, (Real)0, beta);
  495. Real mu = Dot(ExU[i], Delta[i]);
  496. Real omega = Dot(ExU[i], hatV);
  497. QFN1 arg0(mu * sqrLenHatV + omega * alpha, -omega, beta);
  498. if (arg0 >= qfzero)
  499. {
  500. QFN1 arg1(del[i] * sqrLenHatV + nu[i] * alpha, -nu[i], beta);
  501. if (arg1 >= qfzero)
  502. {
  503. QFN1 arg2(delp[i] * sqrLenHatV + nu[i] * alpha, -nu[i], beta);
  504. if (arg2 <= qfzero)
  505. {
  506. result.intersectionType = 1;
  507. result.contactTime.x = alpha / sqrLenHatV;
  508. result.contactTime.y = (Real)-1 / sqrLenHatV;
  509. for (int j = 0; j < 3; ++j)
  510. {
  511. result.contactPoint[j].x = sphere.center[j] + result.contactTime.x * sphereVelocity[j];
  512. result.contactPoint[j].y = result.contactTime.y * sphereVelocity[j];
  513. }
  514. return result;
  515. }
  516. }
  517. }
  518. }
  519. }
  520. }
  521. }
  522. // Test for contact with sphere wedges of the sphere-swept
  523. // volume. We know that |V|^2 > 0 because of a previous
  524. // early-exit test.
  525. for (int im1 = 2, i = 0; i < 3; im1 = i++)
  526. {
  527. Real alpha = -Dot(V, Delta[i]);
  528. if (alpha >= (Real)0)
  529. {
  530. Real sqrLenDelta = Dot(Delta[i], Delta[i]);
  531. Real beta = alpha * alpha - sqrLenV * (sqrLenDelta - rsqr);
  532. if (beta >= (Real)0)
  533. {
  534. QFN1 const qfzero((Real)0, (Real)0, beta);
  535. QFN1 arg0(delp[im1] * sqrLenV + nu[im1] * alpha, -nu[im1], beta);
  536. if (arg0 >= qfzero)
  537. {
  538. QFN1 arg1(del[i] * sqrLenV + nu[i] * alpha, -nu[i], beta);
  539. if (arg1 <= qfzero)
  540. {
  541. result.intersectionType = 1;
  542. result.contactTime.x = alpha / sqrLenV;
  543. result.contactTime.y = (Real)-1 / sqrLenV;
  544. for (int j = 0; j < 3; ++j)
  545. {
  546. result.contactPoint[j].x = sphere.center[j] + result.contactTime.x * sphereVelocity[j];
  547. result.contactPoint[j].y = result.contactTime.y * sphereVelocity[j];
  548. }
  549. return result;
  550. }
  551. }
  552. }
  553. }
  554. }
  555. // The ray and sphere-swept volume do not intersect, so the sphere
  556. // and triangle do not come into contact. The 'result' is already
  557. // set to the correct state for this case.
  558. return result;
  559. }
  560. };
  561. }