uGBEUtils3D.pas 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. unit uGBEUtils3D;
  2. interface
  3. uses
  4. System.Math.Vectors,
  5. System.Types,
  6. System.Classes,
  7. FMX.Objects3D,
  8. System.Math,
  9. FMX.Controls3D,
  10. FMX.Graphics,
  11. FMX.Types3D,
  12. System.UITypes,
  13. FMX.Effects,
  14. System.UIConsts,
  15. System.SysUtils,
  16. System.RTLConsts,
  17. FMX.Types,
  18. FMX.Ani,
  19. FMX.Viewport3D;
  20. type
  21. TCustomMeshHelper = class(TCustomMesh);
  22. TGBECollisionRetour = record
  23. bool: boolean;
  24. objet: TControl3D;
  25. end;
  26. function Barycentre(p1, p2, p3: TPoint3D; p4: TPointF): single;
  27. function CalculateHeight(Mesh: TMesh; P: TPoint3D; miseAEchelle: single;
  28. subDivX, subDivZ: integer): single;
  29. function SizeOf3D(const unObjet3D: TControl3D): TPoint3D;
  30. function DetectionCollisionObstacle(Mesh: TMesh; objet: TControl3D)
  31. : TGBECollisionRetour;
  32. procedure InteractionIHM(viewport: TViewport3D);
  33. function CollisionDummyChilds(aDummy: TDummy; objet3D: TControl3D)
  34. : TGBECollisionRetour;
  35. function CollisionEntre2Objets(objet1, objet2: TControl3D): TGBECollisionRetour;
  36. implementation // -------------------------------------------------------------
  37. function Barycentre(p1, p2, p3: TPoint3D; p4: TPointF): single;
  38. var
  39. det, l1, l2, l3, d1, d2, d3, t1, t2: single;
  40. begin
  41. d1 := (p2.z - p3.z);
  42. // Small optimizations to only do intermediate calculations once in each iteration
  43. d2 := (p3.x - p2.x);
  44. d3 := (p1.x - p3.x);
  45. det := 1 / ((d1 * d3) + (d2 * (p1.z - p3.z)));
  46. // Inverse, allows to replace greedy divisions by a multiplication
  47. // (thus, we only do the division once instead of twice at each iteration)
  48. t1 := (p4.x - p3.x);
  49. t2 := (p4.y - p3.z);
  50. l1 := ((d1 * t1) + (d2 * t2)) * det;
  51. l2 := ((p3.z - p1.z) * (t1 + (d3 * t2))) * det;
  52. l3 := 1 - l1 - l2;
  53. Result := l1 * p1.y + l2 * p2.y + l3 * p3.y;
  54. end;
  55. // ------------------------------------------------------------------------------------------
  56. function CalculateHeight(Mesh: TMesh; P: TPoint3D; miseAEchelle: single;
  57. subDivX, subDivZ: integer): single;
  58. var
  59. grilleX, grilleZ, indiceMaille: integer;
  60. xCoord, zCoord, hauteurCalculee, demiDepth, demiWidth, subWidth,
  61. subDepth: single;
  62. begin
  63. if (subDivX = 0) or (subDivZ = 0) then
  64. begin
  65. Result := 0;
  66. Exit;
  67. end;
  68. demiWidth := Mesh.width * 0.5;
  69. demiDepth := Mesh.Depth * 0.5;
  70. subWidth := Mesh.width / subDivX;
  71. subDepth := Mesh.Depth / subDivZ;
  72. // Determination of the indices allowing access to the mesh according to the position of the player
  73. grilleX := trunc((P.x + demiWidth) / subWidth);
  74. grilleZ := trunc((P.z + demiDepth) / subDepth);
  75. // If we are outside the mesh, we force (arbitrarily) the height
  76. if (grilleX >= subDivX) or (grilleZ >= subDivZ) or (grilleX < 0) or
  77. (grilleZ < 0) then
  78. Result := 0
  79. else
  80. begin
  81. xCoord := Frac((P.x + demiWidth) / subWidth);
  82. // X position in the current mesh
  83. zCoord := Frac((P.z + demiDepth) / subDepth);
  84. // Y position in the current mesh
  85. // The height is calculated based on the 3 vertices of the triangle in which the player is located.
  86. // We determine which triangle we are in
  87. indiceMaille := (grilleZ * subDivZ * 4) + grilleX * 4;
  88. if xCoord <= (1 - zCoord) then
  89. begin
  90. hauteurCalculee :=
  91. Barycentre(TPoint3D.Create(0, Mesh.data.VertexBuffer.Vertices
  92. [indiceMaille].y, 0), TPoint3D.Create(1, Mesh.data.VertexBuffer.Vertices
  93. [indiceMaille + 1].y, 0), TPoint3D.Create(0,
  94. Mesh.data.VertexBuffer.Vertices[indiceMaille + 3].y, 1),
  95. TPointF.Create(xCoord, zCoord));
  96. end
  97. else
  98. begin
  99. hauteurCalculee :=
  100. Barycentre(TPoint3D.Create(1, Mesh.data.VertexBuffer.Vertices
  101. [indiceMaille + 1].y, 0), TPoint3D.Create(1,
  102. Mesh.data.VertexBuffer.Vertices[indiceMaille + 2].y, 1),
  103. TPoint3D.Create(0, Mesh.data.VertexBuffer.Vertices[indiceMaille + 3].y,
  104. 1), TPointF.Create(xCoord, zCoord));
  105. end;
  106. Result := hauteurCalculee * miseAEchelle; // - demiHeight ; //- mesh.Height;
  107. end;
  108. end;
  109. // Returns the dimensions of the 3D object
  110. function SizeOf3D(const unObjet3D: TControl3D): TPoint3D;
  111. begin
  112. Result := NullPoint3D;
  113. if unObjet3D <> nil then
  114. Result := Point3D(unObjet3D.width, unObjet3D.Height, unObjet3D.Depth);
  115. end;
  116. // ------------------------------------------------------------------------------------------
  117. // "Bounding Box" collision detection between the mesh
  118. // (TGBEHeightmap and its child objects which have their tag at 1) and an object
  119. function DetectionCollisionObstacle(Mesh: TMesh; objet: TControl3D)
  120. : TGBECollisionRetour;
  121. var
  122. unObjet3D: TControl3D; // the object being rendered
  123. i, j: integer;
  124. resultat: TGBECollisionRetour;
  125. begin
  126. resultat.bool := false;
  127. resultat.objet := nil;
  128. // Collision test with direct children of mSol
  129. for i := 0 to Mesh.ChildrenCount - 1 do
  130. begin
  131. if Mesh.Children[i].Tag = 1 then
  132. // The TMesh child must have his tag at 1
  133. begin
  134. for j := 0 to Mesh.Children[i].ChildrenCount - 1 do
  135. begin
  136. // We are working on the object that is being calculated
  137. unObjet3D := TControl3D(Mesh.Children[i].Children[j]);
  138. if collisionEntre2Objets(unObjet3D, objet).bool then
  139. begin
  140. resultat.bool := true;
  141. resultat.objet := unObjet3D;
  142. Break;
  143. end;
  144. end;
  145. end;
  146. end;
  147. Result := resultat;
  148. end;
  149. // ------------------------------------------------------------------------------------------
  150. procedure InteractionIHM(viewport: TViewport3D);
  151. var
  152. obj: TFmxObject;
  153. begin
  154. for obj in Viewport.Children do
  155. begin
  156. if obj is TAnimation then
  157. TAnimation(obj).ProcessTick(0, 0);
  158. end;
  159. end;
  160. // ------------------------------------------------------------------------------------------
  161. function CollisionDummyChilds(aDummy: TDummy; objet3D: TControl3D)
  162. : TGBECollisionRetour;
  163. var
  164. obj: TFmxObject;
  165. resultat: TGBECollisionRetour;
  166. begin
  167. resultat.bool := false;
  168. resultat.objet := nil;
  169. for obj in aDummy.Children do
  170. begin
  171. if (obj as TControl3D).visible then
  172. begin
  173. resultat := collisionEntre2Objets(objet3D, (obj as TControl3D));
  174. if resultat.bool then
  175. Break;
  176. end;
  177. end;
  178. Result := resultat;
  179. end;
  180. function CollisionEntre2Objets(objet1, objet2: TControl3D): TGBECollisionRetour;
  181. var
  182. DistanceEntreObjets, distanceMinimum: TPoint3D;
  183. begin
  184. result.objet := nil;
  185. result.bool := false;
  186. DistanceEntreObjets := objet1.Position.Point - objet2.Position.Point;
  187. distanceMinimum := (SizeOf3D(objet1) + SizeOf3D(objet2)) * 0.5;
  188. if ((Abs(DistanceEntreObjets.x) < distanceMinimum.x) and
  189. (Abs(DistanceEntreObjets.y) < distanceMinimum.y) and
  190. (Abs(DistanceEntreObjets.z) < distanceMinimum.z)) then
  191. begin
  192. result.bool := True;
  193. result.objet := objet2;
  194. end;
  195. end;
  196. end.