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