1 /* ResidualVM - A 3D game interpreter
2 *
3 * ResidualVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "common/algorithm.h"
24 #include "common/endian.h"
25 #include "common/func.h"
26
27 #include "engines/grim/debug.h"
28 #include "engines/grim/grim.h"
29 #include "engines/grim/model.h"
30 #include "engines/grim/material.h"
31 #include "engines/grim/textsplit.h"
32 #include "engines/grim/gfx_base.h"
33 #include "engines/grim/resource.h"
34 #include "engines/grim/colormap.h"
35 #include "engines/grim/sprite.h"
36
37 namespace Grim {
38
39
40 /**
41 * @class Model
42 */
Model(const Common::String & filename,Common::SeekableReadStream * data,CMap * cmap,Model * parent)43 Model::Model(const Common::String &filename, Common::SeekableReadStream *data, CMap *cmap, Model *parent) :
44 Object(), _parent(parent), _numMaterials(0), _numGeosets(0), _cmap(cmap), _fname(filename) {
45
46 if (data->readUint32BE() == MKTAG('L','D','O','M'))
47 loadBinary(data);
48 else {
49 data->seek(0, SEEK_SET);
50 TextSplitter ts(_fname, data);
51 loadText(&ts);
52 }
53
54 Math::Vector3d max;
55
56 _rootHierNode->update();
57 bool first = true;
58 for (int i = 0; i < _numHierNodes; ++i) {
59 ModelNode &node = _rootHierNode[i];
60 if (node._mesh) {
61 g_driver->createMesh(node._mesh);
62 Mesh &mesh = *node._mesh;
63 Math::Vector3d p = mesh._matrix.getPosition();
64 float x = p.x();
65 float y = p.y();
66 float z = p.z();
67 for (int k = 0; k < mesh._numVertices * 3; k += 3) {
68 if (first || mesh._vertices[k] + x < _bboxPos.x())
69 _bboxPos.x() = mesh._vertices[k] + x;
70 if (first || mesh._vertices[k + 1] + y < _bboxPos.y())
71 _bboxPos.y() = mesh._vertices[k + 1] + y;
72 if (first || mesh._vertices[k + 2] + z < _bboxPos.z())
73 _bboxPos.z() = mesh._vertices[k + 2] + z;
74
75 if (first || mesh._vertices[k] + x > max.x())
76 max.x() = mesh._vertices[k] + x;
77 if (first || mesh._vertices[k + 1] + y > max.y())
78 max.y() = mesh._vertices[k + 1] + y;
79 if (first || mesh._vertices[k + 2] + z > max.z())
80 max.z() = mesh._vertices[k + 2] + z;
81
82 first = false;
83 }
84 }
85 }
86
87 _bboxSize = max - _bboxPos;
88 }
89
~Model()90 Model::~Model() {
91 for (int i = 0; i < _numMaterials; ++i) {
92 if (!_materialsShared[i]) {
93 delete _materials[i];
94 }
95 }
96 delete[] _materials;
97 delete[] _materialNames;
98 delete[] _materialsShared;
99 delete[] _geosets;
100 delete[] _rootHierNode;
101 g_resourceloader->uncacheModel(this);
102 }
103
loadBinary(Common::SeekableReadStream * data)104 void Model::loadBinary(Common::SeekableReadStream *data) {
105 _numMaterials = data->readUint32LE();
106 _materials = new Material*[_numMaterials];
107 _materialNames = new char[_numMaterials][32];
108 _materialsShared = new bool[_numMaterials];
109 for (int i = 0; i < _numMaterials; i++) {
110 data->read(_materialNames[i], 32);
111 _materialsShared[i] = false;
112 _materials[i] = nullptr;
113 loadMaterial(i, _cmap);
114 }
115 data->seek(32, SEEK_CUR); // skip name
116 data->seek(4, SEEK_CUR);
117 _numGeosets = data->readUint32LE();
118 _geosets = new Geoset[_numGeosets];
119 for (int i = 0; i < _numGeosets; i++)
120 _geosets[i].loadBinary(data, _materials);
121 data->seek(4, SEEK_CUR);
122 _numHierNodes = data->readUint32LE();
123 _rootHierNode = new ModelNode[_numHierNodes];
124 for (int i = 0; i < _numHierNodes; i++) {
125 _rootHierNode[i].loadBinary(data, _rootHierNode, &_geosets[0]);
126 }
127 _radius = data->readFloatLE();
128 data->seek(36, SEEK_CUR);
129 _insertOffset.readFromStream(data);
130 }
131
loadText(TextSplitter * ts)132 void Model::loadText(TextSplitter *ts) {
133 ts->expectString("section: header");
134 int major, minor;
135 ts->scanString("3do %d.%d", 2, &major, &minor);
136 ts->expectString("section: modelresource");
137 ts->scanString("materials %d", 1, &_numMaterials);
138 _materials = new Material*[_numMaterials];
139 _materialNames = new char[_numMaterials][32];
140 _materialsShared = new bool[_numMaterials];
141 for (int i = 0; i < _numMaterials; i++) {
142 char materialName[32];
143 int num;
144 _materialsShared[i] = false;
145 _materials[i] = nullptr;
146
147 ts->scanString("%d: %32s", 2, &num, materialName);
148 strcpy(_materialNames[num], materialName);
149 loadMaterial(num, _cmap);
150 }
151
152 ts->expectString("section: geometrydef");
153 ts->scanString("radius %f", 1, &_radius);
154 ts->scanString("insert offset %f %f %f", 3, &_insertOffset.x(), &_insertOffset.y(), &_insertOffset.z());
155 ts->scanString("geosets %d", 1, &_numGeosets);
156 _geosets = new Geoset[_numGeosets];
157 for (int i = 0; i < _numGeosets; i++) {
158 int num;
159 ts->scanString("geoset %d", 1, &num);
160 _geosets[num].loadText(ts, _materials);
161 }
162
163 ts->expectString("section: hierarchydef");
164 ts->scanString("hierarchy nodes %d", 1, &_numHierNodes);
165 _rootHierNode = new ModelNode[_numHierNodes];
166 for (int i = 0; i < _numHierNodes; i++) {
167 int num, mesh, parent, child, sibling, numChildren;
168 unsigned int flags, type;
169 float x, y, z, pitch, yaw, roll, pivotx, pivoty, pivotz;
170 char name[64];
171 ts->scanString(" %d: %x %x %d %d %d %d %d %f %f %f %f %f %f %f %f %f %64s",
172 18, &num, &flags, &type, &mesh, &parent, &child, &sibling,
173 &numChildren, &x, &y, &z, &pitch, &yaw, &roll, &pivotx, &pivoty, &pivotz, name);
174 _rootHierNode[num]._flags = (int)flags;
175 _rootHierNode[num]._type = (int)type;
176 if (mesh < 0)
177 _rootHierNode[num]._mesh = nullptr;
178 else
179 _rootHierNode[num]._mesh = &_geosets[0]._meshes[mesh];
180 if (parent >= 0) {
181 _rootHierNode[num]._parent = &_rootHierNode[parent];
182 _rootHierNode[num]._depth = _rootHierNode[parent]._depth + 1;
183 } else {
184 _rootHierNode[num]._parent = nullptr;
185 _rootHierNode[num]._depth = 0;
186 }
187 if (child >= 0)
188 _rootHierNode[num]._child = &_rootHierNode[child];
189 else
190 _rootHierNode[num]._child = nullptr;
191 if (sibling >= 0)
192 _rootHierNode[num]._sibling = &_rootHierNode[sibling];
193 else
194 _rootHierNode[num]._sibling = nullptr;
195
196 _rootHierNode[num]._numChildren = numChildren;
197 _rootHierNode[num]._pos = Math::Vector3d(x, y, z);
198 _rootHierNode[num]._rot = Math::Quaternion::fromEuler(yaw, pitch, roll, Math::EO_ZXY);
199 _rootHierNode[num]._animRot = _rootHierNode[num]._rot;
200 _rootHierNode[num]._animPos = _rootHierNode[num]._pos;
201 _rootHierNode[num]._pivot = Math::Vector3d(pivotx, pivoty, pivotz);
202 _rootHierNode[num]._meshVisible = true;
203 _rootHierNode[num]._hierVisible = true;
204 _rootHierNode[num]._sprite = nullptr;
205 _rootHierNode[num]._initialized = true;
206 }
207
208 if (!ts->isEof())
209 Debug::warning(Debug::Models, "Unexpected junk at end of model text");
210 }
211
draw() const212 void Model::draw() const {
213 _rootHierNode->draw();
214 }
215
getHierarchy() const216 ModelNode *Model::getHierarchy() const {
217 return _rootHierNode;
218 }
219
reload(CMap * cmap)220 void Model::reload(CMap *cmap) {
221 // Load the new colormap
222 for (int i = 0; i < _numMaterials; i++) {
223 loadMaterial(i, cmap);
224 }
225 for (int i = 0; i < _numGeosets; i++)
226 _geosets[i].changeMaterials(_materials);
227 _cmap = cmap;
228 }
229
loadMaterial(int index,CMap * cmap)230 void Model::loadMaterial(int index, CMap *cmap) {
231 Material *mat = nullptr;
232 if (!_materialsShared[index]) {
233 mat = _materials[index];
234 }
235 _materials[index] = nullptr;
236 if (_parent) {
237 _materials[index] = _parent->findMaterial(_materialNames[index], cmap);
238 if (_materials[index]) {
239 _materialsShared[index] = true;
240 }
241 }
242 if (!_materials[index]) {
243 if (mat && cmap->getFilename() == _cmap->getFilename()) {
244 _materials[index] = mat;
245 } else {
246 _materials[index] = g_resourceloader->loadMaterial(_materialNames[index], cmap, false);
247 }
248 _materialsShared[index] = false;
249 }
250 if (mat != _materials[index]) {
251 delete mat;
252 }
253 }
254
findMaterial(const char * name,CMap * cmap) const255 Material *Model::findMaterial(const char *name, CMap *cmap) const {
256 for (int i = 0; i < _numMaterials; ++i) {
257 if (scumm_stricmp(name, _materialNames[i]) == 0) {
258 if (cmap->getFilename() != _cmap->getFilename())
259 _materials[i]->reload(cmap);
260 return _materials[i];
261 }
262 }
263
264 return nullptr;
265 }
266
267 /**
268 * @class Model::Geoset
269 */
~Geoset()270 Model::Geoset::~Geoset() {
271 delete[] _meshes;
272 }
273
loadBinary(Common::SeekableReadStream * data,Material * materials[])274 void Model::Geoset::loadBinary(Common::SeekableReadStream *data, Material *materials[]) {
275 _numMeshes = data->readUint32LE();
276 _meshes = new Mesh[_numMeshes];
277 for (int i = 0; i < _numMeshes; i++)
278 _meshes[i].loadBinary(data, materials);
279 }
280
loadText(TextSplitter * ts,Material * materials[])281 void Model::Geoset::loadText(TextSplitter *ts, Material *materials[]) {
282 ts->scanString("meshes %d", 1, &_numMeshes);
283 _meshes = new Mesh[_numMeshes];
284 for (int i = 0; i < _numMeshes; i++) {
285 int num;
286 ts->scanString("mesh %d", 1, &num);
287 _meshes[num].loadText(ts, materials);
288 }
289 }
290
changeMaterials(Material * materials[])291 void Model::Geoset::changeMaterials(Material *materials[]) {
292 for (int i = 0; i < _numMeshes; i++)
293 _meshes[i].changeMaterials(materials);
294 }
295
296 /**
297 * @class MeshFace
298 */
MeshFace()299 MeshFace::MeshFace() :
300 _material(nullptr), _type(0), _geo(0), _light(0), _tex(0),
301 _extraLight(0), _numVertices(0), _vertices(nullptr),
302 _texVertices(nullptr), _userData(nullptr) {
303 }
304
~MeshFace()305 MeshFace::~MeshFace() {
306 delete[] _vertices;
307 delete[] _texVertices;
308 }
309
stealData(MeshFace & other)310 void MeshFace::stealData(MeshFace &other) {
311 *this = other;
312 other._vertices = nullptr;
313 other._texVertices = nullptr;
314 }
315
loadBinary(Common::SeekableReadStream * data,Material * materials[])316 int MeshFace::loadBinary(Common::SeekableReadStream *data, Material *materials[]) {
317 data->seek(4, SEEK_CUR);
318 _type = data->readUint32LE();
319 _geo = data->readUint32LE();
320 _light = data->readUint32LE();
321 _tex = data->readUint32LE();
322 _numVertices = data->readUint32LE();
323 data->seek(4, SEEK_CUR);
324 int texPtr = data->readUint32LE();
325 int materialPtr = data->readUint32LE();
326 data->seek(12, SEEK_CUR);
327 _extraLight = data->readFloatLE();
328 data->seek(12, SEEK_CUR);
329 _normal.readFromStream(data);
330
331 _vertices = new int[_numVertices];
332 for (int i = 0; i < _numVertices; i++) {
333 _vertices[i] = data->readUint32LE();
334 }
335
336 if (texPtr != 0) {
337 _texVertices = new int[_numVertices];
338 for (int i = 0; i < _numVertices; i++) {
339 _texVertices[i] = data->readUint32LE();
340 }
341 }
342
343 if (materialPtr != 0) {
344 materialPtr = data->readUint32LE();
345 _material = materials[materialPtr];
346 }
347
348 return materialPtr;
349 }
350
loadText(TextSplitter * ts,Material * materials[],int offset)351 int MeshFace::loadText(TextSplitter *ts, Material *materials[], int offset) {
352 int readlen, materialid;
353
354 if (ts->isEof())
355 error("Expected face data, got EOF");
356
357 ts->scanStringAtOffsetNoNewLine(offset, "%d %x %d %d %d %f %d%n", 7, &materialid, &_type, &_geo, &_light, &_tex, &_extraLight, &_numVertices, &readlen);
358 readlen += offset;
359
360 assert(materialid != -1);
361 _material = materials[materialid];
362 _vertices = new int[_numVertices];
363 _texVertices = new int[_numVertices];
364 for (int i = 0; i < _numVertices; ++i) {
365 int readlen2;
366
367 ts->scanStringAtOffsetNoNewLine(readlen, " %d, %d%n", 2, &_vertices[i], &_texVertices[i], &readlen2);
368 readlen += readlen2;
369 }
370 ts->nextLine();
371
372 return materialid;
373 }
374
changeMaterial(Material * material)375 void MeshFace::changeMaterial(Material *material) {
376 _material = material;
377 }
378
draw(const Mesh * mesh) const379 void MeshFace::draw(const Mesh *mesh) const {
380 if (_light == 0 && !g_driver->isShadowModeActive())
381 g_driver->disableLights();
382
383 _material->select();
384 g_driver->drawModelFace(mesh, this);
385
386 if (_light == 0 && !g_driver->isShadowModeActive())
387 g_driver->enableLights();
388 }
389
390 /**
391 * @class Mesh
392 */
Mesh()393 Mesh::Mesh() :
394 _numFaces(0), _radius(0.0f), _shadow(0), _geometryMode(0),
395 _lightingMode(0), _textureMode(0), _numVertices(0), _materialid(nullptr),
396 _vertices(nullptr), _verticesI(nullptr), _vertNormals(nullptr),
397 _numTextureVerts(0), _textureVerts(nullptr), _faces(nullptr), _userData(nullptr) {
398 _name[0] = '\0';
399
400 }
401
402
~Mesh()403 Mesh::~Mesh() {
404 g_driver->destroyMesh(this);
405
406 delete[] _vertices;
407 delete[] _verticesI;
408 delete[] _vertNormals;
409 delete[] _textureVerts;
410 delete[] _faces;
411 delete[] _materialid;
412 }
413
loadBinary(Common::SeekableReadStream * data,Material * materials[])414 void Mesh::loadBinary(Common::SeekableReadStream *data, Material *materials[]) {
415 data->read(_name, 32);
416 data->seek(4, SEEK_CUR);
417 _geometryMode = data->readUint32LE();
418 _lightingMode = data->readUint32LE();
419 _textureMode = data->readUint32LE();
420 _numVertices = data->readUint32LE();
421 _numTextureVerts = data->readUint32LE();
422 _numFaces = data->readUint32LE();
423 _vertices = new float[3 * _numVertices];
424 _verticesI = new float[_numVertices];
425 _vertNormals = new float[3 * _numVertices];
426 _textureVerts = new float[2 * _numTextureVerts];
427 _faces = new MeshFace[_numFaces];
428 _materialid = new int[_numFaces];
429 for (int i = 0; i < 3 * _numVertices; i++) {
430 _vertices[i] = data->readFloatLE();
431 }
432 for (int i = 0; i < 2 * _numTextureVerts; i++) {
433 _textureVerts[i] = data->readFloatLE();
434 }
435 for (int i = 0; i < _numVertices; i++) {
436 _verticesI[i] = data->readFloatLE();
437 }
438 data->seek(_numVertices * 4, SEEK_CUR);
439 for (int i = 0; i < _numFaces; i++)
440 _materialid[i] = _faces[i].loadBinary(data, materials);
441 for (int i = 0; i < 3 * _numVertices; i++) {
442 _vertNormals[i] = data->readFloatLE();
443 }
444 _shadow = data->readUint32LE();
445 data->seek(4, SEEK_CUR);
446 _radius = data->readFloatLE();
447 data->seek(24, SEEK_CUR);
448 sortFaces();
449 }
450
loadText(TextSplitter * ts,Material * materials[])451 void Mesh::loadText(TextSplitter *ts, Material *materials[]) {
452 ts->scanString("name %32s", 1, _name);
453 ts->scanString("radius %f", 1, &_radius);
454
455 // In data001/rope_scale.3do, the shadow line is missing
456 if (sscanf(ts->getCurrentLine(), "shadow %d", &_shadow) < 1) {
457 _shadow = 0;
458 } else
459 ts->nextLine();
460 ts->scanString("geometrymode %d", 1, &_geometryMode);
461 ts->scanString("lightingmode %d", 1, &_lightingMode);
462 ts->scanString("texturemode %d", 1, &_textureMode);
463 ts->scanString("vertices %d", 1, &_numVertices);
464 _vertices = new float[3 * _numVertices];
465 _verticesI = new float[_numVertices];
466 _vertNormals = new float[3 * _numVertices];
467
468 for (int i = 0; i < _numVertices; i++) {
469 int num;
470 float x, y, z, ival;
471 ts->scanString(" %d: %f %f %f %f", 5, &num, &x, &y, &z, &ival);
472 _vertices[3 * num] = x;
473 _vertices[3 * num + 1] = y;
474 _vertices[3 * num + 2] = z;
475 _verticesI[num] = ival;
476 }
477
478 ts->scanString("texture vertices %d", 1, &_numTextureVerts);
479 _textureVerts = new float[2 * _numTextureVerts];
480
481 for (int i = 0; i < _numTextureVerts; i++) {
482 int num;
483 float x, y;
484 ts->scanString(" %d: %f %f", 3, &num, &x, &y);
485 _textureVerts[2 * num] = x;
486 _textureVerts[2 * num + 1] = y;
487 }
488
489 ts->expectString("vertex normals");
490 for (int i = 0; i < _numVertices; i++) {
491 int num;
492 float x, y, z;
493 ts->scanString(" %d: %f %f %f", 4, &num, &x, &y, &z);
494 _vertNormals[3 * num] = x;
495 _vertNormals[3 * num + 1] = y;
496 _vertNormals[3 * num + 2] = z;
497 }
498
499 ts->scanString("faces %d", 1, &_numFaces);
500 _faces = new MeshFace[_numFaces];
501 _materialid = new int[_numFaces];
502 for (int i = 0; i < _numFaces; i++) {
503 int num, readlen;
504 ts->scanStringNoNewLine(" %d:%n ", 1, &num, &readlen);
505 _materialid[num] = _faces[num].loadText(ts, materials, readlen);
506 }
507
508 ts->expectString("face normals");
509 for (int i = 0; i < _numFaces; i++) {
510 int num;
511 float x, y, z;
512 ts->scanString(" %d: %f %f %f", 4, &num, &x, &y, &z);
513 _faces[num].setNormal(Math::Vector3d(x, y, z));
514 }
515 sortFaces();
516 }
517
sortFaces()518 void Mesh::sortFaces() {
519 if (_numFaces < 2)
520 return;
521
522 MeshFace *newFaces = new MeshFace[_numFaces];
523 int *newMaterialid = new int[_numFaces];
524 bool *copied = new bool[_numFaces];
525 for (int i = 0; i < _numFaces; ++i)
526 copied[i] = false;
527
528 for (int cur = 0, writeIdx = 0; cur < _numFaces; ++cur) {
529 if (copied[cur])
530 continue;
531
532 for (int other = cur; other < _numFaces; ++other) {
533 if (_faces[cur].getMaterial() == _faces[other].getMaterial() && !copied[other]) {
534 copied[other] = true;
535 newFaces[writeIdx].stealData(_faces[other]);
536 newMaterialid[writeIdx] = _materialid[other];
537 writeIdx++;
538 }
539 }
540 }
541
542 delete[] _faces;
543 _faces = newFaces;
544 delete[] _materialid;
545 _materialid = newMaterialid;
546 delete[] copied;
547 }
548
update()549 void Mesh::update() {
550 }
551
changeMaterials(Material * materials[])552 void Mesh::changeMaterials(Material *materials[]) {
553 for (int i = 0; i < _numFaces; i++)
554 _faces[i].changeMaterial(materials[_materialid[i]]);
555 }
556
draw() const557 void Mesh::draw() const {
558 if (_lightingMode == 0)
559 g_driver->disableLights();
560
561 g_driver->drawMesh(this);
562
563 if (_lightingMode == 0)
564 g_driver->enableLights();
565 }
566
getBoundingBox(int * x1,int * y1,int * x2,int * y2) const567 void Mesh::getBoundingBox(int *x1, int *y1, int *x2, int *y2) const {
568 int winX1, winY1, winX2, winY2;
569 g_driver->getScreenBoundingBox(this, &winX1, &winY1, &winX2, &winY2);
570 if (winX1 != -1 && winY1 != -1 && winX2 != -1 && winY2 != -1) {
571 *x1 = MIN(*x1, winX1);
572 *y1 = MIN(*y1, winY1);
573 *x2 = MAX(*x2, winX2);
574 *y2 = MAX(*y2, winY2);
575 }
576 }
577
578 /**
579 * @class ModelNode
580 */
ModelNode()581 ModelNode::ModelNode() :
582 _initialized(false), _needsUpdate(true), _mesh(nullptr), _flags(0), _type(0),
583 _depth(0), _numChildren(0), _parent(nullptr), _child(nullptr), _sprite(nullptr),
584 _sibling(nullptr), _meshVisible(false), _hierVisible(false) {
585 _name[0] = '\0';
586 }
587
~ModelNode()588 ModelNode::~ModelNode() {
589 ModelNode *child = _child;
590 while (child) {
591 child->_parent = nullptr;
592 child = child->_sibling;
593 }
594 }
595
loadBinary(Common::SeekableReadStream * data,ModelNode * hierNodes,const Model::Geoset * g)596 void ModelNode::loadBinary(Common::SeekableReadStream *data, ModelNode *hierNodes, const Model::Geoset *g) {
597 data->read(_name, 64);
598 _flags = data->readUint32LE();
599 data->seek(4, SEEK_CUR);
600 _type = data->readUint32LE();
601 int meshNum = data->readUint32LE();
602 if (meshNum < 0)
603 _mesh = nullptr;
604 else
605 _mesh = g->_meshes + meshNum;
606 _depth = data->readUint32LE();
607 int parentPtr = data->readUint32LE();
608 _numChildren = data->readUint32LE();
609 int childPtr = data->readUint32LE();
610 int siblingPtr = data->readUint32LE();
611 _pivot.readFromStream(data);
612 _pos.readFromStream(data);
613 float pitch = data->readFloatLE();
614 float yaw = data->readFloatLE();
615 float roll = data->readFloatLE();
616 _rot = Math::Quaternion::fromEuler(yaw, pitch, roll, Math::EO_ZXY);
617 _animRot = _rot;
618 _animPos = _pos;
619 _sprite = nullptr;
620
621 data->seek(48, SEEK_CUR);
622
623 if (parentPtr != 0)
624 _parent = hierNodes + data->readUint32LE();
625 else
626 _parent = nullptr;
627
628 if (childPtr != 0)
629 _child = hierNodes + data->readUint32LE();
630 else
631 _child = nullptr;
632
633 if (siblingPtr != 0)
634 _sibling = hierNodes + data->readUint32LE();
635 else
636 _sibling = nullptr;
637
638 _meshVisible = true;
639 _hierVisible = true;
640 _initialized = true;
641 }
642
draw() const643 void ModelNode::draw() const {
644 if (_sibling || _child) {
645 translateViewpointStart();
646 }
647 translateViewpoint();
648 if (_hierVisible) {
649 if (_child) {
650 translateViewpointStart();
651 }
652 g_driver->translateViewpoint(_pivot);
653
654 if (!g_driver->isShadowModeActive()) {
655 Sprite *sprite = _sprite;
656 while (sprite) {
657 sprite->draw();
658 sprite = sprite->_next;
659 }
660 }
661
662 if (_mesh && _meshVisible) {
663 _mesh->draw();
664 }
665
666 if (_child) {
667 translateViewpointFinish();
668 _child->draw();
669 }
670 }
671
672 if (_sibling || _child) {
673 translateViewpointFinish();
674 }
675 if (_sibling) {
676 _sibling->draw();
677 }
678 }
679
getBoundingBox(int * x1,int * y1,int * x2,int * y2) const680 void ModelNode::getBoundingBox(int *x1, int *y1, int *x2, int *y2) const {
681 if (_sibling || _child) {
682 translateViewpointStart();
683 }
684 translateViewpoint();
685 if (_hierVisible) {
686 if (_child) {
687 translateViewpointStart();
688 }
689 g_driver->translateViewpoint(_pivot);
690
691 if (_mesh && _meshVisible) {
692 _mesh->getBoundingBox(x1, y1, x2, y2);
693 }
694
695 if (_child) {
696 translateViewpointFinish();
697 _child->getBoundingBox(x1, y1, x2, y2);
698 }
699 }
700
701 if (_sibling || _child) {
702 translateViewpointFinish();
703 }
704 if (_sibling) {
705 _sibling->getBoundingBox(x1, y1, x2, y2);
706 }
707 }
708
addChild(ModelNode * child)709 void ModelNode::addChild(ModelNode *child) {
710 ModelNode **childPos = &_child;
711 while (*childPos)
712 childPos = &(*childPos)->_sibling;
713 *childPos = child;
714 child->_parent = this;
715 }
716
removeChild(ModelNode * child)717 void ModelNode::removeChild(ModelNode *child) {
718 ModelNode **childPos = &_child;
719 while (*childPos && *childPos != child)
720 childPos = &(*childPos)->_sibling;
721 if (*childPos) {
722 *childPos = child->_sibling;
723 child->_parent = nullptr;
724 }
725 }
726
setMatrix(const Math::Matrix4 & matrix)727 void ModelNode::setMatrix(const Math::Matrix4 &matrix) {
728 _matrix = matrix;
729 if (_sibling)
730 _sibling->setMatrix(matrix);
731 }
732
update()733 void ModelNode::update() {
734 if (!_initialized)
735 return;
736
737 if (_hierVisible && _needsUpdate) {
738 _localMatrix = _animRot.toMatrix();
739 _localMatrix.setPosition(_animPos);
740
741 _matrix = _matrix * _localMatrix;
742
743 _pivotMatrix = _matrix;
744 _pivotMatrix.translate(_pivot);
745
746 if (_mesh) {
747 _mesh->_matrix = _pivotMatrix;
748 }
749
750 if (_child) {
751 _child->setMatrix(_matrix);
752 _child->update();
753 }
754
755 _needsUpdate = false;
756 }
757
758 if (_sibling) {
759 _sibling->update();
760 }
761 }
762
addSprite(Sprite * sprite)763 void ModelNode::addSprite(Sprite *sprite) {
764 sprite->_next = _sprite;
765 _sprite = sprite;
766 }
767
removeSprite(const Sprite * sprite)768 void ModelNode::removeSprite(const Sprite *sprite) {
769 Sprite *curr = _sprite;
770 Sprite *prev = nullptr;
771 while (curr) {
772 if (curr == sprite) {
773 if (prev)
774 prev->_next = curr->_next;
775 else
776 _sprite = curr->_next;
777 }
778 prev = curr;
779 curr = curr->_next;
780 }
781 }
782
translateViewpoint() const783 void ModelNode::translateViewpoint() const {
784 g_driver->translateViewpoint(_animPos);
785
786 Math::Matrix4 rot = _animRot.toMatrix();
787 rot.transpose();
788 g_driver->rotateViewpoint(rot);
789 }
790
translateViewpointStart() const791 void ModelNode::translateViewpointStart() const {
792 g_driver->translateViewpointStart();
793 }
794
translateViewpointFinish() const795 void ModelNode::translateViewpointFinish() const {
796 g_driver->translateViewpointFinish();
797 }
798
799 } // end of namespace Grim
800