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, &center, &dest, k, tot * 1.4f);
308 
309 				if (val > 0.2f)
310 				{
311 					long idx = AddVertexToVertexList(obj, &center, 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