GXS.EllipseCollision.pas 27 KB

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