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-2000 ARKANE Studios SA. All rights reserved
46 
47 #include "graphics/data/MeshManipulation.h"
48 
49 #include <stddef.h>
50 #include <cstring>
51 #include <cstdlib>
52 #include <algorithm>
53 #include <string>
54 #include <vector>
55 
56 #include "game/Entity.h"
57 
58 #include "graphics/BaseGraphicsTypes.h"
59 #include "graphics/GraphicsTypes.h"
60 #include "graphics/Vertex.h"
61 #include "graphics/data/Mesh.h"
62 #include "graphics/data/TextureContainer.h"
63 #include "graphics/effects/DrawEffects.h"
64 
65 #include "io/resource/PakReader.h"
66 #include "io/log/Logger.h"
67 
68 #include "math/MathFwd.h"
69 #include "math/Vector3.h"
70 
71 #include "platform/Platform.h"
72 
73 #include "scene/Object.h"
74 
75 using std::max;
76 using std::string;
77 using std::vector;
78 
EERIE_MESH_TWEAK_Skin(EERIE_3DOBJ * obj,const res::path & s1,const res::path & s2)79 void EERIE_MESH_TWEAK_Skin(EERIE_3DOBJ * obj, const res::path & s1, const res::path & s2) {
80 
81 	LogDebug("Tweak Skin " << s1 << " " << s2);
82 
83 	if ( obj == NULL || s1.empty() || s2.empty() )
84 	{
85 		LogError << "Tweak Skin got NULL Pointer";
86 		return;
87 	}
88 
89 	LogDebug("Tweak Skin " << s1 << " " << s2);
90 
91 	res::path skintochange = "graph/obj3d/textures" / s1;
92 
93 	res::path skinname = "graph/obj3d/textures" / s2;
94 	TextureContainer * tex = TextureContainer::Load(skinname);
95 
96 	if (obj->originaltextures == NULL)
97 	{
98 		obj->originaltextures = (char *)malloc(256 * obj->texturecontainer.size());
99 		memset(obj->originaltextures, 0, 256 * obj->texturecontainer.size());
100 
101 		for (size_t i = 0; i < obj->texturecontainer.size(); i++)
102 		{
103 			if (obj->texturecontainer[i])
104 				strcpy(obj->originaltextures + 256 * i, obj->texturecontainer[i]->m_texName.string().c_str());
105 
106 		}
107 	}
108 
109 	if ((tex != NULL) && (obj->originaltextures != NULL))
110 	{
111 		for (size_t i = 0; i < obj->texturecontainer.size(); i++)
112 		{
113 			if ((strstr(obj->originaltextures + 256 * i, skintochange.string().c_str())))
114 			{
115 				skintochange = obj->texturecontainer[i]->m_texName;
116 				break;
117 			}
118 		}
119 
120 		TextureContainer * tex2 = TextureContainer::Find(skintochange);
121 		if (tex2)
122 		{
123 			for (size_t i = 0; i < obj->texturecontainer.size(); i++)
124 			{
125 				if (obj->texturecontainer[i] == tex2)
126 					obj->texturecontainer[i] = tex;
127 			}
128 		}
129 	}
130 }
131 
IsInSelection(const EERIE_3DOBJ * obj,long vert,long tw)132 long IsInSelection(const EERIE_3DOBJ * obj, long vert, long tw) {
133 
134 	if (obj == NULL) return -1;
135 
136 	if (tw < 0) return -1;
137 
138 	if (vert < 0) return -1;
139 
140 	for (size_t i = 0; i < obj->selections[tw].selected.size(); i++)
141 	{
142 		if (obj->selections[tw].selected[i] == vert)
143 			return i;
144 	}
145 
146 	return -1;
147 }
148 
GetEquivalentVertex(const EERIE_3DOBJ * obj,const EERIE_VERTEX * vert)149 static long GetEquivalentVertex(const EERIE_3DOBJ * obj, const EERIE_VERTEX * vert) {
150 
151 	for (size_t i = 0; i < obj->vertexlist.size(); i++)
152 	{
153 		if ((obj->vertexlist[i].v.x == vert->v.x)
154 		        &&	(obj->vertexlist[i].v.y == vert->v.y)
155 		        &&	(obj->vertexlist[i].v.z == vert->v.z))
156 			return i;
157 	}
158 
159 	return -1;
160 }
161 
ObjectAddVertex(EERIE_3DOBJ * obj,const EERIE_VERTEX * vert)162 static long ObjectAddVertex(EERIE_3DOBJ * obj, const EERIE_VERTEX * vert) {
163 
164 	for (size_t i = 0; i < obj->vertexlist.size(); i++)
165 	{
166 		if ((obj->vertexlist[i].v.x == vert->v.x)
167 		        &&	(obj->vertexlist[i].v.y == vert->v.y)
168 		        &&	(obj->vertexlist[i].v.z == vert->v.z))
169 			return i;
170 	}
171 
172 	obj->vertexlist.push_back(*vert);
173 	return (obj->vertexlist.size() - 1);
174 }
175 
GetActionPoint(const EERIE_3DOBJ * obj,const char * name)176 static long GetActionPoint(const EERIE_3DOBJ * obj, const char * name) {
177 
178 	if (!obj) return -1;
179 
180 	for (size_t n = 0; n < obj->actionlist.size(); n++)
181 	{ // TODO iterator
182 		if(obj->actionlist[n].name == name) {
183 			return obj->actionlist[n].idx;
184 		}
185 	}
186 
187 	return -1;
188 }
189 
ObjectAddFace(EERIE_3DOBJ * obj,const EERIE_FACE * face,const EERIE_3DOBJ * srcobj)190 static long ObjectAddFace(EERIE_3DOBJ * obj, const EERIE_FACE * face, const EERIE_3DOBJ * srcobj) {
191 
192 	for (size_t i = 0; i < obj->facelist.size(); i++) // Check Already existing faces
193 	{
194 		if ((obj->vertexlist[obj->facelist[i].vid[0]].v.x == srcobj->vertexlist[face->vid[0]].v.x)
195 		        &&	(obj->vertexlist[obj->facelist[i].vid[1]].v.x == srcobj->vertexlist[face->vid[1]].v.x)
196 		        &&	(obj->vertexlist[obj->facelist[i].vid[2]].v.x == srcobj->vertexlist[face->vid[2]].v.x)
197 		        &&	(obj->vertexlist[obj->facelist[i].vid[0]].v.y == srcobj->vertexlist[face->vid[0]].v.y)
198 		        &&	(obj->vertexlist[obj->facelist[i].vid[1]].v.y == srcobj->vertexlist[face->vid[1]].v.y)
199 		        &&	(obj->vertexlist[obj->facelist[i].vid[2]].v.y == srcobj->vertexlist[face->vid[2]].v.y)
200 		        &&	(obj->vertexlist[obj->facelist[i].vid[0]].v.z == srcobj->vertexlist[face->vid[0]].v.z)
201 		        &&	(obj->vertexlist[obj->facelist[i].vid[1]].v.z == srcobj->vertexlist[face->vid[1]].v.z)
202 		        &&	(obj->vertexlist[obj->facelist[i].vid[2]].v.z == srcobj->vertexlist[face->vid[2]].v.z)
203 		   )
204 
205 			return -1;
206 	}
207 
208 	long f0, f1, f2;
209 	f0 = ObjectAddVertex(obj, &srcobj->vertexlist[face->vid[0]]);
210 	f1 = ObjectAddVertex(obj, &srcobj->vertexlist[face->vid[1]]);
211 	f2 = ObjectAddVertex(obj, &srcobj->vertexlist[face->vid[2]]);
212 
213 	if ((f1 == -1) || (f2 == -1) || (f0 == -1)) return -1;
214 
215 
216 	obj->facelist.push_back(*face);
217 
218 	obj->facelist.back().vid[0] = (unsigned short)f0;
219 	obj->facelist.back().vid[1] = (unsigned short)f1;
220 	obj->facelist.back().vid[2] = (unsigned short)f2;
221 	obj->facelist.back().texid = 0;
222 
223 	for (size_t i = 0; i < obj->texturecontainer.size(); i++)
224 	{
225 		if (0 <= face->texid && (size_t)face->texid < srcobj->texturecontainer.size() && obj->texturecontainer[i] == srcobj->texturecontainer[face->texid])
226 		{
227 			obj->facelist.back().texid = (short)i;
228 			break;
229 		}
230 	}
231 
232 	return (obj->facelist.size() - 1);
233 }
234 
ObjectAddAction(EERIE_3DOBJ * obj,const string & name,long act,long sfx,const EERIE_VERTEX * vert)235 static long ObjectAddAction(EERIE_3DOBJ * obj, const string & name, long act,
236                             long sfx, const EERIE_VERTEX * vert) {
237 
238 	long newvert = ObjectAddVertex(obj, vert);
239 
240 	if (newvert < 0) return -1;
241 
242 	long j = 0;
243 	for(vector<EERIE_ACTIONLIST>::iterator i = obj->actionlist.begin();
244 	    i != obj->actionlist.end(); ++i) {
245 		if(i->name == name) {
246 			return j;
247 		}
248 		j++;
249 	}
250 
251 	obj->actionlist.push_back(EERIE_ACTIONLIST());
252 
253 	EERIE_ACTIONLIST & action = obj->actionlist.back();
254 
255 	action.name = name;
256 	action.act = act;
257 	action.sfx = sfx;
258 	action.idx = newvert;
259 
260 	return (obj->actionlist.size() - 1);
261 }
262 
ObjectAddMap(EERIE_3DOBJ * obj,TextureContainer * tc)263 long ObjectAddMap(EERIE_3DOBJ * obj, TextureContainer * tc) {
264 
265 	if (tc == NULL) return -1;
266 
267 	for (size_t i = 0; i < obj->texturecontainer.size(); i++)
268 	{
269 		if (obj->texturecontainer[i] == tc)
270 			return i;
271 	}
272 
273 	obj->texturecontainer.push_back(tc);
274 
275 	return (obj->texturecontainer.size() - 1);
276 }
277 
AddVertexToGroup(EERIE_3DOBJ * obj,long group,const EERIE_VERTEX * vert)278 static void AddVertexToGroup(EERIE_3DOBJ * obj, long group, const EERIE_VERTEX * vert) {
279 
280 	for (size_t i = 0; i < obj->vertexlist.size(); i++)
281 	{
282 		if ((obj->vertexlist[i].v.x == vert->v.x)
283 		        &&	(obj->vertexlist[i].v.y == vert->v.y)
284 		        &&	(obj->vertexlist[i].v.z == vert->v.z))
285 		{
286 			AddVertexIdxToGroup(obj, group, i);
287 		}
288 	}
289 }
290 
AddVertexIdxToGroup(EERIE_3DOBJ * obj,long group,long val)291 void AddVertexIdxToGroup(EERIE_3DOBJ * obj, long group, long val) {
292 
293 	for(size_t i = 0; i < obj->grouplist[group].indexes.size(); i++) {
294 		if(obj->grouplist[group].indexes[i] == val) {
295 			return;
296 		}
297 	}
298 
299 	obj->grouplist[group].indexes.push_back(val);
300 }
301 
ObjectAddSelection(EERIE_3DOBJ * obj,long numsel,long vidx)302 static void ObjectAddSelection(EERIE_3DOBJ * obj, long numsel, long vidx) {
303 
304 	for (size_t i = 0; i < obj->selections[numsel].selected.size(); i++)
305 	{
306 		if (obj->selections[numsel].selected[i] == vidx) return;
307 	}
308 
309 	obj->selections[numsel].selected.push_back(vidx);
310 }
311 
CreateIntermediaryMesh(const EERIE_3DOBJ * obj1,const EERIE_3DOBJ * obj2,long tw)312 static EERIE_3DOBJ * CreateIntermediaryMesh(const EERIE_3DOBJ * obj1, const EERIE_3DOBJ * obj2, long tw) {
313 
314 	long tw1 = -1;
315 	long tw2 = -1;
316 	long iw1 = -1;
317 	long jw1 = -1;
318 	long sel_head1 = -1;
319 	long sel_head2 = -1;
320 	long sel_torso1 = -1;
321 	long sel_torso2 = -1;
322 	long sel_legs1 = -1;
323 	long sel_legs2 = -1;
324 
325 	// First we retreive selection groups indexes
326 	for(size_t i = 0; i < obj1->selections.size(); i++) { // TODO iterator
327 		if(obj1->selections[i].name == "head") {
328 			sel_head1 = i;
329 		} else if(obj1->selections[i].name == "chest") {
330 			sel_torso1 = i;
331 		} else if(obj1->selections[i].name == "leggings") {
332 			sel_legs1 = i;
333 		}
334 	}
335 
336 	for(size_t i = 0; i < obj2->selections.size(); i++) { // TODO iterator
337 		if(obj2->selections[i].name == "head") {
338 			sel_head2 = i;
339 		} else if(obj2->selections[i].name == "chest") {
340 			sel_torso2 = i;
341 		} else if(obj2->selections[i].name == "leggings") {
342 			sel_legs2 = i;
343 		}
344 	}
345 
346 	if (sel_head1 == -1) return NULL;
347 
348 	if (sel_head2 == -1) return NULL;
349 
350 	if (sel_torso1 == -1) return NULL;
351 
352 	if (sel_torso2 == -1) return NULL;
353 
354 	if (sel_legs1 == -1) return NULL;
355 
356 	if (sel_legs2 == -1) return NULL;
357 
358 	if (tw == TWEAK_HEAD)
359 	{
360 		tw1 = sel_head1;
361 		tw2 = sel_head2;
362 		iw1 = sel_torso1;
363 		jw1 = sel_legs1;
364 	}
365 
366 	if (tw == TWEAK_TORSO)
367 	{
368 		tw1 = sel_torso1;
369 		tw2 = sel_torso2;
370 		iw1 = sel_head1;
371 		jw1 = sel_legs1;
372 	}
373 
374 	if (tw == TWEAK_LEGS)
375 	{
376 		tw1 = sel_legs1;
377 		tw2 = sel_legs2;
378 		iw1 = sel_torso1;
379 		jw1 = sel_head1;
380 	}
381 
382 	if ((tw1 == -1) || (tw2 == -1)) return NULL;
383 
384 	// Now Retreives Tweak Action Points
385 	{
386 		long idx_head1, idx_head2;
387 		long idx_torso1, idx_torso2;
388 
389 		idx_head1 = GetActionPoint(obj1, "head2chest");
390 
391 		if (idx_head1 < 0) return NULL;
392 
393 		idx_head2 = GetActionPoint(obj2, "head2chest");
394 
395 		if (idx_head2 < 0) return NULL;
396 
397 		idx_torso1 = GetActionPoint(obj1, "chest2leggings");
398 
399 		if (idx_torso1 < 0) return NULL;
400 
401 		idx_torso2 = GetActionPoint(obj2, "chest2leggings");
402 
403 		if (idx_torso2 < 0) return NULL;
404 	}
405 
406 	// copy vertices
407 	vector<EERIE_VERTEX> obj1vertexlist2 = obj1->vertexlist;
408 	vector<EERIE_VERTEX> obj2vertexlist2 = obj2->vertexlist;
409 
410 	// Work will contain the Tweaked object
411 	EERIE_3DOBJ * work = new EERIE_3DOBJ();
412 	work->pos = obj1->pos;
413 	work->angle = obj1->angle;
414 
415 	// ident will be the same as original object obj1
416 	work->ident = obj1->ident;
417 
418 	// We reset all data to create a fresh object
419 	memcpy(&work->cub, &obj1->cub, sizeof(CUB3D));
420 	memcpy(&work->quat, &obj1->quat, sizeof(EERIE_QUAT));
421 
422 	// Linked objects are linked to this object.
423 	if (obj1->nblinked > obj2->nblinked)
424 	{
425 		work->linked = (EERIE_LINKED *)malloc(obj1->nblinked * sizeof(EERIE_LINKED));
426 		memcpy(work->linked, obj1->linked, obj1->nblinked * sizeof(EERIE_LINKED));
427 		work->nblinked = obj1->nblinked;
428 	}
429 	else if (obj2->nblinked > 0)
430 	{
431 		work->linked = (EERIE_LINKED *)malloc(obj2->nblinked * sizeof(EERIE_LINKED));
432 		memcpy(work->linked, obj2->linked, obj2->nblinked * sizeof(EERIE_LINKED));
433 		work->nblinked = obj2->nblinked;
434 	}
435 	else
436 	{
437 		work->linked = NULL;
438 		work->nblinked = 0;
439 	}
440 
441 	// Is the origin of object in obj1 or obj2 ? Retreives it for work object
442 	if(IsInSelection(obj1, obj1->origin, tw1) != -1) {
443 		work->point0 = obj2->point0;
444 		work->origin = ObjectAddVertex(work, &obj2vertexlist2[obj2->origin]);
445 	} else {
446 		work->point0 = obj1->point0;
447 		work->origin = ObjectAddVertex(work, &obj1vertexlist2[obj1->origin]);
448 	}
449 
450 	// Recreate Action Points included in work object.for Obj1
451 	for (size_t i = 0; i < obj1->actionlist.size(); i++)
452 	{
453 		if ((IsInSelection(obj1, obj1->actionlist[i].idx, iw1) != -1)
454 		        || (IsInSelection(obj1, obj1->actionlist[i].idx, jw1) != -1)
455 		        || obj1->actionlist[i].name == "head2chest"
456 		        || obj1->actionlist[i].name == "chest2leggings") {
457 			ObjectAddAction(work, obj1->actionlist[i].name, obj1->actionlist[i].act,
458 			                obj1->actionlist[i].sfx, &obj1vertexlist2[obj1->actionlist[i].idx]);
459 		}
460 	}
461 
462 	// Do the same for Obj2
463 	for (size_t i = 0; i < obj2->actionlist.size(); i++)
464 	{
465 		if ((IsInSelection(obj2, obj2->actionlist[i].idx, tw2) != -1)
466 		        || obj2->actionlist[i].name == "head2chest"
467 		        || obj2->actionlist[i].name == "chest2leggings") {
468 			ObjectAddAction(work, obj2->actionlist[i].name, obj2->actionlist[i].act,
469 			                obj2->actionlist[i].sfx, &obj2vertexlist2[obj2->actionlist[i].idx]);
470 		}
471 	}
472 
473 	// Recreate Vertex using Obj1 Vertexes
474 	for (size_t i = 0; i < obj1->vertexlist.size(); i++)
475 	{
476 		if ((IsInSelection(obj1, i, iw1) != -1)
477 		        ||	(IsInSelection(obj1, i, jw1) != -1))
478 		{
479 			ObjectAddVertex(work, &obj1vertexlist2[i]);
480 		}
481 	}
482 
483 	// The same for Obj2
484 	for (size_t i = 0; i < obj2->vertexlist.size(); i++)
485 	{
486 		if (IsInSelection(obj2, i, tw2) != -1)
487 		{
488 			ObjectAddVertex(work, &obj2vertexlist2[i]);
489 		}
490 	}
491 
492 
493 	// Look in Faces for forgotten Vertexes... AND
494 	// Re-Create TextureContainers Infos
495 	// We look for texturecontainers included in the future tweaked object
496 	TextureContainer * tc = NULL;
497 
498 	for (size_t i = 0; i < obj1->facelist.size(); i++)
499 	{
500 		if (((IsInSelection(obj1, obj1->facelist[i].vid[0], iw1) != -1)
501 		        ||	(IsInSelection(obj1, obj1->facelist[i].vid[0], jw1) != -1))
502 		        &&	((IsInSelection(obj1, obj1->facelist[i].vid[1], iw1) != -1)
503 		             ||	(IsInSelection(obj1, obj1->facelist[i].vid[1], jw1) != -1))
504 		        &&	((IsInSelection(obj1, obj1->facelist[i].vid[2], iw1) != -1)
505 		             ||	(IsInSelection(obj1, obj1->facelist[i].vid[2], jw1) != -1))
506 		   )
507 		{
508 
509 			if (obj1->facelist[i].texid != -1)
510 				if (tc != obj1->texturecontainer[obj1->facelist[i].texid])
511 				{
512 					tc = obj1->texturecontainer[obj1->facelist[i].texid];
513 					ObjectAddMap(work, tc);
514 				}
515 
516 			ObjectAddFace(work, &obj1->facelist[i], obj1);
517 		}
518 	}
519 
520 	for (size_t i = 0; i < obj2->facelist.size(); i++)
521 	{
522 		if ((IsInSelection(obj2, obj2->facelist[i].vid[0], tw2) != -1)
523 		        || (IsInSelection(obj2, obj2->facelist[i].vid[1], tw2) != -1)
524 		        || (IsInSelection(obj2, obj2->facelist[i].vid[2], tw2) != -1))
525 		{
526 
527 			if (obj2->facelist[i].texid != -1)
528 				if (tc != obj2->texturecontainer[obj2->facelist[i].texid])
529 				{
530 					tc = obj2->texturecontainer[obj2->facelist[i].texid];
531 					ObjectAddMap(work, tc);
532 				}
533 
534 			ObjectAddFace(work, &obj2->facelist[i], obj2);
535 		}
536 	}
537 
538 	// Recreate Groups
539 	work->nbgroups = max(obj1->nbgroups, obj2->nbgroups);
540 	work->grouplist = new EERIE_GROUPLIST[work->nbgroups];
541 
542 	for (long k = 0; k < obj1->nbgroups; k++)
543 	{
544 		work->grouplist[k].name = obj1->grouplist[k].name;
545 		long v = GetEquivalentVertex(work, &obj1vertexlist2[obj1->grouplist[k].origin]);
546 
547 		if (v >= 0)
548 		{
549 			work->grouplist[k].siz = obj1->grouplist[k].siz;
550 
551 			if ((IsInSelection(obj1, obj1->grouplist[k].origin, iw1) != -1)
552 			        || (IsInSelection(obj1, obj1->grouplist[k].origin, jw1) != -1))
553 				work->grouplist[k].origin = v;
554 		}
555 	}
556 
557 	for (int k = 0; k < obj2->nbgroups; k++)
558 	{
559 		if (k >= obj1->nbgroups)
560 		{
561 			work->grouplist[k].name = obj2->grouplist[k].name;
562 
563 		}
564 
565 		long v = GetEquivalentVertex(work, &obj2vertexlist2[obj2->grouplist[k].origin]);
566 
567 		if (v >= 0)
568 		{
569 			work->grouplist[k].siz = obj2->grouplist[k].siz;
570 
571 			if (IsInSelection(obj2, obj2->grouplist[k].origin, tw2) != -1)
572 				work->grouplist[k].origin = v;
573 		}
574 	}
575 
576 	// Recreate Selection Groups (only the 3 selections needed to reiterate MeshTweaking !)
577 	work->selections.resize(3);
578 	work->selections[0].name = "head";
579 	work->selections[1].name = "chest";
580 	work->selections[2].name = "leggings";
581 
582 	// Re-Creating sel_head
583 	if (tw == TWEAK_HEAD)
584 	{
585 		for(size_t l = 0; l < obj2->selections[sel_head2].selected.size(); l++) {
586 			EERIE_VERTEX temp;
587 			temp.v = obj2vertexlist2[obj2->selections[sel_head2].selected[l]].v;
588 			long t = GetEquivalentVertex(work, &temp);
589 
590 			if (t != -1)
591 			{
592 				ObjectAddSelection(work, 0, t);
593 			}
594 		}
595 	}
596 	else for (size_t l = 0; l < obj1->selections[sel_head1].selected.size(); l++)
597 		{
598 			EERIE_VERTEX temp;
599 			temp.v = obj1vertexlist2[obj1->selections[sel_head1].selected[l]].v;
600 			long t = GetEquivalentVertex(work, &temp);
601 
602 			if (t != -1)
603 			{
604 				ObjectAddSelection(work, 0, t);
605 			}
606 		}
607 
608 	// Re-Create sel_torso
609 	if (tw == TWEAK_TORSO)
610 	{
611 		for (size_t l = 0; l < obj2->selections[sel_torso2].selected.size(); l++)
612 		{
613 			EERIE_VERTEX temp;
614 			temp.v = obj2vertexlist2[obj2->selections[sel_torso2].selected[l]].v;
615 			long t = GetEquivalentVertex(work, &temp);
616 
617 			if (t != -1)
618 			{
619 				ObjectAddSelection(work, 1, t);
620 			}
621 		}
622 	}
623 	else for (size_t l = 0; l < obj1->selections[sel_torso1].selected.size(); l++)
624 		{
625 			EERIE_VERTEX temp;
626 			temp.v = obj1vertexlist2[obj1->selections[sel_torso1].selected[l]].v;
627 			long t = GetEquivalentVertex(work, &temp);
628 
629 			if (t != -1)
630 			{
631 				ObjectAddSelection(work, 1, t);
632 			}
633 		}
634 
635 	// Re-Create sel_legs
636 	if (tw == TWEAK_LEGS)
637 	{
638 		for (size_t l = 0; l < obj2->selections[sel_legs2].selected.size(); l++)
639 		{
640 			EERIE_VERTEX temp;
641 			temp.v = obj2vertexlist2[obj2->selections[sel_legs2].selected[l]].v;
642 			long t = GetEquivalentVertex(work, &temp);
643 
644 			if (t != -1)
645 			{
646 				ObjectAddSelection(work, 2, t);
647 			}
648 		}
649 	}
650 	else for (size_t l = 0; l < obj1->selections[sel_legs1].selected.size(); l++)
651 		{
652 			EERIE_VERTEX temp;
653 			temp.v = obj1vertexlist2[obj1->selections[sel_legs1].selected[l]].v;
654 			long t = GetEquivalentVertex(work, &temp);
655 
656 			if (t != -1)
657 			{
658 				ObjectAddSelection(work, 2, t);
659 			}
660 		}
661 
662 	//Now recreates other selections...
663 	for (size_t i = 0; i < obj1->selections.size(); i++) {
664 
665 		if (EERIE_OBJECT_GetSelection(work, obj1->selections[i].name) == -1)
666 		{
667 			long num = work->selections.size();
668 			work->selections.resize(num + 1);
669 			work->selections[num].name = obj1->selections[i].name;
670 
671 			for (size_t l = 0; l < obj1->selections[i].selected.size(); l++)
672 			{
673 				EERIE_VERTEX temp;
674 				temp.v = obj1vertexlist2[obj1->selections[i].selected[l]].v;
675 				long t = GetEquivalentVertex(work, &temp);
676 
677 				if (t != -1)
678 				{
679 					ObjectAddSelection(work, num, t);
680 				}
681 			}
682 
683 			long ii = EERIE_OBJECT_GetSelection(obj2, obj1->selections[i].name);
684 
685 			if (ii != -1)
686 				for (size_t l = 0; l < obj2->selections[ii].selected.size(); l++)
687 				{
688 					EERIE_VERTEX temp;
689 					temp.v = obj2vertexlist2[obj2->selections[ii].selected[l]].v;
690 					long t = GetEquivalentVertex(work, &temp);
691 
692 					if (t != -1)
693 					{
694 						ObjectAddSelection(work, num, t);
695 					}
696 				}
697 		}
698 	}
699 
700 	for (size_t i = 0; i < obj2->selections.size(); i++) {
701 
702 		if (EERIE_OBJECT_GetSelection(work, obj2->selections[i].name) == -1)
703 		{
704 			long num = work->selections.size();
705 			work->selections.resize(num + 1);
706 			work->selections[num].name = obj2->selections[i].name;
707 
708 			for (size_t l = 0; l < obj2->selections[i].selected.size(); l++)
709 			{
710 				EERIE_VERTEX temp;
711 				temp.v = obj2vertexlist2[obj2->selections[i].selected[l]].v;
712 				long t = GetEquivalentVertex(work, &temp);
713 
714 				if (t != -1)
715 				{
716 					ObjectAddSelection(work, num, t);
717 				}
718 			}
719 		}
720 	}
721 
722 	// Recreate Animation-groups vertex
723 	for (long i = 0; i < obj1->nbgroups; i++)
724 	{
725 		for (size_t j = 0; j < obj1->grouplist[i].indexes.size(); j++)
726 		{
727 			AddVertexToGroup(work, i, &obj1vertexlist2[obj1->grouplist[i].indexes[j]]);
728 		}
729 	}
730 
731 	for (long i = 0; i < obj2->nbgroups; i++)
732 	{
733 		for (size_t j = 0; j < obj2->grouplist[i].indexes.size(); j++)
734 		{
735 			AddVertexToGroup(work, i, &obj2vertexlist2[obj2->grouplist[i].indexes[j]]);
736 		}
737 	}
738 
739 	work->vertexlist3 = work->vertexlist;
740 
741 	return work;
742 }
743 
EERIE_MESH_TWEAK_Do(Entity * io,TweakType tw,const res::path & path)744 void EERIE_MESH_TWEAK_Do(Entity * io, TweakType tw, const res::path & path) {
745 
746 	res::path ftl_file = ("game" / path).set_ext("ftl");
747 
748 	if ((!resources->getFile(ftl_file)) && (!resources->getFile(path))) return;
749 
750 	if (!tw) return;
751 
752 	if (io == NULL) return;
753 
754 	if (io->obj == NULL) return;
755 
756 	if(path.empty() && tw == TWEAK_REMOVE) {
757 
758 		if(io->tweaky) {
759 			delete io->obj;
760 			io->obj = io->tweaky;
761 			EERIE_Object_Precompute_Fast_Access(io->obj);
762 			io->tweaky = NULL;
763 		}
764 
765 		return;
766 	}
767 
768 	EERIE_3DOBJ * tobj = NULL;
769 	EERIE_3DOBJ * result = NULL;
770 	EERIE_3DOBJ * result2 = NULL;
771 
772 	{
773 		tobj = loadObject(path);
774 
775 		if (!tobj) return;
776 
777 		switch (tw)
778 		{
779 			case (u32)TWEAK_HEAD | (u32)TWEAK_TORSO | (u32)TWEAK_LEGS:
780 
781 				if (!io->tweaky)
782 					io->tweaky = io->obj;
783 				else delete
784 					io->obj;
785 
786 				io->obj = tobj;
787 				return;
788 				break;
789 			case (u32)TWEAK_HEAD | (u32)TWEAK_TORSO:
790 				result2 = CreateIntermediaryMesh(io->obj, tobj, TWEAK_HEAD);
791 				result = CreateIntermediaryMesh(result2, tobj, TWEAK_TORSO);
792 				delete result2;
793 				break;
794 			case (u32)TWEAK_TORSO | (u32)TWEAK_LEGS:
795 				result2 = CreateIntermediaryMesh(io->obj, tobj, TWEAK_TORSO);
796 				result = CreateIntermediaryMesh(result2, tobj, TWEAK_LEGS);
797 				delete result2;
798 				break;
799 			case (u32)TWEAK_HEAD | (u32)TWEAK_LEGS:
800 				result = CreateIntermediaryMesh(tobj, io->obj, TWEAK_TORSO);
801 				break;
802 			default:
803 				result = CreateIntermediaryMesh(io->obj, tobj, tw);
804 				break;
805 		}
806 
807 		if(!result) {
808 			delete tobj;
809 			return;
810 		}
811 
812 		result->pdata = NULL;
813 		result->cdata = NULL;
814 
815 		if (io->tweaky == NULL) io->tweaky = io->obj;
816 		else if (io->tweaky != io->obj)
817 			delete io->obj;
818 
819 		io->obj = result;
820 		EERIE_Object_Precompute_Fast_Access(io->obj);
821 	}
822 
823 	EERIE_CreateCedricData(io->obj);
824 
825 	if (io)
826 	{
827 		io->lastanimtime = 0;
828 		io->nb_lastanimvertex = 0;
829 	}
830 
831 	delete tobj;
832 }
833