GLEllipseCollision.pas 28 KB

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