1 /*
2 * Copyright 2011-2012 Arx Libertatis Team (see the AUTHORS file)
3 *
4 * This file is part of Arx Libertatis.
5 *
6 * Arx Libertatis is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Arx Libertatis is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with Arx Libertatis. If not, see <http://www.gnu.org/licenses/>.
18 */
19 /* Based on:
20 ===========================================================================
21 ARX FATALIS GPL Source Code
22 Copyright (C) 1999-2010 Arkane Studios SA, a ZeniMax Media company.
23
24 This file is part of the Arx Fatalis GPL Source Code ('Arx Fatalis Source Code').
25
26 Arx Fatalis Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
27 License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
28
29 Arx Fatalis Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
30 warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
31
32 You should have received a copy of the GNU General Public License along with Arx Fatalis Source Code. If not, see
33 <http://www.gnu.org/licenses/>.
34
35 In addition, the Arx Fatalis Source Code is also subject to certain additional terms. You should have received a copy of these
36 additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Arx
37 Fatalis Source Code. If not, please request a copy in writing from Arkane Studios at the address below.
38
39 If you have questions concerning this license or the applicable additional terms, you may contact in writing Arkane Studios, c/o
40 ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
41 ===========================================================================
42 */
43 // Code: Cyril Meynier
44 //
45 // Copyright (c) 1999-2001 ARKANE Studios SA. All rights reserved
46
47 #include "physics/CollisionShapes.h"
48
49 #include <cstring>
50
51 #include "game/Entity.h"
52 #include "graphics/Math.h"
53 #include "graphics/data/MeshManipulation.h"
54
55 using std::max;
56 using std::vector;
57 using std::memset;
58
EERIE_COLLISION_Cylinder_Create(Entity * io)59 void EERIE_COLLISION_Cylinder_Create(Entity * io)
60 {
61 if (io == NULL) return;
62
63 EERIE_3DOBJ * obj = io->obj;
64
65 if (!obj) return;
66
67 if (obj->vertexlist.empty())
68 {
69 io->physics.cyl.height = 0.f;
70 return;
71 }
72
73 io->physics.cyl.origin = obj->vertexlist[obj->origin].v;
74
75 float d = 0.f;
76 float height = 0.f;
77 for(size_t i = 0; i < obj->vertexlist.size(); i++) {
78 if((i != (size_t)obj->origin) && (EEfabs(io->physics.cyl.origin.y - obj->vertexlist[i].v.y) < 20.f)) {
79 d = max(d, dist(io->physics.cyl.origin, obj->vertexlist[i].v));
80 }
81 height = max(height, io->physics.cyl.origin.y - obj->vertexlist[i].v.y);
82 }
83
84 if ((d == 0.f) || (height == 0.f))
85 {
86 io->physics.cyl.height = 0.f;
87 return;
88 }
89
90 io->original_radius = d * 1.2f;
91 io->original_height = -height;
92 io->physics.cyl.origin = io->pos;
93
94 if (io->original_height > -40)
95 {
96 float v = (-io->original_height) * ( 1.0f / 40 );
97 io->original_radius *= (0.5f + v * 0.5f);
98 }
99
100 if (io->original_height > -40) io->original_height = -40;
101
102 if (io->original_height < -165) io->original_height = -165;
103
104 if (io->original_radius > 40.f) io->original_radius = 40.f;
105
106 io->physics.cyl.radius = io->original_radius * io->scale;
107 io->physics.cyl.height = io->original_height * io->scale;
108 }
109
EERIE_COLLISION_SPHERES_Release(EERIE_3DOBJ * obj)110 void EERIE_COLLISION_SPHERES_Release(EERIE_3DOBJ * obj) {
111
112 if(!obj || !obj->sdata) {
113 return;
114 }
115
116 delete obj->sdata;
117 obj->sdata = NULL;
118 }
119
AddCollisionSphere(EERIE_3DOBJ * obj,long idx,float radius)120 void AddCollisionSphere(EERIE_3DOBJ * obj, long idx, float radius) {
121
122 if(radius < 1.f) {
123 return;
124 }
125
126 for(vector<COLLISION_SPHERE>::iterator i = obj->sdata->spheres.begin(); i < obj->sdata->spheres.end(); ++i) {
127
128 if(i->idx == idx) {
129 if(radius >= i->radius) {
130 i->radius = radius;
131 }
132 return;
133 }
134 }
135
136 COLLISION_SPHERE newSphere;
137 newSphere.idx = (short)idx;
138 newSphere.radius = radius;
139 newSphere.flags = 0;
140 obj->sdata->spheres.push_back(newSphere);
141 }
142
GetFirstChildGroup(EERIE_3DOBJ * obj,long group)143 long GetFirstChildGroup(EERIE_3DOBJ * obj, long group)
144 {
145 if (obj->nbgroups < group + 2) return -1;
146
147 for (long k = group + 1; k < obj->nbgroups; k++)
148 {
149 for (size_t i = 0; i < obj->grouplist[group].indexes.size(); i++)
150 {
151 if (obj->grouplist[group].indexes[i] == obj->grouplist[k].origin)
152 {
153 return k;
154 }
155 }
156 }
157
158 return -1;
159 }
160
IsExclusiveGroupMember(EERIE_3DOBJ * obj,long idx,long group)161 bool IsExclusiveGroupMember(EERIE_3DOBJ * obj, long idx, long group)
162 {
163
164 for (long i = group + 1; i < obj->nbgroups; i++)
165 {
166 for (size_t j = 0; j < obj->grouplist[i].indexes.size(); j++)
167 {
168 if (idx == obj->grouplist[i].indexes[j])
169 {
170 return false;
171 }
172 }
173 }
174
175 return true;
176 }
177
GetSphereRadiusForGroup(EERIE_3DOBJ * obj,Vec3f * center,Vec3f * dirvect,long group,float maxi)178 float GetSphereRadiusForGroup(EERIE_3DOBJ * obj, Vec3f * center, Vec3f * dirvect, long group, float maxi)
179 {
180 float curradius = 0.f;
181 float maxf = 0.f;
182 float div = 0.f;
183 long sel = -1;
184
185 for(size_t i = 0; i < obj->selections.size(); i++) { // TODO iterator
186 if(obj->selections[i].name == "mou") {
187 sel = i;
188 break;
189 }
190 }
191
192 for (size_t i = 0; i < obj->grouplist[group].indexes.size(); i++)
193 {
194 if (!IsExclusiveGroupMember(obj, obj->grouplist[group].indexes[i], group)) continue;
195
196 if ((sel > -1) && (IsInSelection(obj, obj->grouplist[group].indexes[i], sel) >= 0)) continue;
197
198 Vec3f target = obj->vertexlist[obj->grouplist[group].indexes[i]].v;
199 float distance = fdist(*center, target);
200
201 if (distance < 2.f) continue;
202
203 if (distance < maxf) continue;
204
205 Vec3f targvect = (target - *center) * 1.f / distance;
206 float val = dot(*dirvect, targvect);
207
208 if(fabs(val) < 1.2f) {
209 if (distance > maxi) distance = maxi;
210
211 curradius += distance;
212 div += 1.f;
213 maxf = max(maxf, distance);
214 }
215 }
216
217 if (div > 0.f)
218 {
219 curradius /= div;
220 }
221
222 return (curradius);
223 }
224
AddVertexToVertexList(EERIE_3DOBJ * obj,Vec3f * center,long group)225 long AddVertexToVertexList(EERIE_3DOBJ * obj, Vec3f * center, long group)
226 {
227 if (obj->vertexlist.empty()) return -1;
228
229 for (size_t i = 0; i < obj->vertexlist.size(); i++)
230 {
231 if ((center->x == obj->vertexlist[i].v.x)
232 && (center->y == obj->vertexlist[i].v.y)
233 && (center->z == obj->vertexlist[i].v.z))
234 {
235 if (IsVertexIdxInGroup(obj, i, group) == false)
236 {
237 if ((obj->vertexlist[i].norm.x == 50.f)
238 && (obj->vertexlist[i].norm.y == 50.f)
239 && (obj->vertexlist[i].norm.z == 50.f)
240 )
241 AddVertexIdxToGroup(obj, group, i);
242 }
243
244 return i;
245 }
246 }
247
248 size_t nvertex = obj->vertexlist.size();
249
250 // Must create a new one...
251 obj->vertexlist.resize(nvertex + 1);
252
253 memset(&obj->vertexlist[nvertex], 0, sizeof(EERIE_VERTEX));
254
255 if (obj->pdata)
256 {
257 obj->pdata = (PROGRESSIVE_DATA *)
258 realloc(obj->pdata, sizeof(PROGRESSIVE_DATA) * (nvertex + 1));
259
260 memset(&obj->pdata[nvertex], 0, sizeof(PROGRESSIVE_DATA));
261 obj->pdata[nvertex].collapse_candidate = -1;
262 obj->pdata[nvertex].collapse_cost = 1000000;
263 }
264
265 obj->vertexlist[nvertex].v = *center;
266 obj->vertexlist[nvertex].norm = Vec3f::repeat(50.f);
267
268 obj->vertexlist3.push_back(obj->vertexlist[nvertex]);
269
270 AddVertexIdxToGroup(obj, group, nvertex);
271
272 return nvertex;
273 }
EERIE_COLLISION_SPHERES_Create(EERIE_3DOBJ * obj)274 void EERIE_COLLISION_SPHERES_Create(EERIE_3DOBJ * obj)
275 {
276 if (obj == NULL) return;
277
278 EERIE_COLLISION_SPHERES_Release(obj);
279 obj->sdata = new COLLISION_SPHERES_DATA();
280
281 for (long k = 1; k < obj->nbgroups; k++)
282 {
283 long workon;
284 workon = GetFirstChildGroup(obj, k);
285
286 if(workon != -1) {
287
288 // Group origin pos
289 Vec3f center = obj->vertexlist[obj->grouplist[k].origin].v;
290
291 // Destination Group origin pos
292 Vec3f dest = obj->vertexlist[obj->grouplist[workon].origin].v;
293
294 // Direction Vector from Origin Group to Destination Origin Group
295 Vec3f dirvect = dest - center;
296
297 // Distance between those 2 pos
298 float dista = fdist(dest, center);
299 float tot = dista;
300 float divdist = 1.f / dista;
301 // Vector Normalization
302 dirvect *= divdist;
303
304 while (dista >= 0.f) // Iterate along the whole distance
305 {
306 // Compute required radius for this group and that pos
307 float val = GetSphereRadiusForGroup(obj, ¢er, &dest, k, tot * 1.4f);
308
309 if (val > 0.2f)
310 {
311 long idx = AddVertexToVertexList(obj, ¢er, k);
312
313 if (idx > -1)
314 AddCollisionSphere(obj, idx, val);
315
316 val *= ( 1.0f / 2 );
317 }
318 else val = 0.15f;
319
320 float inc = val;
321 dista -= inc;
322
323 center += dirvect * inc;
324 }
325 }
326 else AddCollisionSphere(obj, obj->grouplist[k].origin, obj->grouplist[k].siz * 18.f);
327
328
329 }
330
331 if (obj->sdata->spheres.empty() == 0) EERIE_COLLISION_SPHERES_Release(obj);
332 }
333