1 /*
2 * Copyright 2011-2013 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 ARKANE Studios SA. All rights reserved
46
47 #include "scene/Object.h"
48
49 #include <cstdio>
50
51 #include <boost/algorithm/string/case_conv.hpp>
52 #include <boost/algorithm/string/predicate.hpp>
53
54 #include "core/Config.h"
55 #include "core/Core.h"
56
57 #include "game/Entity.h"
58 #include "game/EntityManager.h"
59
60 #include "graphics/GraphicsTypes.h"
61 #include "graphics/Math.h"
62 #include "graphics/data/Progressive.h"
63 #include "graphics/data/FTL.h"
64 #include "graphics/data/TextureContainer.h"
65
66 #include "io/fs/FilePath.h"
67 #include "io/fs/SystemPaths.h"
68 #include "io/resource/ResourcePath.h"
69 #include "io/resource/PakReader.h"
70 #include "io/log/Logger.h"
71
72 #include "physics/Clothes.h"
73 #include "physics/Box.h"
74 #include "physics/CollisionShapes.h"
75
76 #include "scene/LinkedObject.h"
77 #include "scene/GameSound.h"
78 #include "scene/ObjectFormat.h"
79 #include "scene/Interactive.h"
80 #include "scene/Light.h"
81
82 #include "util/String.h"
83
84 using std::sprintf;
85 using std::min;
86 using std::max;
87 using std::string;
88 using std::vector;
89
90 void EERIE_RemoveCedricData(EERIE_3DOBJ * eobj);
91
92 void Clear3DScene(EERIE_3DSCENE * eerie);
93
GetGroupOriginByName(const EERIE_3DOBJ * eobj,const string & text)94 long GetGroupOriginByName(const EERIE_3DOBJ * eobj, const string & text) {
95
96 if(!eobj) {
97 return -1;
98 }
99
100 for(long i = 0; i < eobj->nbgroups; i++) {
101 if(eobj->grouplist[i].name == text) {
102 return eobj->grouplist[i].origin;
103 }
104 }
105
106 return -1;
107 }
108
GetActionPointIdx(const EERIE_3DOBJ * eobj,const string & text)109 long GetActionPointIdx(const EERIE_3DOBJ * eobj, const string & text) {
110
111 if(!eobj) {
112 return -1;
113 }
114
115 for(vector<EERIE_ACTIONLIST>::const_iterator i = eobj->actionlist.begin();
116 i != eobj->actionlist.end(); ++i) {
117 if(i->name == text) {
118 return i->idx;
119 }
120 }
121
122 return -1;
123 }
124
GetActionPointGroup(const EERIE_3DOBJ * eobj,long idx)125 long GetActionPointGroup(const EERIE_3DOBJ * eobj, long idx) {
126
127 if(!eobj) {
128 return -1;
129 }
130
131 for(long i = eobj->nbgroups - 1; i >= 0; i--) {
132 const vector<long> & indices = eobj->grouplist[i].indexes;
133 for(size_t j = 0; j < indices.size(); j++){
134 if(indices[j] == idx) {
135 return i;
136 }
137 }
138 }
139
140 return -1;
141 }
142
EERIE_Object_Precompute_Fast_Access(EERIE_3DOBJ * eerie)143 void EERIE_Object_Precompute_Fast_Access(EERIE_3DOBJ * eerie) {
144
145 if(!eerie) return;
146
147 long lVRight = GetActionPointIdx(eerie, "v_right");
148 long lURight = GetActionPointIdx(eerie, "u_right");
149 long lViewAttach = GetActionPointIdx(eerie, "view_attach") ;
150 long lPrimAttach = GetActionPointIdx(eerie, "primary_attach");
151 long lLeftAttach = GetActionPointIdx(eerie, "left_attach");
152
153 eerie->fastaccess.V_right = checked_range_cast<short>(lVRight);
154 eerie->fastaccess.U_right = checked_range_cast<short>(lURight);
155 eerie->fastaccess.view_attach = checked_range_cast<short>(lViewAttach);
156 eerie->fastaccess.primary_attach = checked_range_cast<short>(lPrimAttach);
157 eerie->fastaccess.left_attach = checked_range_cast<short>(lLeftAttach);
158
159
160 long lWeapAttach = GetActionPointIdx(eerie, "weapon_attach");
161 long lSecAttach = GetActionPointIdx(eerie, "secondary_attach");
162 long lJaw = EERIE_OBJECT_GetGroup(eerie, "jaw");
163 long lMouthAll = EERIE_OBJECT_GetGroup(eerie, "mouth all");
164
165 eerie->fastaccess.weapon_attach = checked_range_cast<short>(lWeapAttach);
166 eerie->fastaccess.secondary_attach = checked_range_cast<short>(lSecAttach);
167 eerie->fastaccess.jaw_group = checked_range_cast<short>(lJaw);
168 eerie->fastaccess.mouth_group = checked_range_cast<short>(lMouthAll);
169
170
171 if (eerie->fastaccess.mouth_group == -1)
172 eerie->fastaccess.mouth_group_origin = -1;
173 else
174 {
175 long lMouthOrigin = eerie->grouplist[eerie->fastaccess.mouth_group].origin;
176 eerie->fastaccess.mouth_group_origin = checked_range_cast<short>(lMouthOrigin);
177 }
178
179 long lHeadGroup = EERIE_OBJECT_GetGroup(eerie, "head");
180 eerie->fastaccess.head_group = checked_range_cast<short>(lHeadGroup);
181
182 if (eerie->fastaccess.head_group == -1)
183 eerie->fastaccess.head_group_origin = -1;
184 else
185 {
186 long lHeadOrigin = eerie->grouplist[eerie->fastaccess.head_group].origin;
187 eerie->fastaccess.head_group_origin = checked_range_cast<short>(lHeadOrigin);
188 }
189
190
191 long lFire = GetActionPointIdx(eerie, "fire");
192 long lCarryAttach = GetActionPointIdx(eerie, "carry_attach");
193 long lHead = EERIE_OBJECT_GetSelection(eerie, "head");
194 long lChest = EERIE_OBJECT_GetSelection(eerie, "chest");
195 long lLeggings = EERIE_OBJECT_GetSelection(eerie, "leggings") ;
196
197 eerie->fastaccess.fire = checked_range_cast<short>(lFire);
198 eerie->fastaccess.carry_attach = checked_range_cast<short>(lCarryAttach);
199 eerie->fastaccess.sel_head = checked_range_cast<short>(lHead);
200 eerie->fastaccess.sel_chest = checked_range_cast<short>(lChest);
201 eerie->fastaccess.sel_leggings = checked_range_cast<short>(lLeggings);
202 }
203
ReleaseAnim(EERIE_ANIM * ea)204 void ReleaseAnim(EERIE_ANIM * ea) {
205
206 if(!ea) {
207 return;
208 }
209
210 if(ea->frames) {
211 for(long i = 0; i < ea->nb_key_frames; i++) {
212 ARX_SOUND_Free(ea->frames[i].sample);
213 }
214 free(ea->frames);
215 }
216
217 free(ea->groups);
218 free(ea->voidgroups);
219 free(ea);
220 }
221
GetTimeBetweenKeyFrames(EERIE_ANIM * ea,long f1,long f2)222 float GetTimeBetweenKeyFrames(EERIE_ANIM * ea, long f1, long f2)
223 {
224 if (!ea) return 0;
225
226 if (f1 < 0) return 0;
227
228 if (f1 > ea->nb_key_frames - 1) return 0;
229
230 if (f2 < 0) return 0;
231
232 if (f2 > ea->nb_key_frames - 1) return 0;
233
234 float time = 0;
235
236 for (long kk = f1 + 1; kk <= f2; kk++)
237 {
238 time += ea->frames[kk].time;
239 }
240
241 return time;
242 }
243
244 template <class T>
allocStructZero(size_t n=1)245 static T * allocStructZero(size_t n = 1) {
246 T * result = (T*)malloc(n * sizeof(T));
247 memset(result, 0, n * sizeof(T));
248 return result;
249 }
250
251 template <class T>
copyStruct(const T * src,size_t n=1)252 static T * copyStruct(const T * src, size_t n = 1) {
253 T * result = (T*)malloc(n * sizeof(T));
254 memcpy(result, src, sizeof(T) * n);
255 return result;
256 }
257
TheaToEerie(const char * adr,size_t size,const res::path & file)258 EERIE_ANIM * TheaToEerie(const char * adr, size_t size, const res::path & file) {
259
260 (void)size; // TODO use size
261
262 LogDebug("Loading animation file " << file);
263
264 size_t pos = 0;
265
266 EERIE_ANIM * eerie = allocStructZero<EERIE_ANIM>();
267
268 const THEA_HEADER * th = reinterpret_cast<const THEA_HEADER *>(adr + pos);
269 if(th->version < 2014) {
270 LogError << "Invalid TEA Version " << th->version << " in " << file;
271 free(eerie);
272 return NULL;
273 }
274 pos += sizeof(THEA_HEADER);
275
276 LogDebug("TEA header size: " << sizeof(THEA_HEADER));
277 LogDebug("Identity " << th->identity);
278 LogDebug("Version - " << th->version << " Frames " << th->nb_frames
279 << " Groups " << th->nb_groups << " KeyFrames " << th->nb_key_frames);
280
281 eerie->nb_groups = th->nb_groups;
282 eerie->nb_key_frames = th->nb_key_frames;
283
284 eerie->frames = allocStructZero<EERIE_FRAME>(th->nb_key_frames);
285 eerie->groups = allocStructZero<EERIE_GROUP>(th->nb_key_frames * th->nb_groups);
286 eerie->voidgroups = allocStructZero<unsigned char>(th->nb_groups);
287
288 eerie->anim_time = 0.f;
289
290 // Go For Keyframes read
291 for(long i = 0; i < th->nb_key_frames; i++) {
292 LogDebug("Loading keyframe " << i);
293
294 THEA_KEYFRAME_2015 kf2015;
295 const THEA_KEYFRAME_2015 * tkf2015;
296 if(th->version >= 2015) {
297 LogDebug(" New keyframe version THEA_KEYFRAME_2015:" << sizeof(THEA_KEYFRAME_2015));
298 tkf2015 = reinterpret_cast<const THEA_KEYFRAME_2015 *>(adr + pos);
299 pos += sizeof(THEA_KEYFRAME_2015);
300 } else {
301 LogDebug(" Old keyframe version THEA_KEYFRAME:" << sizeof(THEA_KEYFRAME));
302 const THEA_KEYFRAME * tkf = reinterpret_cast<const THEA_KEYFRAME *>(adr + pos);
303 pos += sizeof(THEA_KEYFRAME);
304 memset(&kf2015, 0, sizeof(THEA_KEYFRAME_2015));
305 kf2015.num_frame = tkf->num_frame;
306 kf2015.flag_frame = tkf->flag_frame;
307 kf2015.master_key_frame = tkf->master_key_frame;
308 kf2015.key_frame = tkf->key_frame;
309 kf2015.key_move = tkf->key_move;
310 kf2015.key_orient = tkf->key_orient;
311 kf2015.key_morph = tkf->key_morph;
312 kf2015.time_frame = tkf->time_frame;
313 tkf2015 = &kf2015;
314 }
315
316 eerie->frames[i].master_key_frame = tkf2015->master_key_frame;
317 eerie->frames[i].num_frame = tkf2015->num_frame;
318
319 long lKeyOrient = tkf2015->key_orient;
320 long lKeyMove = tkf2015->key_move;
321 eerie->frames[i].f_rotate = checked_range_cast<short>(lKeyOrient);
322 eerie->frames[i].f_translate = checked_range_cast<short>(lKeyMove);
323
324 s32 time_frame = tkf2015->num_frame * 1000;
325 eerie->frames[i].time = time_frame * (1.f/24);
326 eerie->anim_time += time_frame;
327 eerie->frames[i].flag = tkf2015->flag_frame;
328
329 LogDebug(" pos " << pos << " - NumFr " << eerie->frames[i].num_frame
330 << " MKF " << tkf2015->master_key_frame << " THEA_KEYFRAME " << sizeof(THEA_KEYFRAME)
331 << " TIME " << (float)(eerie->frames[i].time / 1000.f) << "s -Move " << tkf2015->key_move
332 << " Orient " << tkf2015->key_orient << " Morph " << tkf2015->key_morph);
333
334 // Is There a Global translation ?
335 if(tkf2015->key_move != 0) {
336
337 const THEA_KEYMOVE * tkm = reinterpret_cast<const THEA_KEYMOVE *>(adr + pos);
338 pos += sizeof(THEA_KEYMOVE);
339
340 LogDebug(" -> move x " << tkm->x << " y " << tkm->y << " z " << tkm->z
341 << " THEA_KEYMOVE:" << sizeof(THEA_KEYMOVE));
342
343 eerie->frames[i].translate = *tkm;
344 }
345
346 // Is There a Global Rotation ?
347 if(tkf2015->key_orient != 0) {
348 pos += 8; // THEO_ANGLE
349
350 const ArxQuat * quat = reinterpret_cast<const ArxQuat *>(adr + pos);
351 pos += sizeof(ArxQuat);
352
353 LogDebug(" -> rotate x " << quat->x << " y " << quat->y << " z " << quat->z
354 << " w " << quat->w << " ArxQuat:" << sizeof(ArxQuat));
355
356 eerie->frames[i].quat = *quat;
357 }
358
359 // Is There a Global Morph ? (IGNORED!)
360 if(tkf2015->key_morph != 0) {
361 pos += 16; // THEA_MORPH
362 }
363
364 // Now go for Group Rotations/Translations/scaling for each GROUP
365 for(long j = 0; j < th->nb_groups; j++) {
366
367 const THEO_GROUPANIM * tga = reinterpret_cast<const THEO_GROUPANIM *>(adr + pos);
368 pos += sizeof(THEO_GROUPANIM);
369
370 EERIE_GROUP * eg = &eerie->groups[j + i * th->nb_groups];
371 eg->key = tga->key_group;
372 eg->quat = tga->Quaternion;
373 eg->translate = tga->translate;
374 eg->zoom = tga->zoom;
375 }
376
377 // Now Read Sound Data included in this frame
378 s32 num_sample = *reinterpret_cast<const s32 *>(adr + pos);
379 pos += sizeof(s32);
380 LogDebug(" -> num_sample " << num_sample << " s32:" << sizeof(s32));
381
382 eerie->frames[i].sample = -1;
383 if(num_sample != -1) {
384
385 const THEA_SAMPLE * ts = reinterpret_cast<const THEA_SAMPLE *>(adr + pos);
386 pos += sizeof(THEA_SAMPLE);
387 pos += ts->sample_size;
388
389 LogDebug(" -> sample " << ts->sample_name << " size " << ts->sample_size
390 << " THEA_SAMPLE:" << sizeof(THEA_SAMPLE));
391
392 eerie->frames[i].sample = ARX_SOUND_Load(res::path::load(util::loadString(ts->sample_name)));
393 }
394
395 pos += 4; // num_sfx
396 }
397
398 for(long i = 0; i < th->nb_key_frames; i++) {
399
400 if(!eerie->frames[i].f_translate) {
401
402 long k = i;
403 while((k >= 0) && (!eerie->frames[k].f_translate)) {
404 k--;
405 }
406
407 long j = i;
408 while((j < th->nb_key_frames) && (!eerie->frames[j].f_translate)) {
409 j++;
410 }
411
412 if((j < th->nb_key_frames) && (k >= 0)) {
413 float r1 = GetTimeBetweenKeyFrames(eerie, k, i);
414 float r2 = GetTimeBetweenKeyFrames(eerie, i, j);
415 float tot = 1.f / (r1 + r2);
416 r1 *= tot;
417 r2 *= tot;
418 eerie->frames[i].translate = eerie->frames[j].translate * r1 + eerie->frames[k].translate * r2;
419 }
420 }
421
422 if(!eerie->frames[i].f_rotate) {
423
424 long k = i;
425 while((k >= 0) && (!eerie->frames[k].f_rotate)) {
426 k--;
427 }
428
429 long j = i;
430 while ((j < th->nb_key_frames) && (!eerie->frames[j].f_rotate)) {
431 j++;
432 }
433
434 if ((j < th->nb_key_frames) && (k >= 0)) {
435 float r1 = GetTimeBetweenKeyFrames(eerie, k, i);
436 float r2 = GetTimeBetweenKeyFrames(eerie, i, j);
437 float tot = 1.f / (r1 + r2);
438 r1 *= tot;
439 r2 *= tot;
440 // TODO use overloaded operators
441 eerie->frames[i].quat.w = eerie->frames[j].quat.w * r1 + eerie->frames[k].quat.w * r2;
442 eerie->frames[i].quat.x = eerie->frames[j].quat.x * r1 + eerie->frames[k].quat.x * r2;
443 eerie->frames[i].quat.y = eerie->frames[j].quat.y * r1 + eerie->frames[k].quat.y * r2;
444 eerie->frames[i].quat.z = eerie->frames[j].quat.z * r1 + eerie->frames[k].quat.z * r2;
445 }
446 }
447 }
448
449 for(long i = 0; i < th->nb_key_frames; i++) {
450 eerie->frames[i].f_translate = true;
451 eerie->frames[i].f_rotate = true;
452 }
453
454 // Sets Flag for voidgroups (unmodified groups for whole animation)
455 for(long i = 0; i < eerie->nb_groups; i++) {
456
457 bool voidd = true;
458 for(long j = 0; j < eerie->nb_key_frames; j++) {
459 long pos = i + (j * eerie->nb_groups);
460
461 if((eerie->groups[pos].quat.x != 0.f)
462 || (eerie->groups[pos].quat.y != 0.f)
463 || (eerie->groups[pos].quat.z != 0.f)
464 || (eerie->groups[pos].quat.w != 1.f)
465 || eerie->groups[pos].translate != Vec3f::ZERO
466 || eerie->groups[pos].zoom != Vec3f::ZERO) {
467 voidd = false;
468 break;
469 }
470 }
471
472 if(voidd) {
473 eerie->voidgroups[i] = 1;
474 }
475 }
476
477 eerie->anim_time = th->nb_frames * 1000.f * (1.f/24);
478 if(eerie->anim_time < 1) {
479 eerie->anim_time = 1;
480 }
481
482 LogDebug("Finished Conversion TEA -> EERIE - " << (eerie->anim_time / 1000) << " seconds");
483
484 return eerie;
485 }
486
MakeUserFlag(TextureContainer * tc)487 void MakeUserFlag(TextureContainer * tc) {
488
489 if(tc == NULL) {
490 return;
491 }
492
493 const string & tex = tc->m_texName.string();
494
495 if(boost::contains(tex, "npc_")) {
496 tc->userflags |= POLY_LATE_MIP;
497 }
498
499 if(boost::contains(tex, "nocol")) {
500 tc->userflags |= POLY_NOCOL;
501 }
502
503 if(boost::contains(tex, "climb")) {
504 tc->userflags |= POLY_CLIMB;
505 }
506
507 if(boost::contains(tex, "fall")) {
508 tc->userflags |= POLY_FALL;
509 }
510
511 if(boost::contains(tex, "lava")) {
512 tc->userflags |= POLY_LAVA;
513 }
514
515 if(boost::contains(tex, "water") || boost::contains(tex, "spider_web")) {
516 tc->userflags |= POLY_WATER;
517 tc->userflags |= POLY_TRANS;
518 } else if(boost::contains(tex, "[metal]")) {
519 tc->userflags |= POLY_METAL;
520 }
521
522 }
523
524 #ifdef BUILD_EDIT_LOADSAVE
525
ReCreateUVs(EERIE_3DOBJ * eerie)526 static void ReCreateUVs(EERIE_3DOBJ * eerie) {
527
528 if(eerie->texturecontainer.empty()) {
529 return;
530 }
531
532 for(size_t i = 0; i < eerie->facelist.size(); i++) {
533
534 if(eerie->facelist[i].texid == -1) {
535 continue;
536 }
537
538 TextureContainer * tex = eerie->texturecontainer[eerie->facelist[i].texid];
539 Vec2f scale = (tex) ? Vec2f(1.f / tex->m_dwWidth, 1.f / tex->m_dwHeight) : (Vec2f::ONE / 256);
540
541 eerie->facelist[i].u[0] = (float)eerie->facelist[i].ou[0] * scale.x;
542 eerie->facelist[i].u[1] = (float)eerie->facelist[i].ou[1] * scale.x;
543 eerie->facelist[i].u[2] = (float)eerie->facelist[i].ou[2] * scale.x;
544 eerie->facelist[i].v[0] = (float)eerie->facelist[i].ov[0] * scale.y;
545 eerie->facelist[i].v[1] = (float)eerie->facelist[i].ov[1] * scale.y;
546 eerie->facelist[i].v[2] = (float)eerie->facelist[i].ov[2] * scale.y;
547 }
548 }
549
loadObjectData(EERIE_3DOBJ * eerie,const char * adr,size_t * poss,long version)550 static void loadObjectData(EERIE_3DOBJ * eerie, const char * adr, size_t * poss, long version) {
551
552 LogWarning << "loadObjectData";
553
554 size_t pos = *poss;
555
556 const THEO_OFFSETS * to = reinterpret_cast<const THEO_OFFSETS *>(adr + pos);
557 pos += sizeof(THEO_OFFSETS);
558
559 const THEO_NB * tn = reinterpret_cast<const THEO_NB *>(adr + pos);
560
561 LogDebug("Nb Vertex " << tn->nb_vertex << " Nb Action Points " << tn->nb_action_point
562 << " Nb Lines " << tn->nb_lines);
563 LogDebug("Nb Faces " << tn->nb_faces << " Nb Groups " << tn->nb_groups);
564
565 eerie->vertexlist.resize(tn->nb_vertex);
566 eerie->facelist.resize(tn->nb_faces);
567 eerie->nbgroups = tn->nb_groups;
568 eerie->actionlist.resize(tn->nb_action_point);
569
570 eerie->ndata = NULL;
571 eerie->pdata = NULL;
572 eerie->cdata = NULL;
573 eerie->sdata = NULL;
574
575 eerie->vertexlist3.resize(tn->nb_vertex);
576
577 if(tn->nb_groups == 0) {
578 eerie->grouplist = NULL;
579 } else {
580 eerie->grouplist = new EERIE_GROUPLIST[tn->nb_groups];
581 }
582
583 // read vertices
584
585 pos = to->vertex_seek;
586
587 if(tn->nb_vertex > 65535) {
588 LogError << ("Warning Vertex Number Too High...");
589 }
590
591 for(long i = 0; i < tn->nb_vertex; i++) {
592 const THEO_VERTEX * ptv = reinterpret_cast<const THEO_VERTEX *>(adr + pos);
593 pos += sizeof(THEO_VERTEX);
594 eerie->vertexlist[i].v = ptv->pos;
595 eerie->cub.xmin = min(eerie->cub.xmin, ptv->pos.x);
596 eerie->cub.xmax = max(eerie->cub.xmax, ptv->pos.x);
597 eerie->cub.ymin = min(eerie->cub.ymin, ptv->pos.y);
598 eerie->cub.ymax = max(eerie->cub.ymax, ptv->pos.y);
599 eerie->cub.zmin = min(eerie->cub.zmin, ptv->pos.z);
600 eerie->cub.zmax = max(eerie->cub.zmax, ptv->pos.z);
601 }
602
603 // Lecture des FACES THEO
604 pos = to->faces_seek;
605
606 for(long i = 0; i < tn->nb_faces; i++) {
607
608 THEO_FACES_3006 tf3006;
609 const THEO_FACES_3006 * ptf3006;
610 if(version >= 3006) {
611 ptf3006 = reinterpret_cast<const THEO_FACES_3006 *>(adr + pos);
612 pos += sizeof(THEO_FACES_3006);
613 } else {
614 memset(&tf3006, 0, sizeof(THEO_FACES_3006));
615 const THEO_FACES * ptf = reinterpret_cast<const THEO_FACES *>(adr + pos);
616 pos += sizeof(THEO_FACES);
617 tf3006.color = ptf->color;
618 tf3006.index1 = ptf->index1;
619 tf3006.index2 = ptf->index2;
620 tf3006.index3 = ptf->index3;
621 tf3006.ismap = ptf->ismap;
622 tf3006.liste_uv = ptf->liste_uv;
623 tf3006.element_uv = ptf->element_uv;
624 tf3006.num_map = ptf->num_map;
625 tf3006.tile_x = ptf->tile_x;
626 tf3006.tile_y = ptf->tile_y;
627 tf3006.user_tile_x = ptf->user_tile_x;
628 tf3006.user_tile_y = ptf->user_tile_y;
629 tf3006.flag = ptf->flag;
630 tf3006.collision_type = ptf->collision_type;
631 tf3006.rgb = ptf->rgb;
632 tf3006.rgb1 = ptf->rgb1;
633 tf3006.rgb2 = ptf->rgb2;
634 tf3006.rgb3 = ptf->rgb3;
635 tf3006.double_side = ptf->double_side;
636 tf3006.transparency = ptf->transparency;
637 tf3006.trans = ptf->trans;
638 ptf3006 = &tf3006;
639 }
640
641 eerie->facelist[i].vid[0] = (unsigned short)ptf3006->index1;
642 eerie->facelist[i].vid[1] = (unsigned short)ptf3006->index2;
643 eerie->facelist[i].vid[2] = (unsigned short)ptf3006->index3;
644
645 s32 num_map = ((size_t)ptf3006->num_map >= eerie->texturecontainer.size()) ? -1 : ptf3006->num_map;
646
647 if(ptf3006->ismap) {
648 eerie->facelist[i].texid = (short)num_map;
649 eerie->facelist[i].facetype = POLY_NO_SHADOW;
650
651 if(num_map >= 0 && eerie->texturecontainer[num_map] && (eerie->texturecontainer[num_map]->userflags & POLY_NOCOL)) {
652 eerie->facelist[i].facetype |= POLY_NOCOL;
653 }
654 } else if(ptf3006->rgb) {
655 eerie->facelist[i].texid = -1;
656 } else {
657 eerie->facelist[i].texid = -1;
658 }
659
660 switch(ptf3006->flag) {
661 case 0:
662 eerie->facelist[i].facetype |= POLY_GLOW;
663 break;
664 case 1:
665 eerie->facelist[i].facetype |= POLY_NO_SHADOW;
666 break;
667 case 4:
668 eerie->facelist[i].facetype |= POLY_METAL;
669 break;
670 case 10:
671 eerie->facelist[i].facetype |= POLY_NOPATH;
672 break;
673 case 11:
674 eerie->facelist[i].facetype |= POLY_CLIMB;
675 break;
676 case 12:
677 eerie->facelist[i].facetype |= POLY_NOCOL;
678 break;
679 case 13:
680 eerie->facelist[i].facetype |= POLY_NODRAW;
681 break;
682 case 14:
683 eerie->facelist[i].facetype |= POLY_PRECISE_PATH;
684 break;
685 case 16:
686 eerie->facelist[i].facetype |= POLY_NO_CLIMB;
687 break;
688 }
689
690 eerie->facelist[i].ou[0] = (short)ptf3006->liste_uv.u1;
691 eerie->facelist[i].ov[0] = (short)ptf3006->liste_uv.v1;
692 eerie->facelist[i].ou[1] = (short)ptf3006->liste_uv.u2;
693 eerie->facelist[i].ov[1] = (short)ptf3006->liste_uv.v2;
694 eerie->facelist[i].ou[2] = (short)ptf3006->liste_uv.u3;
695 eerie->facelist[i].ov[2] = (short)ptf3006->liste_uv.v3;
696
697 if(ptf3006->double_side) {
698 eerie->facelist[i].facetype |= POLY_DOUBLESIDED;
699 }
700
701 if(ptf3006->transparency > 0) {
702 if(ptf3006->transparency == 2) {
703 // NORMAL TRANS 0.00001 to 0.999999
704 if(ptf3006->trans < 1.f) {
705 eerie->facelist[i].facetype |= POLY_TRANS;
706 eerie->facelist[i].transval = ptf3006->trans;
707 }
708 }
709 else if (ptf3006->transparency == 1) {
710 if(ptf3006->trans < 0.f) {
711 // SUBTRACTIVE -0.000001 to -0.999999
712 eerie->facelist[i].facetype |= POLY_TRANS;
713 eerie->facelist[i].transval = ptf3006->trans;
714 } else {
715 // ADDITIVE 1.000001 to 1.9999999
716 eerie->facelist[i].facetype |= POLY_TRANS;
717 eerie->facelist[i].transval = ptf3006->trans + 1.f;
718 }
719 } else {
720 // MULTIPLICATIVE 2.000001 to 2.9999999
721 eerie->facelist[i].facetype |= POLY_TRANS;
722 eerie->facelist[i].transval = ptf3006->trans + 2.f;
723 }
724 }
725
726 if(eerie->facelist[i].texid != -1 && !eerie->texturecontainer.empty() && eerie->texturecontainer[eerie->facelist[i].texid] != NULL) {
727
728 if(eerie->texturecontainer[eerie->facelist[i].texid]->userflags & POLY_TRANS) {
729 if(!(eerie->facelist[i].facetype & POLY_TRANS)) {
730 eerie->facelist[i].facetype |= POLY_TRANS;
731 eerie->facelist[i].transval = ptf3006->trans;
732 }
733 }
734
735 if(eerie->texturecontainer[eerie->facelist[i].texid]->userflags & POLY_WATER) {
736 eerie->facelist[i].facetype |= POLY_WATER;
737 }
738
739 if(eerie->texturecontainer[eerie->facelist[i].texid]->userflags & POLY_LAVA) {
740 eerie->facelist[i].facetype |= POLY_LAVA;
741 }
742
743 if(eerie->texturecontainer[eerie->facelist[i].texid]->userflags & POLY_FALL) {
744 eerie->facelist[i].facetype |= POLY_FALL;
745 }
746
747 if(eerie->texturecontainer[eerie->facelist[i].texid]->userflags & POLY_CLIMB) {
748 eerie->facelist[i].facetype |= POLY_CLIMB;
749 }
750 }
751
752 }
753
754 // Groups Data
755 pos = to->groups_seek;
756
757 for(long i = 0; i < tn->nb_groups; i++) {
758
759 THEO_GROUPS_3011 tg3011;
760 const THEO_GROUPS_3011 * ptg3011;
761 if(version >= 3011) {
762 ptg3011 = reinterpret_cast<const THEO_GROUPS_3011 *>(adr + pos);
763 pos += sizeof(THEO_GROUPS_3011);
764 } else {
765 const THEO_GROUPS * ltg = reinterpret_cast<const THEO_GROUPS *>(adr + pos);
766 pos += sizeof(THEO_GROUPS);
767 memset(&tg3011, 0, sizeof(THEO_GROUPS_3011));
768 tg3011.origin = ltg->origin;
769 tg3011.nb_index = ltg->nb_index;
770 ptg3011 = &tg3011;
771 }
772
773 eerie->grouplist[i].origin = ptg3011->origin;
774 eerie->grouplist[i].indexes.resize(ptg3011->nb_index);
775
776 std::copy((const long*)(adr + pos), (const long*)(adr + pos) + ptg3011->nb_index, eerie->grouplist[i].indexes.begin());
777 pos += ptg3011->nb_index * sizeof(long);
778
779 eerie->grouplist[i].name = boost::to_lower_copy(util::loadString(adr + pos, 256));
780 pos += 256;
781 eerie->grouplist[i].siz = 0.f;
782
783 for(long o = 0; o < ptg3011->nb_index; o++) {
784 eerie->grouplist[i].siz = max(eerie->grouplist[i].siz,
785 fdist(eerie->vertexlist[eerie->grouplist[i].origin].v,
786 eerie->vertexlist[eerie->grouplist[i].indexes[o]].v));
787 }
788
789 eerie->grouplist[i].siz = ffsqrt(eerie->grouplist[i].siz) * (1.f/16);
790
791 }
792
793 // SELECTIONS
794 s32 THEO_nb_selected = *reinterpret_cast<const s32 *>(adr + pos);
795 pos += sizeof(s32);
796
797 eerie->selections.resize(THEO_nb_selected);
798 for(long i = 0; i < THEO_nb_selected; i++) {
799
800 const THEO_SELECTED * pts = reinterpret_cast<const THEO_SELECTED *>(adr + pos);
801 pos += sizeof(THEO_SELECTED);
802
803 eerie->selections[i].name = boost::to_lower_copy(util::loadString(pts->name));
804 eerie->selections[i].selected.resize(pts->nb_index);
805
806 if(pts->nb_index > 0) {
807 std::copy((const long*)(adr + pos), (const long*)(adr + pos) + pts->nb_index, eerie->selections[i].selected.begin());
808 pos += sizeof(long) * pts->nb_index;
809 }
810 }
811
812 // Theo Action Points Read
813 pos = to->action_point_seek;
814
815 for(long i = 0; i < tn->nb_action_point; i++) {
816
817 const THEO_ACTION_POINT * ptap = reinterpret_cast<const THEO_ACTION_POINT *>(adr + pos);
818 pos += sizeof(THEO_ACTION_POINT);
819
820 eerie->actionlist[i].act = ptap->action;
821 eerie->actionlist[i].sfx = ptap->num_sfx;
822 eerie->actionlist[i].idx = ptap->vert_index;
823 eerie->actionlist[i].name = boost::to_lower_copy(util::loadString(ptap->name));
824 }
825
826 eerie->angle = Anglef::ZERO;
827 eerie->pos = Vec3f::ZERO;
828
829 // Now Interpret Extra Data chunk
830 pos = to->extras_seek + 4;
831
832 if(version >= 3005) {
833
834 const THEO_EXTRA_DATA_3005 * pted3005 = reinterpret_cast<const THEO_EXTRA_DATA_3005 *>(adr + pos);
835 pos += sizeof(THEO_EXTRA_DATA_3005);
836
837 eerie->pos = pted3005->pos;
838
839 eerie->angle.a = (float)(pted3005->angle.alpha & 0xfff) * THEO_ROTCONVERT;
840 eerie->angle.b = (float)(pted3005->angle.beta & 0xfff) * THEO_ROTCONVERT;
841 eerie->angle.g = (float)(pted3005->angle.gamma & 0xfff) * THEO_ROTCONVERT;
842
843 eerie->point0 = eerie->vertexlist[pted3005->origin_index].v;
844 eerie->origin = pted3005->origin_index;
845
846 eerie->quat = pted3005->quat;
847
848 } else {
849
850 const THEO_EXTRA_DATA * pted = reinterpret_cast<const THEO_EXTRA_DATA *>(adr + pos);
851 pos += sizeof(THEO_EXTRA_DATA);
852
853 eerie->pos = pted->pos;
854
855 eerie->angle.a = (float)(pted->angle.alpha & 0xfff) * THEO_ROTCONVERT;
856 eerie->angle.b = (float)(pted->angle.beta & 0xfff) * THEO_ROTCONVERT;
857 eerie->angle.g = (float)(pted->angle.gamma & 0xfff) * THEO_ROTCONVERT;
858
859 eerie->point0 = eerie->vertexlist[pted->origin_index].v;
860 eerie->origin = pted->origin_index;
861
862 }
863
864 *poss = pos;
865
866 eerie->vertexlist3 = eerie->vertexlist;
867 ReCreateUVs(eerie);
868 EERIE_Object_Precompute_Fast_Access(eerie);
869 }
870
ScnToEerie(const char * adr,size_t size,const res::path & fic)871 static EERIE_3DSCENE * ScnToEerie(const char * adr, size_t size, const res::path & fic) {
872
873 (void)size; // TODO use size
874
875 LogDebug("Loading Scene " << fic);
876
877 size_t pos = 0;
878
879 EERIE_3DSCENE * seerie = allocStructZero<EERIE_3DSCENE>();
880 Clear3DScene(seerie);
881
882 const TSCN_HEADER * psth = reinterpret_cast<const TSCN_HEADER *>(adr + pos);
883 pos += sizeof(TSCN_HEADER);
884
885 LogDebug("SCNtoEERIE " << fic << " Version " << psth->version << " Nb Textures " << psth->nb_maps);
886
887 if(psth->version < 3008 || psth->version > 3024) {
888 LogError << "ScnToEerie: invalid version in " << fic << ": found " << psth->version
889 << " expected 3008 to 3024";
890 free(seerie);
891 return NULL;
892 }
893
894 seerie->nbtex = psth->nb_maps;
895
896 const res::path temp = "graph/obj3d/textures";
897
898 if(psth->type_write == 0) {
899
900 seerie->texturecontainer = allocStructZero<TextureContainer *>(psth->nb_maps);
901
902 for(long i = 0; i < psth->nb_maps; i++) {
903
904 const THEO_TEXTURE * tt = reinterpret_cast<const THEO_TEXTURE *>(adr + pos);
905 pos += sizeof(THEO_TEXTURE);
906
907 res::path mapsname = temp / res::path::load(util::loadString(tt->texture_name)).remove_ext();
908 seerie->texturecontainer[i] = TextureContainer::Load(mapsname, TextureContainer::Level);
909 }
910
911 } else {
912
913 if((psth->type_write & SAVE_MAP_BMP) || (psth->type_write & SAVE_MAP_TGA)) {
914
915 seerie->texturecontainer = allocStructZero<TextureContainer *>(psth->nb_maps);
916
917 for(long i = 0; i < psth->nb_maps; i++) {
918
919 res::path name;
920 if(psth->version >= 3019) {
921 const THEO_SAVE_MAPS_IN_3019 * tsmi3019 = reinterpret_cast<const THEO_SAVE_MAPS_IN_3019 *>(adr + pos);
922 pos += sizeof(THEO_SAVE_MAPS_IN_3019);
923 name = res::path::load(util::loadString(tsmi3019->texture_name)).remove_ext();
924 } else {
925 const THEO_SAVE_MAPS_IN * tsmi = reinterpret_cast<const THEO_SAVE_MAPS_IN *>(adr + pos);
926 pos += sizeof(THEO_SAVE_MAPS_IN);
927 name = res::path::load(util::loadString(tsmi->texture_name)).remove_ext();
928 }
929
930 if(!name.empty()) {
931 seerie->texturecontainer[i] = TextureContainer::Load(temp / name, TextureContainer::Level);
932 }
933 }
934 }
935 }
936
937 // read objects
938 pos = psth->object_seek;
939
940 s32 nbo = *reinterpret_cast<const s32 *>(adr + pos);
941 pos += sizeof(s32);
942
943 seerie->nbobj = nbo;
944 seerie->objs = allocStructZero<EERIE_3DOBJ *>(nbo);
945
946 seerie->point0 = Vec3f::repeat(-999999999999.f);
947
948 long id = 0;
949
950 for(long i = 0; i < nbo; i++) {
951
952 const TSCN_OBJHEADER * ptoh = reinterpret_cast<const TSCN_OBJHEADER *>(adr + pos);
953 pos += sizeof(TSCN_OBJHEADER);
954
955 seerie->objs[id] = new EERIE_3DOBJ();
956 // TODO most is done in the constructor already
957 seerie->objs[id]->clear();
958
959 seerie->objs[id]->texturecontainer.resize(seerie->nbtex);
960 std::copy(seerie->texturecontainer, seerie->texturecontainer + seerie->nbtex, seerie->objs[id]->texturecontainer.begin());
961
962 long objVersion;
963 if(psth->version < 3013) {
964 objVersion = 3004;
965 } else if(psth->version < 3015) {
966 objVersion = 3005;
967 } else if(psth->version < 3019) {
968 objVersion = 3006;
969 } else if(psth->version < 3023) {
970 objVersion = 3008;
971 } else {
972 objVersion = 3011;
973 }
974 loadObjectData(seerie->objs[id], adr, &pos, objVersion);
975
976 seerie->cub.xmin = min(seerie->cub.xmin, seerie->objs[id]->cub.xmin + seerie->objs[id]->pos.x);
977 seerie->cub.xmax = max(seerie->cub.xmax, seerie->objs[id]->cub.xmax + seerie->objs[id]->pos.x);
978 seerie->cub.ymin = min(seerie->cub.ymin, seerie->objs[id]->cub.ymin + seerie->objs[id]->pos.y);
979 seerie->cub.ymax = max(seerie->cub.ymax, seerie->objs[id]->cub.ymax + seerie->objs[id]->pos.y);
980 seerie->cub.zmin = min(seerie->cub.zmin, seerie->objs[id]->cub.zmin + seerie->objs[id]->pos.z);
981 seerie->cub.zmax = max(seerie->cub.zmax, seerie->objs[id]->cub.zmax + seerie->objs[id]->pos.z);
982
983 string name = boost::to_lower_copy(util::loadString(ptoh->object_name));
984 if(name == "map_origin") {
985 seerie->point0 = seerie->objs[id]->point0 + seerie->objs[id]->pos;
986 delete seerie->objs[id];
987 seerie->nbobj--;
988 id--;
989 } else {
990 seerie->objs[id]->name = name;
991 }
992
993 id++;
994
995 pos = ptoh->next_obj;
996 }
997
998 pos = psth->light_seek; // ambient
999
1000 pos += sizeof(SavedColor); // ignore ambient color
1001
1002 s32 nbl = *reinterpret_cast<const s32 *>(adr + pos);
1003 pos += sizeof(s32);
1004
1005 seerie->light = NULL;
1006 seerie->nblight = nbl;
1007
1008 for(long i = 0; i < nbl; i++) {
1009
1010 TSCN_LIGHT_3024 sl3024;
1011 const TSCN_LIGHT_3024 * tsl3024;
1012
1013 if(psth->version >= 3024) {
1014 tsl3024 = reinterpret_cast<const TSCN_LIGHT_3024 *>(adr + pos);
1015 pos += sizeof(TSCN_LIGHT_3024);
1016 } else if(psth->version >= 3019) {
1017 const TSCN_LIGHT_3019 * tsl3019 = reinterpret_cast<const TSCN_LIGHT_3019 *>(adr + pos);
1018 pos += sizeof(TSCN_LIGHT_3019);
1019 memset(&sl3024, 0, sizeof(TSCN_LIGHT_3024));
1020 sl3024.red = tsl3019->red;
1021 sl3024.green = tsl3019->green;
1022 sl3024.blue = tsl3019->blue;
1023 sl3024.pos = tsl3019->pos;
1024 sl3024.hotspot = tsl3019->hotspot;
1025 sl3024.falloff = tsl3019->falloff;
1026 sl3024.intensity = tsl3019->intensity;
1027 tsl3024 = &sl3024;
1028 } else {
1029 const TSCN_LIGHT * tsl = reinterpret_cast<const TSCN_LIGHT *>(adr + pos);
1030 pos += sizeof(TSCN_LIGHT);
1031 memset(&sl3024, 0, sizeof(TSCN_LIGHT_3024));
1032 sl3024.red = tsl->red;
1033 sl3024.green = tsl->green;
1034 sl3024.blue = tsl->blue;
1035 sl3024.pos = tsl->pos;
1036 sl3024.hotspot = tsl->hotspot;
1037 sl3024.falloff = tsl->falloff;
1038 sl3024.intensity = tsl->intensity;
1039 tsl3024 = &sl3024;
1040 }
1041
1042 EERIE_LIGHT light;
1043
1044 light.rgb.r = (float)tsl3024->red * ( 1.0f / 255 );
1045 light.rgb.g = (float)tsl3024->green * ( 1.0f / 255 );
1046 light.rgb.b = (float)tsl3024->blue * ( 1.0f / 255 );
1047 light.pos = tsl3024->pos;
1048 light.fallstart = (float)tsl3024->hotspot;
1049 light.fallend = (float)tsl3024->falloff;
1050
1051 float t = light.fallend - light.fallstart;
1052 if(t < 150.f) {
1053 light.fallend += 150.f - t;
1054 }
1055
1056 light.intensity = (float)tsl3024->intensity;
1057 light.exist = 1;
1058 light.treat = 1;
1059 light.selected = 0;
1060 light.type = 0;
1061 EERIE_LIGHT_GlobalAdd(&light);
1062 }
1063
1064 return seerie;
1065 }
1066
ReleaseScene(EERIE_3DSCENE * scene)1067 static void ReleaseScene(EERIE_3DSCENE * scene) {
1068
1069 free(scene->texturecontainer), scene->texturecontainer = NULL;
1070
1071 for(long i = 0; i < scene->nbobj; i++) {
1072 delete scene->objs[i];
1073 }
1074
1075 free(scene->objs), scene->objs = NULL;
1076 free(scene->texturecontainer), scene->texturecontainer = NULL;
1077
1078 if(scene->light) {
1079 for(long i = 0; i < scene->nblight; i++) {
1080 free(scene->light[i]), scene->light[i] = NULL;
1081 }
1082 free(scene->light), scene->light = NULL;
1083 }
1084
1085 free(scene);
1086 }
1087
ReleaseMultiScene(EERIE_MULTI3DSCENE * ms)1088 void ReleaseMultiScene(EERIE_MULTI3DSCENE * ms) {
1089
1090 if(ms) {
1091 for(long i = 0; i < ms->nb_scenes; i++) {
1092 ReleaseScene(ms->scenes[i]);
1093 ms->scenes[i] = NULL;
1094 }
1095 }
1096
1097 free(ms);
1098 }
1099
PAK_MultiSceneToEerie_Impl(const res::path & dirr)1100 static EERIE_MULTI3DSCENE * PAK_MultiSceneToEerie_Impl(const res::path & dirr) {
1101
1102 EERIE_MULTI3DSCENE * es = allocStructZero<EERIE_MULTI3DSCENE>();
1103
1104 LastLoadedScene = dirr;
1105
1106 PakDirectory * dir = resources->getDirectory(dirr);
1107 if(dir) {
1108 bool loaded = false;
1109 for(PakDirectory::files_iterator i = dir->files_begin(); i != dir->files_end(); i++) {
1110 if(!res::path(i->first).has_ext("scn")) {
1111 continue;
1112 }
1113
1114 char * adr = i->second->readAlloc();
1115 if(adr) {
1116 es->scenes[es->nb_scenes] = ScnToEerie(adr, i->second->size(), dirr);
1117 es->nb_scenes++;
1118 free(adr);
1119 } else {
1120 LogError << "Could not read scene " << dirr << '/' << i->first;
1121 }
1122
1123 loaded = true;
1124 }
1125 if(!loaded) {
1126 LogWarning << "Empty multiscene: " << dirr;
1127 }
1128 } else {
1129 LogWarning << "Multiscene not found: " << dirr;
1130 }
1131
1132 es->cub.xmax = -9999999999.f;
1133 es->cub.xmin = 9999999999.f;
1134 es->cub.ymax = -9999999999.f;
1135 es->cub.ymin = 9999999999.f;
1136 es->cub.zmax = -9999999999.f;
1137 es->cub.zmin = 9999999999.f;
1138
1139 for(long i = 0; i < es->nb_scenes; i++) {
1140 es->cub.xmax = max(es->cub.xmax, es->scenes[i]->cub.xmax);
1141 es->cub.xmin = min(es->cub.xmin, es->scenes[i]->cub.xmin);
1142 es->cub.ymax = max(es->cub.ymax, es->scenes[i]->cub.ymax);
1143 es->cub.ymin = min(es->cub.ymin, es->scenes[i]->cub.ymin);
1144 es->cub.zmax = max(es->cub.zmax, es->scenes[i]->cub.zmax);
1145 es->cub.zmin = min(es->cub.zmin, es->scenes[i]->cub.zmin);
1146 es->pos = es->scenes[i]->pos;
1147
1148 if((es->scenes[i]->point0.x != -999999999999.f) &&
1149 (es->scenes[i]->point0.y != -999999999999.f) &&
1150 (es->scenes[i]->point0.z != -999999999999.f)) {
1151 es->point0 = es->scenes[i]->point0;
1152 }
1153 }
1154
1155 if(es->nb_scenes == 0) {
1156 free(es);
1157 return NULL;
1158 }
1159
1160 return es;
1161 }
1162
PAK_MultiSceneToEerie(const res::path & dirr)1163 EERIE_MULTI3DSCENE * PAK_MultiSceneToEerie(const res::path & dirr) {
1164
1165 LogDebug("Loading Multiscene " << dirr);
1166
1167 EERIE_MULTI3DSCENE * em = NULL;
1168
1169 em = PAK_MultiSceneToEerie_Impl(dirr);
1170
1171 EERIEPOLY_Compute_PolyIn();
1172 return em;
1173 }
1174
1175 #endif // BUILD_EDIT_LOADSAVE
1176
1177 //-----------------------------------------------------------------------------------------------------
1178 // Warning Clear3DObj/Clear3DScene don't release Any pointer Just Clears Structures
clear()1179 void EERIE_3DOBJ::clear() {
1180
1181 point0 = pos = Vec3f::ZERO;
1182 angle = Anglef::ZERO;
1183
1184 origin = 0;
1185 ident = 0;
1186 nbgroups = 0;
1187 drawflags = 0;
1188
1189 vertexlocal = NULL;
1190 vertexlist.clear();
1191 vertexlist3.clear();
1192
1193 facelist.clear();
1194 grouplist = NULL;
1195 texturecontainer.clear();
1196
1197 originaltextures = NULL;
1198 linked = NULL;
1199
1200 // TODO Default constructor
1201 quat.x = quat.y = quat.z = quat.w = 0;
1202 nblinked = 0;
1203
1204 pbox = 0;
1205 pdata = 0;
1206 ndata = 0;
1207 cdata = 0;
1208 sdata = 0;
1209
1210 fastaccess.view_attach = 0;
1211 fastaccess.primary_attach = 0;
1212 fastaccess.left_attach = 0;
1213 fastaccess.weapon_attach = 0;
1214 fastaccess.secondary_attach = 0;
1215 fastaccess.mouth_group = 0;
1216 fastaccess.jaw_group = 0;
1217 fastaccess.head_group_origin = 0;
1218 fastaccess.head_group = 0;
1219 fastaccess.mouth_group_origin = 0;
1220 fastaccess.V_right = 0;
1221 fastaccess.U_right = 0;
1222 fastaccess.fire = 0;
1223 fastaccess.sel_head = 0;
1224 fastaccess.sel_chest = 0;
1225 fastaccess.sel_leggings = 0;
1226 fastaccess.carry_attach = 0;
1227 fastaccess.padding_ = 0;
1228
1229 c_data = 0;
1230
1231 cub.xmin = cub.ymin = cub.zmin = std::numeric_limits<float>::max();
1232 cub.xmax = cub.ymax = cub.zmax = std::numeric_limits<float>::min();
1233 }
1234
Clear3DScene(EERIE_3DSCENE * eerie)1235 void Clear3DScene(EERIE_3DSCENE * eerie) {
1236
1237 if(eerie == NULL) {
1238 return;
1239 }
1240
1241 memset(eerie, 0, sizeof(EERIE_3DSCENE));
1242 eerie->cub.xmin = eerie->cub.ymin = eerie->cub.zmin = std::numeric_limits<float>::max();
1243 eerie->cub.xmax = eerie->cub.ymax = eerie->cub.zmax = std::numeric_limits<float>::min();
1244 }
1245
1246 // TODO move to destructor?
~EERIE_3DOBJ()1247 EERIE_3DOBJ::~EERIE_3DOBJ() {
1248
1249 free(originaltextures), originaltextures = NULL;
1250
1251 if(ndata) {
1252 KillNeighbours(this);
1253 }
1254
1255 if(pdata) {
1256 KillProgressiveData(this);
1257 }
1258
1259 if(cdata) {
1260 KillClothesData(this);
1261 }
1262
1263 EERIE_RemoveCedricData(this);
1264 EERIE_PHYSICS_BOX_Release(this);
1265 EERIE_COLLISION_SPHERES_Release(this);
1266
1267 delete[] grouplist;
1268 free(linked);
1269 }
1270
Eerie_Copy(const EERIE_3DOBJ * obj)1271 EERIE_3DOBJ * Eerie_Copy(const EERIE_3DOBJ * obj) {
1272
1273 EERIE_3DOBJ * nouvo = new EERIE_3DOBJ();
1274
1275 nouvo->vertexlist = obj->vertexlist;
1276
1277 nouvo->vertexlist3 = obj->vertexlist3;
1278
1279 nouvo->linked = NULL;
1280 nouvo->ndata = NULL;
1281 nouvo->pbox = NULL;
1282 nouvo->pdata = NULL;
1283 nouvo->cdata = NULL;
1284 nouvo->sdata = NULL;
1285 nouvo->c_data = NULL;
1286 nouvo->vertexlocal = NULL;
1287
1288 nouvo->angle = obj->angle;
1289 nouvo->pos = obj->pos;
1290 nouvo->cub.xmax = obj->cub.xmax;
1291 nouvo->cub.xmin = obj->cub.xmin;
1292 nouvo->cub.ymax = obj->cub.ymax;
1293 nouvo->cub.ymin = obj->cub.ymin;
1294 nouvo->cub.zmax = obj->cub.zmax;
1295 nouvo->cub.zmin = obj->cub.zmin;
1296 nouvo->drawflags = obj->drawflags;
1297
1298 if ( !obj->file.empty() )
1299 nouvo->file = obj->file;
1300
1301 nouvo->ident = obj->ident;
1302
1303 if ( !obj->name.empty() )
1304 nouvo->name = obj->name;
1305
1306 nouvo->origin = obj->origin;
1307 nouvo->point0 = obj->point0;
1308 Quat_Copy(&nouvo->quat, &obj->quat);
1309
1310
1311 if (obj->ndata)
1312 {
1313 nouvo->ndata = copyStruct(obj->ndata, obj->vertexlist.size());
1314 }
1315 else nouvo->ndata = NULL;
1316
1317 nouvo->facelist = obj->facelist;
1318
1319 if (obj->nbgroups) {
1320 nouvo->nbgroups = obj->nbgroups;
1321 nouvo->grouplist = new EERIE_GROUPLIST[obj->nbgroups];
1322 std::copy(obj->grouplist, obj->grouplist + obj->nbgroups, nouvo->grouplist);
1323 }
1324
1325 nouvo->actionlist = obj->actionlist;
1326
1327 nouvo->selections = obj->selections;
1328
1329 nouvo->texturecontainer = obj->texturecontainer;
1330
1331 memcpy(&nouvo->fastaccess, &obj->fastaccess, sizeof(EERIE_FASTACCESS));
1332 EERIE_CreateCedricData(nouvo);
1333
1334 if (obj->pbox)
1335 {
1336 nouvo->pbox = allocStructZero<PHYSICS_BOX_DATA>();
1337 nouvo->pbox->nb_physvert = obj->pbox->nb_physvert;
1338 nouvo->pbox->stopcount = 0;
1339 nouvo->pbox->radius = obj->pbox->radius;
1340
1341 nouvo->pbox->vert = copyStruct(obj->pbox->vert, obj->pbox->nb_physvert);
1342 }
1343
1344 nouvo->linked = NULL;
1345 nouvo->nblinked = 0;
1346 nouvo->originaltextures = NULL;
1347 return nouvo;
1348 }
1349
EERIE_OBJECT_GetSelection(const EERIE_3DOBJ * obj,const string & selname)1350 long EERIE_OBJECT_GetSelection(const EERIE_3DOBJ * obj, const string & selname) {
1351
1352 if(!obj) {
1353 return -1;
1354 }
1355
1356 for(size_t i = 0; i < obj->selections.size(); i++) {
1357 if(obj->selections[i].name == selname) {
1358 return i;
1359 }
1360 }
1361
1362 return -1;
1363 }
1364
EERIE_OBJECT_GetGroup(const EERIE_3DOBJ * obj,const string & groupname)1365 long EERIE_OBJECT_GetGroup(const EERIE_3DOBJ * obj, const string & groupname) {
1366
1367 if(!obj) {
1368 return -1;
1369 }
1370
1371 for(long i = 0; i < obj->nbgroups; i++) {
1372 if(obj->grouplist[i].name == groupname) {
1373 return i;
1374 }
1375 }
1376
1377 return -1;
1378 }
1379
AddIdxToBone(EERIE_BONE * bone,long idx)1380 void AddIdxToBone(EERIE_BONE * bone, long idx)
1381 {
1382 bone->idxvertices = (long *)realloc(bone->idxvertices, sizeof(long) * (bone->nb_idxvertices + 1));
1383
1384 if (bone->idxvertices)
1385 {
1386 bone->idxvertices[bone->nb_idxvertices] = idx;
1387 bone->nb_idxvertices++;
1388 }
1389 }
1390 //-----------------------------------------------------------------------------------------------------
GetFather(EERIE_3DOBJ * eobj,long origin,long startgroup)1391 long GetFather(EERIE_3DOBJ * eobj, long origin, long startgroup)
1392 {
1393 for (long i = startgroup; i >= 0; i--)
1394 {
1395 for (size_t j = 0; j < eobj->grouplist[i].indexes.size(); j++)
1396 {
1397 if (eobj->grouplist[i].indexes[j] == origin)
1398 {
1399 return i;
1400 }
1401 }
1402 }
1403
1404 return -1;
1405 }
1406
EERIE_RemoveCedricData(EERIE_3DOBJ * eobj)1407 void EERIE_RemoveCedricData(EERIE_3DOBJ * eobj) {
1408
1409 if(!eobj || !eobj->c_data) {
1410 return;
1411 }
1412
1413 for(long i = 0; i < eobj->c_data->nb_bones; i++) {
1414 free(eobj->c_data->bones[i].idxvertices), eobj->c_data->bones[i].idxvertices = NULL;
1415 }
1416
1417 delete[] eobj->c_data->bones, eobj->c_data->bones = NULL;
1418 delete eobj->c_data, eobj->c_data = NULL;
1419 delete[] eobj->vertexlocal, eobj->vertexlocal = NULL;
1420 }
1421
EERIE_CreateCedricData(EERIE_3DOBJ * eobj)1422 void EERIE_CreateCedricData(EERIE_3DOBJ * eobj) {
1423
1424 eobj->c_data = new EERIE_C_DATA();
1425 memset(eobj->c_data, 0, sizeof(EERIE_C_DATA));
1426
1427 if (eobj->nbgroups <= 0) // If no groups were specified
1428 {
1429 // Make one bone
1430 eobj->c_data->nb_bones = 1;
1431 eobj->c_data->bones = new EERIE_BONE[eobj->c_data->nb_bones];
1432 memset(eobj->c_data->bones, 0, sizeof(EERIE_BONE)*eobj->c_data->nb_bones);
1433
1434 // Add all vertices to the bone
1435 for (size_t i = 0; i < eobj->vertexlist.size(); i++)
1436 AddIdxToBone(&eobj->c_data->bones[0], i);
1437
1438 // Initialize the bone
1439 Quat_Init(&eobj->c_data->bones[0].quatinit);
1440 Quat_Init(&eobj->c_data->bones[0].quatanim);
1441 eobj->c_data->bones[0].scaleinit = Vec3f::ZERO;
1442 eobj->c_data->bones[0].scaleanim = Vec3f::ZERO;
1443 eobj->c_data->bones[0].transinit = Vec3f::ZERO;
1444 eobj->c_data->bones[0].transinit_global = eobj->c_data->bones[0].transinit;
1445 eobj->c_data->bones[0].original_group = NULL;
1446 eobj->c_data->bones[0].father = -1;
1447 }
1448 else // Groups were specified
1449 {
1450 // Alloc the bones
1451 eobj->c_data->nb_bones = eobj->nbgroups;
1452 eobj->c_data->bones = new EERIE_BONE[eobj->c_data->nb_bones];
1453 // TODO memset -> use constructor instead
1454 memset(eobj->c_data->bones, 0, sizeof(EERIE_BONE)*eobj->c_data->nb_bones);
1455
1456 bool * temp = new bool[eobj->vertexlist.size()];
1457 memset(temp, 0, eobj->vertexlist.size());
1458
1459 for (long i = eobj->nbgroups - 1; i >= 0; i--)
1460 {
1461 EERIE_VERTEX * v_origin = &eobj->vertexlist[eobj->grouplist[i].origin];
1462
1463 for (size_t j = 0; j < eobj->grouplist[i].indexes.size(); j++)
1464 {
1465 if (!temp[eobj->grouplist[i].indexes[j]])
1466 {
1467 temp[eobj->grouplist[i].indexes[j]] = true;
1468 AddIdxToBone(&eobj->c_data->bones[i], eobj->grouplist[i].indexes[j]);
1469 }
1470 }
1471
1472 Quat_Init(&eobj->c_data->bones[i].quatinit);
1473 Quat_Init(&eobj->c_data->bones[i].quatanim);
1474 eobj->c_data->bones[i].scaleinit = Vec3f::ZERO;
1475 eobj->c_data->bones[i].scaleanim = Vec3f::ZERO;
1476 eobj->c_data->bones[i].transinit = Vec3f(v_origin->v.x, v_origin->v.y, v_origin->v.z);
1477 eobj->c_data->bones[i].transinit_global = eobj->c_data->bones[i].transinit;
1478 eobj->c_data->bones[i].original_group = &eobj->grouplist[i];
1479 eobj->c_data->bones[i].father = GetFather(eobj, eobj->grouplist[i].origin, i - 1);
1480 }
1481
1482 delete[] temp;
1483
1484 // Try to correct lonely vertex
1485 for (size_t i = 0; i < eobj->vertexlist.size(); i++)
1486 {
1487 long ok = 0;
1488
1489 for (long j = 0; j < eobj->nbgroups; j++)
1490 {
1491 for (size_t k = 0; k < eobj->grouplist[j].indexes.size(); k++)
1492 {
1493 if ((size_t)eobj->grouplist[j].indexes[k] == i)
1494 {
1495 ok = 1;
1496 break;
1497 }
1498 }
1499
1500 if (ok)
1501 break;
1502 }
1503
1504 if (!ok)
1505 {
1506 AddIdxToBone(&eobj->c_data->bones[0], i);
1507 }
1508 }
1509
1510 for(long i = eobj->nbgroups - 1; i >= 0; i--) {
1511 if(eobj->c_data->bones[i].father >= 0) {
1512 long father = eobj->c_data->bones[i].father;
1513 eobj->c_data->bones[i].transinit -= eobj->c_data->bones[father].transinit;
1514 }
1515 eobj->c_data->bones[i].transinit_global = eobj->c_data->bones[i].transinit;
1516 }
1517
1518 }
1519
1520 /* Build proper mesh */
1521 {
1522 EERIE_C_DATA* obj = eobj->c_data;
1523
1524
1525 for (long i = 0; i != obj->nb_bones; i++)
1526 {
1527 EERIE_QUAT qt1;
1528
1529 if (obj->bones[i].father >= 0)
1530 {
1531 /* Rotation*/
1532 Quat_Copy(&qt1, &obj->bones[i].quatinit);
1533 Quat_Multiply(&obj->bones[i].quatanim, &obj->bones[obj->bones[i].father].quatanim, &qt1);
1534 /* Translation */
1535 TransformVertexQuat(&obj->bones[obj->bones[i].father].quatanim, &obj->bones[i].transinit, &obj->bones[i].transanim);
1536 obj->bones[i].transanim = obj->bones[obj->bones[i].father].transanim + obj->bones[i].transanim;
1537 }
1538 else
1539 {
1540 /* Rotation*/
1541 Quat_Copy(&obj->bones[i].quatanim, &obj->bones[i].quatinit);
1542 /* Translation */
1543 obj->bones[i].transanim = obj->bones[i].transinit;
1544 }
1545 obj->bones[i].scaleanim = Vec3f::ONE;
1546 }
1547
1548 eobj->vertexlocal = new EERIE_3DPAD[eobj->vertexlist.size()];
1549 // TODO constructor is better than memset
1550 memset(eobj->vertexlocal, 0, sizeof(EERIE_3DPAD)*eobj->vertexlist.size());
1551
1552 for (long i = 0; i != obj->nb_bones; i++) {
1553 Vec3f vector = obj->bones[i].transanim;
1554
1555 for (int v = 0; v != obj->bones[i].nb_idxvertices; v++) {
1556
1557 long idx = obj->bones[i].idxvertices[v];
1558 const EERIE_VERTEX & inVert = eobj->vertexlist[idx];
1559 EERIE_3DPAD & outVert = eobj->vertexlocal[idx];
1560
1561 Vec3f temp = inVert.v - vector;
1562 TransformInverseVertexQuat(&obj->bones[i].quatanim, &temp, &temp);
1563 outVert.x = temp.x, outVert.y = temp.y, outVert.z = temp.z;
1564 }
1565 }
1566 }
1567 }
1568
1569 #ifdef BUILD_EDIT_LOADSAVE
1570
1571 // Converts a Theo Object to an EERIE object
TheoToEerie(const char * adr,long size,const res::path & texpath,const res::path & fic)1572 static EERIE_3DOBJ * TheoToEerie(const char * adr, long size, const res::path & texpath, const res::path & fic) {
1573
1574 LogWarning << "TheoToEerie " << fic;
1575
1576 if(!adr) {
1577 return NULL;
1578 }
1579
1580 res::path txpath = texpath.empty() ? "graph/obj3d/textures" : texpath;
1581
1582 if(size < 10) {
1583 return NULL;
1584 }
1585
1586 size_t pos = 0;
1587
1588 const THEO_HEADER * pth = reinterpret_cast<const THEO_HEADER *>(adr + pos);
1589 pos += sizeof(THEO_HEADER);
1590
1591 if (pth->version < 3003 || pth->version > 3011) {
1592 LogError << "TheoToEerie: invalid version in " << fic << ": found " << pth->version
1593 << " expected 3004 to 3011";
1594 return NULL;
1595 }
1596
1597 EERIE_3DOBJ * eerie = new EERIE_3DOBJ;
1598 eerie->clear();
1599
1600 eerie->file = fic;
1601
1602 if(pth->type_write == 0) {
1603 // read the texture
1604
1605 LogError << "WARNING object " << fic << " SAVE MAP IN OBJECT = INVALID... Using Dummy Textures...";
1606
1607 eerie->texturecontainer.resize(pth->nb_maps);
1608 for(long i = 0; i < pth->nb_maps; i++) {
1609 pos += sizeof(THEO_TEXTURE);
1610 eerie->texturecontainer[i] = GetAnyTexture();
1611 }
1612
1613 } else {
1614
1615 if((pth->type_write & SAVE_MAP_BMP) || (pth->type_write & SAVE_MAP_TGA)) {
1616
1617 eerie->texturecontainer.resize(pth->nb_maps);
1618 for(long i = 0; i < pth->nb_maps; i++) {
1619
1620 res::path name;
1621 if(pth->version >= 3008) {
1622 const THEO_SAVE_MAPS_IN_3019 * tsmi3019 = reinterpret_cast<const THEO_SAVE_MAPS_IN_3019 *>(adr + pos);
1623 pos += sizeof(THEO_SAVE_MAPS_IN_3019);
1624 name = res::path::load(util::loadString(tsmi3019->texture_name)).remove_ext();
1625 } else {
1626 const THEO_SAVE_MAPS_IN * tsmi = reinterpret_cast<const THEO_SAVE_MAPS_IN *>(adr + pos);
1627 pos += sizeof(THEO_SAVE_MAPS_IN);
1628 name = res::path::load(util::loadString(tsmi->texture_name)).remove_ext();
1629 }
1630
1631 if(!name.empty()) {
1632 eerie->texturecontainer[i] = TextureContainer::Load(txpath / name, TextureContainer::Level);
1633 }
1634 }
1635 }
1636 }
1637
1638 pos = pth->object_seek;
1639 loadObjectData(eerie, adr, &pos, pth->version);
1640 eerie->angle = Anglef::ZERO;
1641 eerie->pos = Vec3f::ZERO;
1642
1643 // NORMALS CALCULATIONS
1644 Vec3f nrml;
1645 Vec3f nrrr;
1646 float count;
1647 long j, j2;
1648
1649 //Compute Faces Areas
1650 for(size_t i = 0; i < eerie->facelist.size(); i++) {
1651 const Vec3f & p0 = eerie->vertexlist[eerie->facelist[i].vid[0]].v;
1652 const Vec3f & p1 = eerie->vertexlist[eerie->facelist[i].vid[1]].v;
1653 const Vec3f & p2 = eerie->vertexlist[eerie->facelist[i].vid[2]].v;
1654 eerie->facelist[i].temp = dist((p0 + p1) * .5f, p2) * dist(p0, p1) * .5f;
1655 }
1656
1657 for (size_t i = 0; i < eerie->facelist.size(); i++)
1658 {
1659 CalcObjFaceNormal(
1660 &eerie->vertexlist[eerie->facelist[i].vid[0]].v,
1661 &eerie->vertexlist[eerie->facelist[i].vid[1]].v,
1662 &eerie->vertexlist[eerie->facelist[i].vid[2]].v,
1663 &eerie->facelist[i]
1664 );
1665 float area = eerie->facelist[i].temp;
1666
1667 for (j = 0; j < 3; j++)
1668 {
1669 float mod = area * area;
1670 nrrr = nrml = eerie->facelist[i].norm * mod;
1671 count = mod;
1672
1673 for (size_t i2 = 0; i2 < eerie->facelist.size(); i2++)
1674 {
1675 if (i != i2)
1676 {
1677 float area2 = eerie->facelist[i].temp;
1678
1679 for (j2 = 0; j2 < 3; j2++)
1680 {
1681 if (closerThan(eerie->vertexlist[eerie->facelist[i2].vid[j2]].v, eerie->vertexlist[eerie->facelist[i].vid[j]].v, .1f))
1682 {
1683 mod = (area2 * area2);
1684 nrml += eerie->facelist[i2].norm * mod;
1685 count += mod;
1686 }
1687 }
1688 }
1689 }
1690
1691 count = 1.f / count;
1692 eerie->vertexlist[eerie->facelist[i].vid[j]].vert.p = nrml * count;
1693 }
1694 }
1695
1696 for(size_t i = 0; i < eerie->facelist.size(); i++) {
1697 for(j = 0; j < 3; j++) {
1698 eerie->vertexlist[eerie->facelist[i].vid[j]].norm = eerie->vertexlist[eerie->facelist[i].vid[j]].vert.p;
1699 }
1700 }
1701
1702 // Apply Normals Spherical correction for NPC head
1703 long neck_orgn = GetGroupOriginByName(eerie, "neck");
1704 long head_idx = EERIE_OBJECT_GetGroup(eerie, "head");
1705
1706 if ((head_idx >= 0) && (neck_orgn >= 0))
1707 {
1708 Vec3f center = Vec3f::ZERO;
1709 Vec3f origin = eerie->vertexlist[neck_orgn].v;
1710 float count = (float)eerie->grouplist[head_idx].indexes.size();
1711
1712 if (count > 0.f)
1713 {
1714 for(size_t idx = 0 ; idx < eerie->grouplist[head_idx].indexes.size(); idx++) {
1715 center += eerie->vertexlist[ eerie->grouplist[head_idx].indexes[idx] ].v;
1716 }
1717
1718 center = (center * (1.f / count) + origin + origin) * (1.0f / 3);
1719 float max_threshold = dist(origin, center);
1720
1721 for (size_t i = 0; i < eerie->grouplist[head_idx].indexes.size(); i++)
1722 {
1723 EERIE_VERTEX * ev = &eerie->vertexlist[eerie->grouplist[head_idx].indexes[i]];
1724 float d = dist(ev->v, origin);
1725 float factor = 1.f;
1726
1727 if (d < max_threshold)
1728 {
1729 factor = d / max_threshold;
1730 }
1731
1732 float ifactor = 1.f - factor;
1733 Vec3f fakenorm;
1734 fakenorm = ev->v - center;
1735 fakenorm.normalize();
1736 ev->norm = ev->norm * ifactor + fakenorm * factor;
1737 ev->norm.normalize();
1738 }
1739 }
1740 }
1741
1742 // NORMALS CALCULATIONS END
1743 //***********************************************************
1744
1745 EERIE_LINKEDOBJ_InitData(eerie);
1746 eerie->c_data = NULL;
1747 EERIE_CreateCedricData(eerie);
1748 return eerie;
1749 }
1750
GetExistingEerie(const res::path & file)1751 static EERIE_3DOBJ * GetExistingEerie(const res::path & file) {
1752
1753 for(size_t i = 1; i < entities.size(); i++) {
1754 if(entities[i] != NULL && !entities[i]->tweaky && entities[i]->obj) {
1755 EERIE_3DOBJ * obj = entities[i]->obj;
1756 if(!obj->originaltextures && entities[i]->obj->file == file) {
1757 return entities[i]->obj;
1758 }
1759 }
1760 }
1761
1762 return NULL;
1763 }
1764
1765 #endif
1766
TheoToEerie_Fast(const res::path & texpath,const res::path & file,bool pbox)1767 static EERIE_3DOBJ * TheoToEerie_Fast(const res::path & texpath, const res::path & file, bool pbox) {
1768
1769 EERIE_3DOBJ * ret = ARX_FTL_Load(file);
1770 if(ret) {
1771 if(pbox) {
1772 EERIE_PHYSICS_BOX_Create(ret);
1773 }
1774 return ret;
1775 }
1776
1777 #ifndef BUILD_EDIT_LOADSAVE
1778 ARX_UNUSED(texpath);
1779 #else
1780
1781 ret = GetExistingEerie(file);
1782 if(ret) {
1783 ret = Eerie_Copy(ret);
1784 }
1785
1786 if(!ret) {
1787
1788 size_t size = 0;
1789 char * adr = resources->readAlloc(file, size);
1790 if(!adr) {
1791 LogWarning << "Object not found: " << file;
1792 return NULL;
1793 }
1794
1795 ret = TheoToEerie(adr, size, texpath, file);
1796 if(!ret) {
1797 free(adr);
1798 return NULL;
1799 }
1800
1801 EERIE_OBJECT_CenterObjectCoordinates(ret);
1802 free(adr);
1803 }
1804
1805 CreateNeighbours(ret);
1806 EERIEOBJECT_AddClothesData(ret);
1807 KillNeighbours(ret);
1808
1809 if(ret->cdata) {
1810 EERIE_COLLISION_SPHERES_Create(ret); // Must be out of the Neighbours zone
1811 }
1812
1813 if(pbox) {
1814 EERIE_PHYSICS_BOX_Create(ret);
1815 }
1816
1817 ARX_FTL_Save(fs::paths.user / file.string(), ret);
1818
1819 #endif // BUILD_EDIT_LOADSAVE
1820
1821 return ret;
1822 }
1823
loadObject(const res::path & file,bool pbox)1824 EERIE_3DOBJ * loadObject(const res::path & file, bool pbox) {
1825 return TheoToEerie_Fast("graph/obj3d/textures", file, pbox);
1826 }
1827
LoadTheObj(const res::path & file,const res::path & texpath)1828 EERIE_3DOBJ * LoadTheObj(const res::path & file, const res::path & texpath) {
1829 return TheoToEerie_Fast(file.parent() / texpath, file, true);
1830 }
1831
1832 // TODO why is this in EERIEobject
1833 ACTIONSTRUCT actions[MAX_ACTIONS];
RemoveAllBackgroundActions()1834 void RemoveAllBackgroundActions()
1835 {
1836 memset(actions, 0, sizeof(ACTIONSTRUCT)*MAX_ACTIONS);
1837
1838 for(size_t i = 0; i < MAX_ACTIONS; i++) actions[i].dl = -1;
1839 }
1840
EERIE_OBJECT_CenterObjectCoordinates(EERIE_3DOBJ * ret)1841 void EERIE_OBJECT_CenterObjectCoordinates(EERIE_3DOBJ * ret)
1842 {
1843 if (!ret) return;
1844
1845 Vec3f offset = ret->vertexlist[ret->origin].v;
1846
1847 if ((offset.x == 0) && (offset.y == 0) && (offset.z == 0))
1848 return;
1849
1850 LogWarning << "NOT CENTERED " << ret->file;
1851
1852 for(size_t i = 0; i < ret->vertexlist.size(); i++) {
1853 ret->vertexlist[i].v -= offset;
1854 ret->vertexlist[i].vert.p -= offset;
1855 ret->vertexlist3[i].v -= offset;
1856 ret->vertexlist3[i].vert.p -= offset;
1857 ret->vertexlist3[i].v -= offset;
1858 ret->vertexlist3[i].vert.p -= offset;
1859 }
1860
1861 ret->point0 -= offset;
1862 }
1863