-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathuGBEUtils3D.pas
178 lines (155 loc) · 6.66 KB
/
uGBEUtils3D.pas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
unit uGBEUtils3D;
interface
uses System.Math.Vectors, System.Types,System.Classes, FMX.Objects3D, Math, FMX.Controls3D, FMX.Graphics, FMX.Types3D, System.UITypes, FMX.Effects,
System.UIConsts, System.SysUtils, System.RTLConsts, FMX.Types, FMX.Ani, FMX.Viewport3D;
type
TCustomMeshHelper = class(TCustomMesh);
TGBECollisionRetour = record
bool : boolean;
objet : TControl3D;
end;
function Barycentre(p1, p2, p3 : TPoint3D; p4 : TPointF):single;
function CalculerHauteur(Mesh : TMesh; P: TPoint3D; miseAEchelle : single; subDivX, subDivZ: integer) : single;
function SizeOf3D(const unObjet3D: TControl3D): TPoint3D;
function DetectionCollisionObstacle(mesh : TMesh; objet : TControl3D):TGBECollisionRetour;
procedure interactionIHM(viewport : TViewport3D);
function collisionDummyChilds(aDummy: TDummy; objet3D : TControl3D): TGBECollisionRetour;
function collisionEntre2Objets(objet1, objet2 : TControl3D): TGBECollisionRetour;
implementation
//------------------------------------------------------------------------------------------
function Barycentre(p1, p2, p3 : TPoint3D; p4 : TPointF):single;
var
det, l1, l2, l3, d1, d2, d3, t1,t2 : single;
begin
d1 := (p2.z - p3.z); // Petites optimisations pour ne faire les calculs intermédiaires qu'une seule fois à chaque itération
d2 := (p3.x - p2.x);
d3 := (p1.x - p3.x);
det := 1 / ((d1 * d3) + (d2 * (p1.z - p3.z))); // Inverse, permet de remplacer les divisions gourmandes par une multiplication (ainsi, on ne fait la division qu'une fois au lieu de deux à chaque itération)
t1 := (p4.x - p3.x);
t2 := (p4.y - p3.z);
l1 := (( d1 * t1) + (d2 * t2 )) * det;
l2 := ((p3.z - p1.z) * (t1 + (d3 * t2 ))) * det;
l3 := 1 - l1 - l2;
result := l1 * p1.y + l2 * p2.y + l3 * p3.y;
end;
//------------------------------------------------------------------------------------------
function CalculerHauteur(Mesh : TMesh; P: TPoint3D; miseAEchelle : single; subDivX, subDivZ : integer) : single;
var
grilleX, grilleZ, indiceMaille : integer;
xCoord, zCoord, hauteurCalculee, demiDepth, demiWidth, subWidth, subDepth : single;
begin
if (subDivX = 0) or (subDivZ = 0) then
begin
result := 0;
exit;
end;
demiWidth := mesh.width * 0.5;
demiDepth := mesh.Depth * 0.5;
subWidth := mesh.Width / subDivX;
subDepth := mesh.Depth / subDivZ;
// Détermination des indices permettant d'accéder à la maille en fonction de la position du joueur
grilleX := trunc((P.X+demiWidth) / subWidth);
grilleZ := trunc((P.Z+demiDepth) / subDepth);
// Si on est en dehors du mesh, on force (arbitrairement) la hauteur
if (grilleX >= subdivX) or (grilleZ >= subDivZ) or (grilleX < 0) or (grilleZ < 0) then result := 0
else
begin
xCoord := Frac((P.X+demiWidth) / subWidth); // position X dans la maille courante
zCoord := Frac((P.Z+demiDepth) / subDepth); // position y dans la maille courante
// On calcule la hauteur en fonction des 3 sommets du triangle dans lequel se trouve le joueur
// On détermine dans quel triangle on est
indiceMaille := (grilleZ * subDivZ * 4) + grilleX *4;
if xCoord <= (1 - zCoord) then
begin
hauteurCalculee := Barycentre(TPoint3D.Create(0,mesh.data.VertexBuffer.Vertices[indiceMaille].Y,0),
TPoint3D.Create(1,mesh.data.VertexBuffer.Vertices[indiceMaille + 1].Y, 0),
TPoint3D.Create(0,mesh.data.VertexBuffer.Vertices[indiceMaille + 3].Y, 1),
TPointF.Create(xCoord, zCoord));
end
else
begin
hauteurCalculee := Barycentre(TPoint3D.Create(1,mesh.data.VertexBuffer.Vertices[indiceMaille +1].Y,0),
TPoint3D.Create(1,mesh.data.VertexBuffer.Vertices[indiceMaille +2].Y,1),
TPoint3D.Create(0,mesh.data.VertexBuffer.Vertices[indiceMaille +3].Y,1),
TPointF.Create(xCoord, zCoord));
end;
result := hauteurCalculee*miseAEchelle ;//- demiHeight ; //- mesh.Height;
end;
end;
// Renvoi les dimensions de l'objet 3D
function SizeOf3D(const unObjet3D: TControl3D): TPoint3D;
begin
Result :=NullPoint3D;
if unObjet3D <> nil then
result := Point3D(unObjet3D.Width, unObjet3D.Height, unObjet3D.Depth);
end;
//------------------------------------------------------------------------------------------
// Détection de collision "Bounding Box" entre le mesh (TGBEHeightmap et ses objets enfants qui ont leur tag à 1) et un objet
function DetectionCollisionObstacle(mesh : TMesh; objet : TControl3D):TGBECollisionRetour;
var
unObjet3D:TControl3D; // l'objet en cours de rendu
i, j : integer;
resultat : TGBECollisionRetour;
begin
resultat.bool := false;
resultat.objet := nil;
// Test collision avec enfants directs de mSol
for I := 0 to mesh.ChildrenCount-1 do
begin
if mesh.Children[i].Tag = 1 then // Il faut que l'enfant du TMesh ait son tag à 1
begin
for j := 0 to mesh.Children[i].ChildrenCount-1 do
begin
// On travail sur l'objet qui est en train d'être calculé
unObjet3D := TControl3D(mesh.Children[i].Children[j]);
if collisionEntre2Objets(unObjet3D, objet).bool then begin
resultat.bool := true;
resultat.objet := unObjet3D;
break;
end;
end;
end;
end;
result := resultat;
end;
//------------------------------------------------------------------------------------------
procedure interactionIHM(viewport : TViewport3D);
var
obj : TFmxObject;
begin
for obj in Viewport.Children do
begin
if obj is TAnimation then TAnimation(obj).ProcessTick(0,0);
end;
end;
//------------------------------------------------------------------------------------------
function collisionDummyChilds(aDummy: TDummy; objet3D : TControl3D): TGBECollisionRetour;
var
obj : TFmxObject;
resultat : TGBECollisionRetour;
begin
resultat.bool := false;
resultat.objet := nil;
for obj in aDummy.Children do begin
if (obj as TControl3D).visible then begin
resultat := collisionEntre2Objets(objet3D, (obj as TControl3D));
if resultat.bool then break;
end;
end;
result := resultat;
end;
function collisionEntre2Objets(objet1, objet2 : TControl3D): TGBECollisionRetour;
var
DistanceEntreObjets,distanceMinimum: TPoint3D;
begin
result.objet := nil;
result.bool := false;
DistanceEntreObjets := objet1.Position.Point - objet2.Position.Point;
distanceMinimum := (SizeOf3D(objet1) + SizeOf3D(objet2)) * 0.5;
if ((Abs(DistanceEntreObjets.X) < distanceMinimum.X) and (Abs(DistanceEntreObjets.Y) < distanceMinimum.Y) and
(Abs(DistanceEntreObjets.Z) < distanceMinimum.Z)) then begin
result.bool := true;
result.objet := objet2;
end;
end;
end.