GLS.EllipseCollision.pas 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896
  1. //
  2. // The graphics engine GLScene
  3. //
  4. unit GLS.EllipseCollision;
  5. (* Ellipsoid collision functions mainly used by DCE *)
  6. interface
  7. {$I Stage.Defines.inc}
  8. uses
  9. Stage.VectorTypes,
  10. Stage.VectorGeometry,
  11. GLS.Octree,
  12. GLS.VectorLists;
  13. type
  14. // The Ellipsoid collision class
  15. TECPlane = class
  16. public
  17. Equation: array [0 .. 3] of Single;
  18. Origin: TAffineVector;
  19. Normal: TAffineVector;
  20. procedure MakePlane(const nOrigin, nNormal: TAffineVector); overload;
  21. procedure MakePlane(const p1, p2, p3: TAffineVector); overload;
  22. function isFrontFacingTo(const Direction: TAffineVector): Boolean;
  23. function signedDistanceTo(const Point: TAffineVector): Single;
  24. end;
  25. // Object collision properties
  26. TECObjectInfo = record
  27. AbsoluteMatrix: TGLMatrix;
  28. Solid: Boolean;
  29. IsDynamic: Boolean;
  30. ObjectID: Integer;
  31. end;
  32. // Ellipsoid collision Triangle
  33. TECTriangle = record
  34. p1, p2, p3: TAffineVector;
  35. end;
  36. // Ellipsoid collision Mesh
  37. TECTriMesh = record
  38. Triangles: array of TECTriangle;
  39. ObjectInfo: TECObjectInfo;
  40. end;
  41. TECTriMeshList = array of TECTriMesh;
  42. // Ellipsoid collision FreeForm
  43. TECFreeForm = record
  44. OctreeNodes: array of PGLOctreeNode;
  45. triangleFiler: ^TGLAffineVectorList;
  46. InvertedNormals: Boolean;
  47. ObjectInfo: TECObjectInfo;
  48. end;
  49. TECFreeFormList = array of TECFreeForm;
  50. TECColliderShape = (csEllipsoid, csPoint);
  51. TECCollider = record
  52. Position: TAffineVector;
  53. Radius: TAffineVector;
  54. Shape: TECColliderShape;
  55. ObjectInfo: TECObjectInfo;
  56. end;
  57. TECColliderList = array of TECCollider;
  58. TECContact = record
  59. // Generated by the collision procedure
  60. Position: TAffineVector;
  61. SurfaceNormal: TAffineVector;
  62. Distance: Single;
  63. // Collider data
  64. ObjectInfo: TECObjectInfo;
  65. end;
  66. TECContactList = array of TECContact;
  67. TECCollisionPacket = record
  68. // Information about the move being requested: (in eSpace)
  69. velocity: TAffineVector;
  70. normalizedVelocity: TAffineVector;
  71. basePoint: TAffineVector;
  72. // Hit information
  73. foundCollision: Boolean;
  74. nearestDistance: Single;
  75. NearestObject: Integer;
  76. intersectionPoint: TAffineVector;
  77. intersectionNormal: TAffineVector;
  78. end;
  79. TECMovePack = record
  80. // List of closest triangles
  81. TriMeshes: TECTriMeshList;
  82. // List of freeform octree nodes
  83. Freeforms: TECFreeFormList;
  84. // List of other colliders (f.i. ellipsoids)
  85. Colliders: TECColliderList;
  86. // Movement params
  87. Position: TAffineVector;
  88. velocity: TAffineVector;
  89. Gravity: TAffineVector;
  90. Radius: TAffineVector;
  91. ObjectInfo: TECObjectInfo;
  92. CollisionRange: Single; // Stores the range where objects may collide
  93. // Internal
  94. UnitScale: Double;
  95. MaxRecursionDepth: Byte;
  96. CP: TECCollisionPacket;
  97. collisionRecursionDepth: Byte;
  98. // Result
  99. ResultPos: TAffineVector;
  100. NearestObject: Integer;
  101. VelocityCollided: Boolean;
  102. GravityCollided: Boolean;
  103. GroundNormal: TAffineVector;
  104. Contacts: TECContactList
  105. end;
  106. function VectorDivide(const v, divider: TAffineVector): TAffineVector;
  107. procedure CollideAndSlide(var MP: TECMovePack);
  108. procedure CollideWithWorld(var MP: TECMovePack; pos, vel: TAffineVector;
  109. var HasCollided: Boolean);
  110. const
  111. cECCloseDistance: Single = 0.005;
  112. {.$DEFINE DEBUG }
  113. var
  114. Debug_Tri: array of TECTriangle;
  115. // ---------------------------------------
  116. implementation
  117. // ---------------------------------------
  118. {$IFDEF DEBUG}
  119. procedure AddDebugTri(p1, p2, p3: TAffineVector);
  120. begin
  121. SetLength(Debug_Tri, Length(Debug_Tri) + 1);
  122. Debug_Tri[High(Debug_Tri)].p1 := p1;
  123. Debug_Tri[High(Debug_Tri)].p2 := p2;
  124. Debug_Tri[High(Debug_Tri)].p3 := p3;
  125. end;
  126. {$ENDIF}
  127. // -----------------------------------------
  128. // Utility functions
  129. // -----------------------------------------
  130. function CheckPointInTriangle(Point, pa, pb, pc: TAffineVector): Boolean;
  131. var
  132. e10, e20, vp: TAffineVector;
  133. a, b, c, d, e, ac_bb, x, y, z: Single;
  134. begin
  135. e10 := VectorSubtract(pb, pa);
  136. e20 := VectorSubtract(pc, pa);
  137. a := VectorDotProduct(e10, e10);
  138. b := VectorDotProduct(e10, e20);
  139. c := VectorDotProduct(e20, e20);
  140. ac_bb := (a * c) - (b * b);
  141. vp := AffineVectorMake(Point.x - pa.x, Point.y - pa.y, Point.z - pa.z);
  142. d := VectorDotProduct(vp, e10);
  143. e := VectorDotProduct(vp, e20);
  144. x := (d * c) - (e * b);
  145. y := (e * a) - (d * b);
  146. z := x + y - ac_bb;
  147. result := ((z < 0) and not((x < 0) or (y < 0)));
  148. end;
  149. function getLowestRoot(a, b, c, maxR: Single; var Root: Single): Boolean;
  150. var
  151. determinant: Single;
  152. sqrtD, r1, r2, temp: Single;
  153. begin
  154. // No (valid) solutions
  155. result := false;
  156. // Check if a solution exists
  157. determinant := b * b - 4 * a * c;
  158. // If determinant is negative it means no solutions.
  159. if (determinant < 0) then
  160. Exit;
  161. // calculate the two roots: (if determinant == 0 then
  162. // x1==x2 but let’s disregard that slight optimization)
  163. sqrtD := sqrt(determinant);
  164. if a = 0 then
  165. a := -0.01; // is this really needed?
  166. r1 := (-b - sqrtD) / (2 * a);
  167. r2 := (-b + sqrtD) / (2 * a);
  168. // Sort so x1 <= x2
  169. if (r1 > r2) then
  170. begin
  171. temp := r2;
  172. r2 := r1;
  173. r1 := temp;
  174. end;
  175. // Get lowest root:
  176. if (r1 > 0) and (r1 < maxR) then
  177. begin
  178. Root := r1;
  179. result := true;
  180. Exit;
  181. end;
  182. // It is possible that we want x2 - this can happen
  183. // if x1 < 0
  184. if (r2 > 0) and (r2 < maxR) then
  185. begin
  186. Root := r2;
  187. result := true;
  188. Exit;
  189. end;
  190. end;
  191. function VectorDivide(const v, divider: TAffineVector): TAffineVector;
  192. begin
  193. result.x := v.x / divider.x;
  194. result.y := v.y / divider.y;
  195. result.z := v.z / divider.z;
  196. end;
  197. procedure VectorSetLength(var v: TAffineVector; Len: Single);
  198. var
  199. l, l2: Single;
  200. begin
  201. l2 := v.x * v.x + v.y * v.y + v.z * v.z;
  202. l := sqrt(l2);
  203. if l <> 0 then
  204. begin
  205. Len := Len / l;
  206. v.x := v.x * Len;
  207. v.y := v.y * Len;
  208. v.z := v.z * Len;
  209. end;
  210. end;
  211. // -----------------------------------------
  212. // TECPlane
  213. // -----------------------------------------
  214. procedure TECPlane.MakePlane(const nOrigin, nNormal: TAffineVector);
  215. begin
  216. Normal := nNormal;
  217. Origin := nOrigin;
  218. Equation[0] := Normal.x;
  219. Equation[1] := Normal.y;
  220. Equation[2] := Normal.z;
  221. Equation[3] := -(Normal.x * Origin.x + Normal.y * Origin.y + Normal.z *
  222. Origin.z);
  223. end;
  224. procedure TECPlane.MakePlane(const p1, p2, p3: TAffineVector);
  225. begin
  226. Normal := CalcPlaneNormal(p1, p2, p3);
  227. MakePlane(p1, Normal);
  228. end;
  229. function TECPlane.isFrontFacingTo(const Direction: TAffineVector): Boolean;
  230. var
  231. Dot: Single;
  232. begin
  233. Dot := VectorDotProduct(Normal, Direction);
  234. result := (Dot <= 0);
  235. end;
  236. function TECPlane.signedDistanceTo(const Point: TAffineVector): Single;
  237. begin
  238. result := VectorDotProduct(Point, Normal) + Equation[3];
  239. end;
  240. // --------------------------------------
  241. // Collision detection functions
  242. // --------------------------------------
  243. // Assumes: p1,p2 and p3 are given in ellisoid space:
  244. // Returns true if collided
  245. function CheckTriangle(var MP: TECMovePack; const p1, p2, p3: TAffineVector;
  246. ColliderObjectInfo: TECObjectInfo; var ContactInfo: TECContact): Boolean;
  247. var
  248. trianglePlane: TECPlane;
  249. t0, t1: Double;
  250. embeddedInPlane: Boolean;
  251. signedDistToTrianglePlane: Double;
  252. normalDotVelocity: Single;
  253. temp: Double;
  254. collisionPoint: TAffineVector;
  255. foundCollison: Boolean;
  256. t: Single;
  257. planeIntersectionPoint, v: TAffineVector;
  258. velocity, base: TAffineVector;
  259. velocitySquaredLength: Single;
  260. a, b, c: Single; // Params for equation
  261. newT: Single;
  262. edge, baseToVertex: TAffineVector;
  263. edgeSquaredLength: Single;
  264. edgeDotVelocity: Single;
  265. edgeDotBaseToVertex: Single;
  266. distToCollision: Single;
  267. begin
  268. result := false;
  269. // Make the plane containing this triangle.
  270. trianglePlane := TECPlane.Create;
  271. trianglePlane.MakePlane(p1, p2, p3);
  272. // Is triangle front-facing to the velocity vector?
  273. // We only check front-facing triangles
  274. // (your choice of course)
  275. if not(trianglePlane.isFrontFacingTo(MP.CP.normalizedVelocity)) then
  276. begin
  277. trianglePlane.Free;
  278. Exit;
  279. end; // }
  280. // Get interval of plane intersection:
  281. embeddedInPlane := false;
  282. // Calculate the signed distance from sphere
  283. // position to triangle plane
  284. // V := VectorAdd(MP.CP.basePoint,MP.CP.velocity);
  285. // signedDistToTrianglePlane := trianglePlane.signedDistanceTo(v);
  286. signedDistToTrianglePlane := trianglePlane.signedDistanceTo(MP.CP.basePoint);
  287. // cache this as we’re going to use it a few times below:
  288. normalDotVelocity := VectorDotProduct(trianglePlane.Normal, MP.CP.velocity);
  289. // if sphere is travelling parrallel to the plane:
  290. if normalDotVelocity = 0 then
  291. begin
  292. if (abs(signedDistToTrianglePlane) >= 1) then
  293. begin
  294. // Sphere is not embedded in plane.
  295. // No collision possible:
  296. trianglePlane.Free;
  297. Exit;
  298. end
  299. else
  300. begin
  301. // sphere is embedded in plane.
  302. // It intersects in the whole range [0..1]
  303. embeddedInPlane := true;
  304. t0 := 0;
  305. // t1 := 1;
  306. end;
  307. end
  308. else
  309. begin
  310. // N dot D is not 0. Calculate intersection interval:
  311. t0 := (-1 - signedDistToTrianglePlane) / normalDotVelocity;
  312. t1 := (1 - signedDistToTrianglePlane) / normalDotVelocity;
  313. // Swap so t0 < t1
  314. if (t0 > t1) then
  315. begin
  316. temp := t1;
  317. t1 := t0;
  318. t0 := temp;
  319. end;
  320. // Check that at least one result is within range:
  321. if (t0 > 1) or (t1 < 0) then
  322. begin
  323. trianglePlane.Free;
  324. Exit; // Both t values are outside values [0,1] No collision possible:
  325. end;
  326. // Clamp to [0,1]
  327. if (t0 < 0) then
  328. t0 := 0;
  329. if (t0 > 1) then
  330. t0 := 1;
  331. // if (t1 < 0) then t1 := 0;
  332. // if (t1 > 1) then t1 := 1;
  333. end;
  334. // OK, at this point we have two time values t0 and t1
  335. // between which the swept sphere intersects with the
  336. // triangle plane. If any collision is to occur it must
  337. // happen within this interval.
  338. foundCollison := false;
  339. t := 1;
  340. // First we check for the easy case - collision inside
  341. // the triangle. If this happens it must be at time t0
  342. // as this is when the sphere rests on the front side
  343. // of the triangle plane. Note, this can only happen if
  344. // the sphere is not embedded in the triangle plane.
  345. if (not embeddedInPlane) then
  346. begin
  347. planeIntersectionPoint := VectorSubtract(MP.CP.basePoint,
  348. trianglePlane.Normal);
  349. v := VectorScale(MP.CP.velocity, t0);
  350. VectorAdd(planeIntersectionPoint, v);
  351. if CheckPointInTriangle(planeIntersectionPoint, p1, p2, p3) then
  352. begin
  353. foundCollison := true;
  354. t := t0;
  355. collisionPoint := planeIntersectionPoint;
  356. end;
  357. end;
  358. // if we haven’t found a collision already we’ll have to
  359. // sweep sphere against points and edges of the triangle.
  360. // Note: A collision inside the triangle (the check above)
  361. // will always happen before a vertex or edge collision!
  362. // This is why we can skip the swept test if the above
  363. // gives a collision!
  364. if (not foundCollison) then
  365. begin
  366. // some commonly used terms:
  367. velocity := MP.CP.velocity;
  368. base := MP.CP.basePoint;
  369. velocitySquaredLength := VectorNorm(velocity);
  370. // velocitySquaredLength := Sqr(VectorLength(velocity));
  371. // For each vertex or edge a quadratic equation have to
  372. // be solved. We parameterize this equation as
  373. // a*t^2 + b*t + c = 0 and below we calculate the
  374. // parameters a,b and c for each test.
  375. // Check against points:
  376. a := velocitySquaredLength;
  377. // P1
  378. v := VectorSubtract(base, p1);
  379. b := 2.0 * (VectorDotProduct(velocity, v));
  380. c := VectorNorm(v) - 1.0; // c := Sqr(VectorLength(V)) - 1.0;
  381. if (getLowestRoot(a, b, c, t, newT)) then
  382. begin
  383. t := newT;
  384. foundCollison := true;
  385. collisionPoint := p1;
  386. end;
  387. // P2
  388. v := VectorSubtract(base, p2);
  389. b := 2.0 * (VectorDotProduct(velocity, v));
  390. c := VectorNorm(v) - 1.0; // c := Sqr(VectorLength(V)) - 1.0;
  391. if (getLowestRoot(a, b, c, t, newT)) then
  392. begin
  393. t := newT;
  394. foundCollison := true;
  395. collisionPoint := p2;
  396. end;
  397. // P3
  398. v := VectorSubtract(base, p3);
  399. b := 2.0 * (VectorDotProduct(velocity, v));
  400. c := VectorNorm(v) - 1.0; // c := Sqr(VectorLength(V)) - 1.0;
  401. if (getLowestRoot(a, b, c, t, newT)) then
  402. begin
  403. t := newT;
  404. foundCollison := true;
  405. collisionPoint := p3;
  406. end;
  407. // Check against edges:
  408. // p1 -> p2:
  409. edge := VectorSubtract(p2, p1);
  410. baseToVertex := VectorSubtract(p1, base);
  411. edgeSquaredLength := VectorNorm(edge);
  412. // edgeSquaredLength := Sqr(VectorLength(edge));
  413. edgeDotVelocity := VectorDotProduct(edge, velocity);
  414. edgeDotBaseToVertex := VectorDotProduct(edge, baseToVertex);
  415. // Calculate parameters for equation
  416. a := edgeSquaredLength * -velocitySquaredLength + edgeDotVelocity *
  417. edgeDotVelocity;
  418. b := edgeSquaredLength * (2 * VectorDotProduct(velocity, baseToVertex)) -
  419. 2.0 * edgeDotVelocity * edgeDotBaseToVertex;
  420. c := edgeSquaredLength * (1 - VectorNorm(baseToVertex)) +
  421. // (1- Sqr(VectorLength(baseToVertex)) )
  422. edgeDotBaseToVertex * edgeDotBaseToVertex;
  423. // Does the swept sphere collide against infinite edge?
  424. if (getLowestRoot(a, b, c, t, newT)) then
  425. begin
  426. // Check if intersection is within line segment:
  427. temp := (edgeDotVelocity * newT - edgeDotBaseToVertex) /
  428. edgeSquaredLength;
  429. if (temp >= 0) and (temp <= 1) then
  430. begin
  431. // intersection took place within segment.
  432. t := newT;
  433. foundCollison := true;
  434. collisionPoint := VectorAdd(p1, VectorScale(edge, temp));
  435. end;
  436. end;
  437. // p1 -> p2:
  438. edge := VectorSubtract(p3, p2);
  439. baseToVertex := VectorSubtract(p2, base);
  440. edgeSquaredLength := VectorNorm(edge);
  441. // edgeSquaredLength := Sqr(VectorLength(edge));
  442. edgeDotVelocity := VectorDotProduct(edge, velocity);
  443. edgeDotBaseToVertex := VectorDotProduct(edge, baseToVertex);
  444. // Calculate parameters for equation
  445. a := edgeSquaredLength * -velocitySquaredLength + edgeDotVelocity *
  446. edgeDotVelocity;
  447. b := edgeSquaredLength * (2 * VectorDotProduct(velocity, baseToVertex)) -
  448. 2.0 * edgeDotVelocity * edgeDotBaseToVertex;
  449. c := edgeSquaredLength * (1 - VectorNorm(baseToVertex)) +
  450. // (1- Sqr(VectorLength(baseToVertex)) )
  451. edgeDotBaseToVertex * edgeDotBaseToVertex;
  452. // Does the swept sphere collide against infinite edge?
  453. if (getLowestRoot(a, b, c, t, newT)) then
  454. begin
  455. // Check if intersection is within line segment:
  456. temp := (edgeDotVelocity * newT - edgeDotBaseToVertex) /
  457. edgeSquaredLength;
  458. if (temp >= 0) and (temp <= 1) then
  459. begin
  460. // intersection took place within segment.
  461. t := newT;
  462. foundCollison := true;
  463. collisionPoint := VectorAdd(p2, VectorScale(edge, temp));
  464. end;
  465. end;
  466. // p3 -> p1:
  467. edge := VectorSubtract(p1, p3);
  468. baseToVertex := VectorSubtract(p3, base);
  469. edgeSquaredLength := VectorNorm(edge);
  470. // edgeSquaredLength := Sqr(VectorLength(edge));
  471. edgeDotVelocity := VectorDotProduct(edge, velocity);
  472. edgeDotBaseToVertex := VectorDotProduct(edge, baseToVertex);
  473. // Calculate parameters for equation
  474. a := edgeSquaredLength * -velocitySquaredLength + edgeDotVelocity *
  475. edgeDotVelocity;
  476. b := edgeSquaredLength * (2 * VectorDotProduct(velocity, baseToVertex)) -
  477. 2.0 * edgeDotVelocity * edgeDotBaseToVertex;
  478. c := edgeSquaredLength * (1 - VectorNorm(baseToVertex)) +
  479. // (1- Sqr(VectorLength(baseToVertex)) )
  480. edgeDotBaseToVertex * edgeDotBaseToVertex;
  481. // Does the swept sphere collide against infinite edge?
  482. if (getLowestRoot(a, b, c, t, newT)) then
  483. begin
  484. // Check if intersection is within line segment:
  485. temp := (edgeDotVelocity * newT - edgeDotBaseToVertex) /
  486. edgeSquaredLength;
  487. if (temp >= 0) and (temp <= 1) then
  488. begin
  489. // intersection took place within segment.
  490. t := newT;
  491. foundCollison := true;
  492. collisionPoint := VectorAdd(p3, VectorScale(edge, temp));
  493. end;
  494. end;
  495. end;
  496. // Set result:
  497. if foundCollison then
  498. begin
  499. // distance to collision: ’t’ is time of collision
  500. distToCollision := t * VectorLength(MP.CP.velocity);
  501. result := true;
  502. with ContactInfo do
  503. begin
  504. Position := collisionPoint;
  505. SurfaceNormal := trianglePlane.Normal;
  506. Distance := distToCollision;
  507. ObjectInfo := ColliderObjectInfo;
  508. end;
  509. // Does this triangle qualify for the closest hit?
  510. // it does if it’s the first hit or the closest
  511. if ((MP.CP.foundCollision = false) or
  512. (distToCollision < MP.CP.nearestDistance)) and (MP.ObjectInfo.Solid) and
  513. (ColliderObjectInfo.Solid) then
  514. begin
  515. // Collision information nessesary for sliding
  516. MP.CP.nearestDistance := distToCollision;
  517. MP.CP.NearestObject := ColliderObjectInfo.ObjectID;
  518. MP.CP.intersectionPoint := collisionPoint;
  519. MP.CP.intersectionNormal := trianglePlane.Normal;
  520. MP.CP.foundCollision := true;
  521. end;
  522. end;
  523. trianglePlane.Free;
  524. end;
  525. function CheckPoint(var MP: TECMovePack; pPos, pNormal: TAffineVector;
  526. ColliderObjectInfo: TECObjectInfo): Boolean;
  527. var
  528. newPos: TAffineVector;
  529. Distance: Single;
  530. foundCollision: Boolean;
  531. begin
  532. newPos := VectorAdd(MP.CP.basePoint, MP.CP.velocity);
  533. // *** Need to check if the ellipsoid is embedded ***
  534. Distance := VectorDistance(pPos, newPos) - 1; // 1 is the sphere radius
  535. if Distance < 0 then
  536. Distance := 0;
  537. if (VectorNorm(MP.CP.velocity) >= VectorDistance2(MP.CP.basePoint, pPos)) then
  538. Distance := 0;
  539. foundCollision := Distance <= (cECCloseDistance * MP.UnitScale);
  540. // Very small distance
  541. result := foundCollision;
  542. // Set result:
  543. if foundCollision then
  544. begin
  545. // Add a contact
  546. SetLength(MP.Contacts, Length(MP.Contacts) + 1);
  547. with MP.Contacts[High(MP.Contacts)] do
  548. begin
  549. Position := pPos;
  550. ScaleVector(Position, MP.Radius);
  551. SurfaceNormal := pNormal;
  552. Distance := Distance;
  553. ObjectInfo := ColliderObjectInfo;
  554. end;
  555. if ((MP.CP.foundCollision = false) or (Distance < MP.CP.nearestDistance))
  556. and (MP.ObjectInfo.Solid) and (ColliderObjectInfo.Solid) then
  557. begin
  558. // Collision information nessesary for sliding
  559. MP.CP.nearestDistance := Distance;
  560. MP.CP.NearestObject := ColliderObjectInfo.ObjectID;
  561. MP.CP.intersectionPoint := pPos;
  562. MP.CP.intersectionNormal := pNormal;
  563. MP.CP.foundCollision := true;
  564. end;
  565. end;
  566. end;
  567. function CheckEllipsoid(var MP: TECMovePack; ePos, eRadius: TAffineVector;
  568. ColliderObjectInfo: TECObjectInfo): Boolean;
  569. var
  570. newPos, nA, rA, nB, rB, iPoint, iNormal: TAffineVector;
  571. dist: Single;
  572. begin
  573. result := false;
  574. // Check if the new position has passed the ellipse
  575. if VectorNorm(MP.CP.velocity) < VectorDistance2(MP.CP.basePoint, ePos) then
  576. newPos := VectorAdd(MP.CP.basePoint, MP.CP.velocity)
  577. else
  578. begin
  579. nA := VectorScale(VectorNormalize(VectorSubtract(MP.CP.basePoint,
  580. ePos)), 1);
  581. newPos := VectorAdd(ePos, nA);
  582. end;
  583. // Find intersection
  584. nA := VectorNormalize(VectorDivide(VectorSubtract(ePos, newPos), eRadius));
  585. rA := VectorAdd(newPos, nA);
  586. nB := VectorNormalize(VectorDivide(VectorSubtract(rA, ePos), eRadius));
  587. ScaleVector(nB, eRadius);
  588. // Is colliding?
  589. dist := VectorDistance(newPos, ePos) - 1 - VectorLength(nB);
  590. if (dist > cECCloseDistance) then
  591. Exit;
  592. rB := VectorAdd(ePos, nB);
  593. iPoint := rB;
  594. iNormal := VectorNormalize(VectorDivide(VectorSubtract(newPos, ePos),
  595. eRadius));
  596. if dist < 0 then
  597. dist := 0;
  598. // Add a contact
  599. SetLength(MP.Contacts, Length(MP.Contacts) + 1);
  600. with MP.Contacts[High(MP.Contacts)] do
  601. begin
  602. Position := iPoint;
  603. ScaleVector(Position, MP.Radius);
  604. SurfaceNormal := iNormal;
  605. Distance := dist;
  606. ObjectInfo := ColliderObjectInfo;
  607. end;
  608. if ((MP.CP.foundCollision = false) or (dist < MP.CP.nearestDistance)) and
  609. (MP.ObjectInfo.Solid) and (ColliderObjectInfo.Solid) then
  610. begin
  611. MP.CP.nearestDistance := dist;
  612. MP.CP.NearestObject := ColliderObjectInfo.ObjectID;
  613. MP.CP.intersectionPoint := iPoint;
  614. MP.CP.intersectionNormal := iNormal;
  615. MP.CP.foundCollision := true;
  616. end;
  617. result := true;
  618. end;
  619. procedure CheckCollisionFreeForm(var MP: TECMovePack);
  620. var
  621. n, i, t, k: Integer;
  622. p: PGLOctreeNode;
  623. p1, p2, p3: PAffineVector;
  624. v1, v2, v3: TAffineVector;
  625. Collided: Boolean;
  626. ContactInfo: TECContact;
  627. begin
  628. // For each freeform
  629. for n := 0 to High(MP.Freeforms) do
  630. begin
  631. // For each octree node
  632. for i := 0 to High(MP.Freeforms[n].OctreeNodes) do
  633. begin
  634. p := MP.Freeforms[n].OctreeNodes[i];
  635. // for each triangle
  636. for t := 0 to High(p.TriArray) do
  637. begin
  638. k := p.TriArray[t];
  639. // These are the vertices of the triangle to check
  640. p1 := @MP.Freeforms[n].triangleFiler^.List[k];
  641. p2 := @MP.Freeforms[n].triangleFiler^.List[k + 1];
  642. p3 := @MP.Freeforms[n].triangleFiler^.List[k + 2];
  643. if not MP.Freeforms[n].InvertedNormals then
  644. begin
  645. SetVector(v1, VectorTransform(p1^,
  646. MP.Freeforms[n].ObjectInfo.AbsoluteMatrix));
  647. SetVector(v2, VectorTransform(p2^,
  648. MP.Freeforms[n].ObjectInfo.AbsoluteMatrix));
  649. SetVector(v3, VectorTransform(p3^,
  650. MP.Freeforms[n].ObjectInfo.AbsoluteMatrix));
  651. end
  652. else
  653. begin
  654. SetVector(v1, VectorTransform(p3^,
  655. MP.Freeforms[n].ObjectInfo.AbsoluteMatrix));
  656. SetVector(v2, VectorTransform(p2^,
  657. MP.Freeforms[n].ObjectInfo.AbsoluteMatrix));
  658. SetVector(v3, VectorTransform(p1^,
  659. MP.Freeforms[n].ObjectInfo.AbsoluteMatrix));
  660. end;
  661. {$IFDEF DEBUG}
  662. AddDebugTri(v1, v2, v3); // @Debug
  663. {$ENDIF}
  664. // Set the triangles to the ellipsoid space
  665. v1 := VectorDivide(v1, MP.Radius);
  666. v2 := VectorDivide(v2, MP.Radius);
  667. v3 := VectorDivide(v3, MP.Radius);
  668. Collided := CheckTriangle(MP, v1, v2, v3, MP.Freeforms[n].ObjectInfo,
  669. ContactInfo);
  670. // Add a contact
  671. if Collided then
  672. begin
  673. SetLength(MP.Contacts, Length(MP.Contacts) + 1);
  674. ScaleVector(ContactInfo.Position, MP.Radius);
  675. MP.Contacts[High(MP.Contacts)] := ContactInfo;
  676. end;
  677. end;
  678. end;
  679. end; // for n
  680. end;
  681. procedure CheckCollisionTriangles(var MP: TECMovePack);
  682. var
  683. n, i: Integer;
  684. p1, p2, p3: TAffineVector;
  685. Collided: Boolean;
  686. ContactInfo: TECContact;
  687. begin
  688. for n := 0 to High(MP.TriMeshes) do
  689. begin
  690. for i := 0 to High(MP.TriMeshes[n].Triangles) do
  691. begin
  692. {$IFDEF DEBUG}
  693. AddDebugTri(MP.TriMeshes[n].Triangles[i].p1,
  694. MP.TriMeshes[n].Triangles[i].p2, MP.TriMeshes[n].Triangles[i].p3);
  695. // @Debug
  696. {$ENDIF}
  697. // These are the vertices of the triangle to check
  698. p1 := VectorDivide(MP.TriMeshes[n].Triangles[i].p1, MP.Radius);
  699. p2 := VectorDivide(MP.TriMeshes[n].Triangles[i].p2, MP.Radius);
  700. p3 := VectorDivide(MP.TriMeshes[n].Triangles[i].p3, MP.Radius);
  701. Collided := CheckTriangle(MP, p1, p2, p3, MP.TriMeshes[n].ObjectInfo,
  702. ContactInfo);
  703. // Add a contact
  704. if Collided then
  705. begin
  706. SetLength(MP.Contacts, Length(MP.Contacts) + 1);
  707. ScaleVector(ContactInfo.Position, MP.Radius);
  708. MP.Contacts[High(MP.Contacts)] := ContactInfo;
  709. end;
  710. end;
  711. end;
  712. end;
  713. procedure CheckCollisionColliders(var MP: TECMovePack);
  714. var
  715. i: Integer;
  716. p1, p2: TAffineVector;
  717. begin
  718. for i := 0 to High(MP.Colliders) do
  719. begin
  720. p1 := VectorDivide(MP.Colliders[i].Position, MP.Radius);
  721. p2 := VectorDivide(MP.Colliders[i].Radius, MP.Radius);
  722. case MP.Colliders[i].Shape of
  723. csEllipsoid:
  724. CheckEllipsoid(MP, p1, p2, MP.Colliders[i].ObjectInfo);
  725. csPoint:
  726. CheckPoint(MP, p1, p2, MP.Colliders[i].ObjectInfo);
  727. end;
  728. end;
  729. end;
  730. procedure CollideAndSlide(var MP: TECMovePack);
  731. var
  732. eSpacePosition, eSpaceVelocity: TAffineVector;
  733. begin
  734. // calculate position and velocity in eSpace
  735. eSpacePosition := VectorDivide(MP.Position, MP.Radius);
  736. eSpaceVelocity := VectorDivide(MP.velocity, MP.Radius);
  737. // Iterate until we have our final position.
  738. MP.collisionRecursionDepth := 0;
  739. CollideWithWorld(MP, eSpacePosition, eSpaceVelocity, MP.VelocityCollided);
  740. // Add gravity pull:
  741. // Set the new R3 position (convert back from eSpace to R3
  742. MP.GroundNormal := NullVector;
  743. MP.GravityCollided := false;
  744. if not VectorIsNull(MP.Gravity) then
  745. begin
  746. eSpaceVelocity := VectorDivide(MP.Gravity, MP.Radius);
  747. eSpacePosition := MP.ResultPos;
  748. MP.collisionRecursionDepth := 0;
  749. CollideWithWorld(MP, eSpacePosition, eSpaceVelocity, MP.GravityCollided);
  750. if MP.GravityCollided then
  751. MP.GroundNormal := MP.CP.intersectionNormal;
  752. end;
  753. // Convert final result back to R3:
  754. ScaleVector(MP.ResultPos, MP.Radius);
  755. end;
  756. procedure CollideWithWorld(var MP: TECMovePack; pos, vel: TAffineVector;
  757. var HasCollided: Boolean);
  758. var
  759. veryCloseDistance: Single;
  760. destinationPoint, newBasePoint, v: TAffineVector;
  761. slidePlaneOrigin, slidePlaneNormal: TAffineVector;
  762. slidingPlane: TECPlane;
  763. newDestinationPoint, newVelocityVector: TAffineVector;
  764. begin
  765. // First we set to false (no collision)
  766. if (MP.collisionRecursionDepth = 0) then
  767. HasCollided := false;
  768. veryCloseDistance := cECCloseDistance * MP.UnitScale;
  769. MP.ResultPos := pos;
  770. // do we need to worry?
  771. if (MP.collisionRecursionDepth > MP.MaxRecursionDepth) then
  772. Exit;
  773. // Ok, we need to worry:
  774. MP.CP.velocity := vel;
  775. MP.CP.normalizedVelocity := VectorNormalize(vel);
  776. MP.CP.basePoint := pos;
  777. MP.CP.foundCollision := false;
  778. // Check for collision (calls the collision routines)
  779. CheckCollisionFreeForm(MP);
  780. CheckCollisionTriangles(MP);
  781. CheckCollisionColliders(MP);
  782. // If no collision we just move along the velocity
  783. if (not MP.CP.foundCollision) then
  784. begin
  785. MP.ResultPos := VectorAdd(pos, vel);
  786. Exit;
  787. end;
  788. // *** Collision occured ***
  789. if (MP.CP.foundCollision) then
  790. HasCollided := true;
  791. MP.NearestObject := MP.CP.NearestObject;
  792. // The original destination point
  793. destinationPoint := VectorAdd(pos, vel);
  794. newBasePoint := pos;
  795. // only update if we are not already very close
  796. // and if so we only move very close to intersection..not
  797. // to the exact spot.
  798. if (MP.CP.nearestDistance >= veryCloseDistance) then
  799. begin
  800. v := vel;
  801. VectorSetLength(v, MP.CP.nearestDistance - veryCloseDistance);
  802. newBasePoint := VectorAdd(MP.CP.basePoint, v);
  803. // Adjust polygon intersection point (so sliding
  804. // plane will be unaffected by the fact that we
  805. // move slightly less than collision tells us)
  806. NormalizeVector(v);
  807. ScaleVector(v, veryCloseDistance);
  808. SubtractVector(MP.CP.intersectionPoint, v);
  809. end;
  810. // Determine the sliding plane
  811. slidePlaneOrigin := MP.CP.intersectionPoint;
  812. slidePlaneNormal := VectorSubtract(newBasePoint, MP.CP.intersectionPoint);
  813. NormalizeVector(slidePlaneNormal);
  814. slidingPlane := TECPlane.Create;
  815. slidingPlane.MakePlane(slidePlaneOrigin, slidePlaneNormal);
  816. v := VectorScale(slidePlaneNormal,
  817. slidingPlane.signedDistanceTo(destinationPoint));
  818. newDestinationPoint := VectorSubtract(destinationPoint, v);
  819. // Generate the slide vector, which will become our new
  820. // velocity vector for the next iteration
  821. newVelocityVector := VectorSubtract(newDestinationPoint,
  822. MP.CP.intersectionPoint);
  823. if (MP.CP.nearestDistance = 0) then
  824. begin
  825. v := VectorNormalize(VectorSubtract(newBasePoint, MP.CP.intersectionPoint));
  826. v := VectorScale(v, veryCloseDistance);
  827. AddVector(newVelocityVector, v);
  828. end;
  829. // Recurse:
  830. // dont recurse if the new velocity is very small
  831. if (VectorNorm(newVelocityVector) < Sqr(veryCloseDistance)) then
  832. begin
  833. MP.ResultPos := newBasePoint;
  834. slidingPlane.Free;
  835. Exit;
  836. end;
  837. slidingPlane.Free;
  838. Inc(MP.collisionRecursionDepth);
  839. CollideWithWorld(MP, newBasePoint, newVelocityVector, HasCollided);
  840. end;
  841. end.