xoreos  0.0.5
model_sonic.cpp
Go to the documentation of this file.
1 /* xoreos - A reimplementation of BioWare's Aurora engine
2  *
3  * xoreos is the legal property of its developers, whose names
4  * can be found in the AUTHORS file distributed with this source
5  * distribution.
6  *
7  * xoreos 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 3
10  * of the License, or (at your option) any later version.
11  *
12  * xoreos 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 xoreos. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
25 /* Based on several bits and pieces figured out by the Nintendo DS
26  * modding community. Most prominently:
27  * - The NSBMD reader found in the NDS file viewer and editor Tinke
28  * by pleoNeX (<https://github.com/pleonex/tinke>), which is
29  * under the terms of the GPLv3.
30  * - lowlines' NSBMD documentation
31  * (<http://llref.emutalk.net/docs/?file=xml/bmd0.xml#xml-doc>)
32  * - Output from lowlines' Console Tool
33  * (<http://llref.emutalk.net/projects/ctool/>)
34  * - The Nintendo DS technical information GBATEK by Martin Korth
35  * (<http://problemkaputt.de/gbatek.htm>)
36  *
37  * The original copyright note in Tinke reads as follows:
38  *
39  * Copyright (C) 2011 pleoNeX
40  *
41  * This program is free software: you can redistribute it and/or modify
42  * it under the terms of the GNU General Public License as published by
43  * the Free Software Foundation, either version 3 of the License, or
44  * (at your option) any later version.
45  *
46  * This program is distributed in the hope that it will be useful,
47  * but WITHOUT ANY WARRANTY; without even the implied warranty of
48  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
49  * GNU General Public License for more details.
50  *
51  * You should have received a copy of the GNU General Public License
52  * along with this program. If not, see <http://www.gnu.org/licenses/>.
53  */
54 
55 #include <cassert>
56 #include <cstring>
57 
58 #include "glm/gtc/type_ptr.hpp"
59 #include "glm/gtc/matrix_transform.hpp"
60 #include "glm/gtx/matrix_interpolation.hpp"
61 
62 #include "src/common/util.h"
63 #include "src/common/maths.h"
64 #include "src/common/strutil.h"
65 #include "src/common/readstream.h"
66 #include "src/common/error.h"
67 #include "src/common/encoding.h"
68 
69 #include "src/aurora/types.h"
70 #include "src/aurora/resman.h"
71 
75 
76 static const uint32 kBMD0ID = MKTAG('B', 'M', 'D', '0');
77 static const uint32 kMDL0ID = MKTAG('M', 'D', 'L', '0');
78 
79 namespace Graphics {
80 
81 namespace Aurora {
82 
84  Common::SeekableReadStream *stream = ResMan.getResource(name, ::Aurora::kFileTypeNSBMD);
85  if (!stream)
86  throw Common::Exception("No such NSBMD \"%s\"", name.c_str());
87 
89 }
90 
92  delete nsbmd;
93 
94  clear();
95 }
96 
98  for (std::list<ModelNode_Sonic *>::iterator n = nodes.begin(); n != nodes.end(); ++n)
99  delete *n;
100  nodes.clear();
101 
102  delete state;
103  state = 0;
104 }
105 
106 
108  _fileName = name;
109 
110  ParserContext ctx(name);
111 
112  load(ctx);
113 
114  finalize();
115 
116  // Model::createBound() doesn't know about the geometry in Model_Sonic
117  createBound();
118 }
119 
121 }
122 
123 // --- Loader ---
124 
126  // Read the model data from the file
127 
128  readHeader(ctx);
129  readModel(ctx);
130 
131  // Parse/process the model parts further
132 
133  parseBoneCommands(ctx);
134 
135  findRootBones(ctx);
136  findStackBones(ctx);
137 
138  // Create our run-time data
139 
140  createModelNodes(ctx);
141 
142  findStackMixes(ctx);
143 
144  createGeometry(ctx);
146 }
147 
149  readModelHeader(ctx);
150 
151  ctx.nsbmd->seek(ctx.offsetModel);
152 
153  // Read the model globals
154 
155  ctx.nsbmd->skip(4); // Section size
156 
157  ctx.offsetBoneCommands = ctx.nsbmd->readUint32() + ctx.offsetModel;
158  ctx.offsetMaterials = ctx.nsbmd->readUint32() + ctx.offsetModel;
159  ctx.offsetPolygons = ctx.nsbmd->readUint32() + ctx.offsetModel;
160 
161  ctx.nsbmd->skip(4); // End of the polygons section
162  ctx.nsbmd->skip(8); // Unknown
163 
164  ctx.defaultScale = readNintendoFixedPoint(ctx.nsbmd->readUint32(), true, 19, 12);
165 
166  ctx.nsbmd->skip( 4); // Bound box scaling
167  ctx.nsbmd->skip( 8); // Unknown
168  ctx.nsbmd->skip(12); // Bounding box. We calculate our own
169  ctx.nsbmd->skip( 8); // Unknown
170 
171  ctx.offsetBones = ctx.nsbmd->pos();
172 
173  // Read the model parts
174 
175  readBones(ctx);
176  readBoneCommands(ctx);
177 
178  readMaterials(ctx);
179 
180  readPolygons(ctx);
181 }
182 
183 // --- Loader utility methods ---
184 
186  /* Contains names and offsets of entries in a section. */
187 
188  ctx.nsbmd->skip(1); // Unknown
189 
190  const uint8 count = ctx.nsbmd->readByte();
191  infos.resize(count);
192 
193  ctx.nsbmd->skip(2); // Section size
194  ctx.nsbmd->skip(2 + 2 + 4 + count * (2 + 2)); // Unknown
195 
196  ctx.nsbmd->skip(2 + 2); // Header size + section size
197 
198  for (uint i = 0; i < count; i++) {
199  infos[i].offset = ctx.nsbmd->readUint32() + offset;
200  infos[i].count = 0;
201  }
202 
203  for (uint i = 0; i < count; i++)
204  infos[i].name = Common::readStringFixed(*ctx.nsbmd, Common::kEncodingASCII, 16).toLower();
205 
206  return count;
207 }
208 
210  /* Contains names, offsets and counts/sizes of entries in a section. */
211 
212  ctx.nsbmd->skip(1); // Unknown
213 
214  const uint8 count = ctx.nsbmd->readByte();
215  infos.resize(count);
216 
217  ctx.nsbmd->skip(2); // Section size
218  ctx.nsbmd->skip(2 + 2 + 4 + count * (2 + 2)); // Unknown
219 
220  ctx.nsbmd->skip(2 + 2); // Header size + section size
221 
222  for (uint i = 0; i < count; i++) {
223  infos[i].offset = ctx.nsbmd->readUint16() + offset;
224  infos[i].count = ctx.nsbmd->readByte();
225 
226  ctx.nsbmd->skip(1); // Padding
227  }
228 
229  for (uint i = 0; i < count; i++)
230  infos[i].name = Common::readStringFixed(*ctx.nsbmd, Common::kEncodingASCII, 16).toLower();
231 
232  return count;
233 }
234 
235 // --- Headers ---
236 
238  /* Global NSBMD header. */
239 
240  const uint32 tag = ctx.nsbmd->readUint32BE();
241  if (tag != kBMD0ID)
242  throw Common::Exception("Invalid NSBMD file (%s)", Common::debugTag(tag).c_str());
243 
244  const uint16 bom = ctx.nsbmd->readUint16();
245  if (bom != 0xFEFF)
246  throw Common::Exception("Invalid BOM: %u", bom);
247 
248  const uint8 versionMajor = ctx.nsbmd->readByte();
249  const uint8 versionMinor = ctx.nsbmd->readByte();
250  if ((versionMajor != 2) || (versionMinor != 0))
251  throw Common::Exception("Unsupported version %u.%u", versionMajor, versionMinor);
252 
253  const uint32 fileSize = ctx.nsbmd->readUint32();
254  if (fileSize > ctx.nsbmd->size())
255  throw Common::Exception("Size too large (%u > %u)", fileSize, (uint)ctx.nsbmd->size());
256 
257  const uint16 headerSize = ctx.nsbmd->readUint16();
258  if (headerSize != 16)
259  throw Common::Exception("Invalid header size (%u)", headerSize);
260 
261  const uint16 sectionCount = ctx.nsbmd->readUint16();
262  if (sectionCount != 1)
263  throw Common::Exception("Invalid number of sections (%u)", sectionCount);
264 
265  ctx.offsetMDL0 = ctx.nsbmd->readUint32();
266 }
267 
269  /* MDL0 section header. */
270 
271  ctx.nsbmd->seek(ctx.offsetMDL0);
272 
273  const uint32 tag = ctx.nsbmd->readUint32BE();
274  if (tag != kMDL0ID)
275  throw Common::Exception("Invalid model section (%s)", Common::debugTag(tag).c_str());
276 
277  ctx.nsbmd->skip(4); // Size
278 
279  Infos models;
280  const uint8 modelCount = readInfoOffset(ctx, models, ctx.offsetMDL0);
281 
282  if (modelCount != 1)
283  throw Common::Exception("Unsupported number of models (%u)", modelCount);
284 
285  _name = models[0].name;
286 
287  ctx.offsetModel = models[0].offset;
288 }
289 
290 // --- Bones ---
291 
293  ctx.nsbmd->seek(ctx.offsetBones);
294 
295  Infos bones;
296  const uint8 boneCount = readInfoOffset(ctx, bones, ctx.offsetBones);
297 
298  ctx.bones.resize(boneCount);
299  for (uint i = 0; i < boneCount; i++)
300  readBone(ctx, ctx.bones[i], bones[i]);
301 }
302 
304  ctx.nsbmd->seek(info.offset);
305 
306  bone.name = info.name;
307 
308  bone.transform = glm::mat4();
309 
310 
311  const uint16 flags = ctx.nsbmd->readUint16();
312 
313  const double rotate0 = readNintendoFixedPoint(ctx.nsbmd->readUint16(), true, 3, 12);
314 
315  const bool hasTranslate = (flags & 0x01) == 0;
316  const bool hasRotate = ((flags & 0x02) == 0) && !((flags & 0x08) != 0);
317  const bool hasScale = (flags & 0x04) == 0;
318  const bool hasPivot = (flags & 0x08) != 0;
319 
320  const uint8 pivotSelect = (flags >> 4) & 0xF;
321  const uint8 pivotNegate = (flags >> 8) & 0xF;
322 
323  // TRS: translate, rotate/pivot, scale
324 
325  if (hasTranslate) {
326  const float x = readNintendoFixedPoint(ctx.nsbmd->readUint32(), true, 19, 12);
327  const float y = readNintendoFixedPoint(ctx.nsbmd->readUint32(), true, 19, 12);
328  const float z = readNintendoFixedPoint(ctx.nsbmd->readUint32(), true, 19, 12);
329 
330  bone.transform = glm::translate(bone.transform, glm::vec3(x, y, z));
331  }
332 
333  if (hasPivot) {
334  const float pivotA = readNintendoFixedPoint(ctx.nsbmd->readUint16(), true, 3, 12);
335  const float pivotB = readNintendoFixedPoint(ctx.nsbmd->readUint16(), true, 3, 12);
336 
337  const glm::mat4 pivot = createPivot(pivotA, pivotB, pivotSelect, pivotNegate);
338 
339  bone.transform *= pivot;
340  }
341 
342  if (hasRotate) {
343  glm::mat4 rotateMatrix;
344 
345  rotateMatrix[0][0] = rotate0;
346  rotateMatrix[0][1] = readNintendoFixedPoint(ctx.nsbmd->readUint16(), true, 3, 12);
347  rotateMatrix[0][2] = readNintendoFixedPoint(ctx.nsbmd->readUint16(), true, 3, 12);
348 
349  rotateMatrix[1][0] = readNintendoFixedPoint(ctx.nsbmd->readUint16(), true, 3, 12);
350  rotateMatrix[1][1] = readNintendoFixedPoint(ctx.nsbmd->readUint16(), true, 3, 12);
351  rotateMatrix[1][2] = readNintendoFixedPoint(ctx.nsbmd->readUint16(), true, 3, 12);
352 
353  rotateMatrix[2][0] = readNintendoFixedPoint(ctx.nsbmd->readUint16(), true, 3, 12);
354  rotateMatrix[2][1] = readNintendoFixedPoint(ctx.nsbmd->readUint16(), true, 3, 12);
355  rotateMatrix[2][2] = readNintendoFixedPoint(ctx.nsbmd->readUint16(), true, 3, 12);
356 
357  bone.transform *= rotateMatrix;
358  }
359 
360  if (hasScale) {
361  const float x = readNintendoFixedPoint(ctx.nsbmd->readUint32(), true, 19, 12);
362  const float y = readNintendoFixedPoint(ctx.nsbmd->readUint32(), true, 19, 12);
363  const float z = readNintendoFixedPoint(ctx.nsbmd->readUint32(), true, 19, 12);
364 
365  bone.transform = glm::scale(bone.transform, glm::vec3(x, y, z));
366  }
367 }
368 
370  ctx.nsbmd->seek(ctx.offsetBoneCommands);
371 
372  BoneCommandID cmd;
373  while ((cmd = (BoneCommandID) ctx.nsbmd->readByte()) != kBoneEnd) {
374  ctx.boneCommands.push_back(BoneCommand(cmd));
375  BoneCommand &boneCommand = ctx.boneCommands.back();
376 
377  uint8 count = 0;
378  if (cmd == kBoneLoadStack) {
379  size_t pos = ctx.nsbmd->pos();
380 
381  ctx.nsbmd->skip(1);
382  count = ctx.nsbmd->readByte();
383 
384  ctx.nsbmd->seek(pos);
385  }
386 
387  const uint8 paramCount = getBoneParameterCount(cmd, count);
388 
389  boneCommand.parameters.resize(paramCount);
390  for (uint i = 0; i < paramCount; i++)
391  boneCommand.parameters[i] = ctx.nsbmd->readByte();
392  }
393 }
394 
395 // --- Materials ---
396 
398  ctx.nsbmd->seek(ctx.offsetMaterials);
399 
400  ctx.offsetTextures = ctx.nsbmd->readUint16() + ctx.offsetMaterials;
401  ctx.offsetPalettes = ctx.nsbmd->readUint16() + ctx.offsetMaterials;
402 
404 
405  // Attach the texture and palette names to the materials
406  // Note: we don't actually care about the palette name
407 
408  ctx.nsbmd->seek(ctx.offsetTextures);
409  readMaterialResource(ctx, 0);
410 
411  ctx.nsbmd->seek(ctx.offsetPalettes);
412  readMaterialResource(ctx, 1);
413 }
414 
416  Infos materials;
417  const uint8 materialCount = readInfoOffset(ctx, materials, ctx.offsetMaterials);
418 
419  ctx.materials.resize(materialCount);
420  for (uint i = 0; i < materialCount; i++)
421  readMaterialDefinition(ctx, ctx.materials[i], materials[i]);
422 }
423 
425  ctx.nsbmd->seek(info.offset);
426 
427  material.name = info.name;
428 
429  ctx.nsbmd->skip(2 + 2 + 18); // Unknown + section size + unknown
430 
431  const uint16 flags = ctx.nsbmd->readUint16();
432 
433  // TODO: Are the different from the ones inside the NSBTX?
434  // TODO: Actually use those parameters
435 
436  material.wrapX = (flags & 0x0001) != 0;
437  material.wrapY = (flags & 0x0002) != 0;
438  material.flipX = (flags & 0x0004) != 0;
439  material.flipY = (flags & 0x0008) != 0;
440 
441  const bool doubleX = (flags & 0x1000) != 0;
442  const bool doubleY = (flags & 0x4000) != 0;
443 
444  material.scaleX = 1 << (doubleX ? 1 : 0);
445  material.scaleY = 1 << (doubleY ? 1 : 0);
446 
447  ctx.nsbmd->skip(8); // Unknown
448 
449  material.width = ctx.nsbmd->readUint16();
450  material.height = ctx.nsbmd->readUint16();
451 }
452 
454  /* Each texture/palette has a list of materials it belongs to. */
455 
456  Infos resources;
457  const uint8 resCount = readInfoOffsetCount(ctx, resources, ctx.offsetMaterials);
458 
459  for (uint i = 0; i < resCount; i++) {
460  ctx.nsbmd->seek(resources[i].offset);
461 
462  for (uint j = 0; j < resources[i].count; j++) {
463  const uint8 materialID = ctx.nsbmd->readByte();
464  if (materialID >= ctx.materials.size())
465  continue;
466 
467  if (textureOrPalette == 0)
468  ctx.materials[materialID].texture = resources[i].name;
469  else if (textureOrPalette == 1)
470  ctx.materials[materialID].palette = resources[i].name;
471  }
472  }
473 }
474 
475 // --- Polygons ---
476 
478  ctx.nsbmd->seek(ctx.offsetPolygons);
479 
480  Infos polygons;
481  const uint8 polygonCount = readInfoOffset(ctx, polygons, ctx.offsetPolygons);
482 
483  ctx.polygons.resize(polygonCount);
484  for (uint i = 0; i < polygonCount; i++)
485  readPolygon(ctx, ctx.polygons[i], polygons[i]);
486 }
487 
489  ctx.nsbmd->seek(info.offset);
490 
491  polygon.name = info.name;
492 
493  ctx.nsbmd->skip(8); // Unknown
494 
495  const uint32 listOffset = ctx.nsbmd->readUint32() + info.offset;
496  const uint32 listSize = ctx.nsbmd->readUint32();
497 
498  ctx.nsbmd->seek(listOffset);
499 
500  readPolygonCommands(ctx, polygon, listSize);
501 }
502 
504  /* Read the commands for a polygon.
505  *
506  * For some reason (alignment?), the layout is as follows:
507  * - 4 command IDs (uint8 each)
508  * - parameters for each of those 4 command IDs (uint32 each)
509  * - 4 command IDs
510  * - [...]
511  *
512  * While we're reading the list, we also count the number of primitives
513  * that will be created and how long they'll be.
514  */
515 
516  uint8 buffer[4];
517 
518  uint32 primitiveSize = 0;
519 
520  polygon.commands.reserve(listSize / 4);
521  while (listSize >= 4) {
522  ctx.nsbmd->read(buffer, 4);
523  listSize -= 4;
524 
525  for (uint i = 0; i < 4; i++) {
526  polygon.commands.push_back(PolygonCommand((PolygonCommandID) buffer[i]));
527  PolygonCommand &cmd = polygon.commands.back();
528 
529  const uint8 paramCount = getPolygonParameterCount(cmd.command);
530  cmd.parameters.resize(paramCount);
531 
532  for (uint j = 0; (j < paramCount) && (listSize >= 4); j++, listSize -= 4)
533  cmd.parameters[j] = ctx.nsbmd->readUint32();
534 
535  if ((cmd.command >= kPolygonVertex16) && (cmd.command <= kPolygonVertexDiff))
536  primitiveSize++;
537 
538  if (cmd.command == kPolygonBeginVertices) {
539  polygon.primitiveCount++;
540 
541  polygon.primitiveSize = MAX(polygon.primitiveSize, primitiveSize);
542  primitiveSize = 0;
543  }
544  }
545  }
546 
547  polygon.primitiveSize = MAX(polygon.primitiveSize, primitiveSize);
548 }
549 
550 // --- Processing the model parts ---
551 
553  /* Go through and evaluate all bone commands. */
554 
555  uint16 polygon = 0xFFFF;
556  uint16 polygonStack = 0xFFFF;
557  uint16 material = 0xFFFF;
558 
559  uint16 nodeStack = 0;
560  uint16 parentStack = 0xFFFF;
561 
562  uint16 nodeID = 0xFFFF;
563  uint16 parentID = 0xFFFF;
564 
565  Bone *node = 0;
566  Bone *parent = 0;
567 
568 
569  for (BoneCommands::const_iterator c = ctx.boneCommands.begin(); c != ctx.boneCommands.end(); ++c) {
570  switch (c->command) {
571  case kBoneNOP:
572  case kBoneEnd:
573  case kBoneBeginPair:
574  case kBoneEndPair:
575  case kBoneUnknown1:
576  case kBoneUnknown2:
577  case kBoneUnknown3:
578  break;
579 
580  case kBoneSetInvisible:
581  ctx.boneInvisible[c->parameters[0]] = c->parameters[1] == 1;
582  break;
583 
585  polygonStack = c->parameters[0];
586  break;
587 
588  case kBoneLoadStack:
589  polygonStack = c->parameters[0];
590 
591  ctx.stackMix[polygonStack] = StackMixes();
592  ctx.stackMix[polygonStack].resize(c->parameters[1]);
593 
594  for (uint32 i = 0; i < c->parameters[1]; i++) {
595  ctx.stackMix[polygonStack][i].nodeID = c->parameters[2 + i * 3 + 0];
596  ctx.stackMix[polygonStack][i].nodeStack = c->parameters[2 + i * 3 + 1];
597  ctx.stackMix[polygonStack][i].ratio = c->parameters[2 + i * 3 + 2] / 256.0f;
598  }
599 
600  warning("TODO: Bone command LoadStack: %u (\"%s\")", c->parameters[0], _name.c_str());
601  break;
602 
603  case kBoneSetPolygon:
604  polygon = c->parameters[0];
605  if (polygon >= ctx.polygons.size())
606  break;
607 
608  if (material < ctx.materials.size())
609  ctx.polygons[polygon].material = &ctx.materials[material];
610 
611  ctx.polygons[polygon].defaultStack = polygonStack;
612  break;
613 
614  case kBoneSetMaterial1:
615  case kBoneSetMaterial2:
616  case kBoneSetMaterial3:
617  material = c->parameters[0];
618  break;
619 
620  case kBoneConnect1:
621  case kBoneConnect2:
622  case kBoneConnect3:
623  case kBoneConnect4:
624  nodeID = c->parameters[0];
625  parentID = c->parameters[1];
626 
627  node = (nodeID < ctx.bones.size()) ? &ctx.bones[nodeID] : 0;
628  parent = (parentID < ctx.bones.size()) ? &ctx.bones[parentID] : 0;
629 
630  if (!node) {
631  warning("No such node %u in bone command connect 0x%02X (\"%s\")",
632  c->parameters[0], c->parameters[1], _name.c_str());
633  break;
634  }
635 
636  if (node == parent)
637  parent = 0;
638 
639  parentStack = parent ? parent->nodeStack : 0xFFFF;
640 
641  switch (c->command) {
642  case kBoneConnect1:
643  nodeStack++;
644  break;
645 
646  case kBoneConnect2:
647  nodeStack = c->parameters[3];
648  break;
649 
650  case kBoneConnect3:
651  parentStack = c->parameters[3];
652  break;
653 
654  case kBoneConnect4:
655  nodeStack = c->parameters[3];
656  parentStack = c->parameters[4];
657  break;
658 
659  default:
660  break;
661  }
662 
663  polygonStack = nodeStack;
664 
665  // If this ever happens, the node doesn't inherit its matrix from its parent
666  if (parent && (parentStack != parent->nodeStack))
667  warning("Parent stack != node parent stack (%u:%u, %u:%u) (\"%s\")",
668  parentID, parent->nodeStack, nodeID, parentStack, _name.c_str());
669 
670  node->nodeID = nodeID;
671  node->parentID = parentID;
672  node->nodeStack = nodeStack;
673  node->parentStack = parentStack;
674  node->parent = parent;
675 
676  if (parent)
677  parent->children.push_back(node);
678 
679  break;
680 
681  default:
682  throw Common::Exception("Invalid bone command: 0x%02X", (uint) c->command);
683  }
684  }
685 }
686 
688  /* Collect all bones without a parent in our root bone list. */
689 
690  for (Bones::iterator j = ctx.bones.begin(); j != ctx.bones.end(); ++j)
691  if (!j->parent)
692  ctx.rootBones.push_back(&*j);
693 
694  // There *should* only be one
695  if (ctx.rootBones.size() != 1)
696  throw Common::Exception("Invalid number of root bones (%u)", (uint) ctx.rootBones.size());
697 }
698 
700  /* Collect the stack positions for each of our bone. */
701 
702  for (Bones::iterator j = ctx.bones.begin(); j != ctx.bones.end(); ++j) {
703  if (j->nodeStack == 0xFFFF)
704  continue;
705 
706  std::pair<StackBoneMap::iterator, bool> result;
707  result = ctx.stackBones.insert(std::make_pair(j->nodeStack, &*j));
708 
709  if (!result.second)
710  warning("Stack collision: %u for node %u and %u (\"%s\")", j->nodeStack,
711  result.first->second->nodeID, j->nodeID, _name.c_str());
712  }
713 }
714 
715 // --- Create our run-time data ---
716 
718  /* Recursively add the model nodes according to how the bones connect. */
719 
720  newState(ctx);
721 
722  ModelNode_Sonic *rootNode = new ModelNode_Sonic(*this);
723  ctx.nodes.push_back(rootNode);
724 
725  rootNode->load(ctx, *ctx.rootBones.front());
726  rootNode->createAbsoluteBound();
727 
728  addState(ctx);
729 
730  // Evaluate the map of invisible bones
731  for (BoneInvisible::const_iterator v = ctx.boneInvisible.begin(); v != ctx.boneInvisible.end(); ++v) {
732  StackBoneMap::iterator b = ctx.stackBones.find(v->first);
733  if (b != ctx.stackBones.end())
734  b->second->modelNode->setInvisible(v->second);
735  }
736 }
737 
739  /* Go through all the stack mixes and fill in the nodes corresponding to the stack positions. */
740 
741  for (StackMixMap::iterator s = ctx.stackMix.begin(); s != ctx.stackMix.end(); ++s) {
742  for (StackMixes::iterator m = s->second.begin(); m != s->second.end(); ++m) {
743  StackBoneMap::iterator b = ctx.stackBones.find(m->nodeStack);
744  if (b == ctx.stackBones.end())
745  continue;
746 
747  m->node = b->second->modelNode;
748  }
749  }
750 }
751 
753  _geometries.resize(ctx.polygons.size());
754  for (size_t i = 0; i < ctx.polygons.size(); i++) {
755  Polygon &polygon = ctx.polygons[i];
756  Geometry &geometry = _geometries[i];
757 
758  // Load texture
759  try {
760  if (polygon.material && !polygon.material->texture.empty())
761  geometry.texture = TextureMan.get(polygon.material->texture);
762  } catch (...) {
764  }
765 
766  // Create the primitives and their vertices
767  createPrimitives(ctx, geometry, polygon);
768 
769  // And fill in the indices
770  for (Primitives::iterator p = geometry.primitives.begin(); p != geometry.primitives.end(); ++p)
771  createIndices(*p);
772  }
773 }
774 
776  /* Go through all polygon commands in this polygon and evaluate them, creating
777  * an intermediate Primitive for each vertex segment between BeginVertices
778  * and EndVertices commands. */
779 
780  // We already counted the number of primitives while reading the command list
781  geometry.primitives.reserve(polygon.primitiveCount);
782 
783  // Running vertex buffer
784 
785  PrimitiveVertex vertex;
786 
787  bool hasVertex = false;
788  Primitive *primitive = 0;
789 
790  StackMixMap::const_iterator stackMix = ctx.stackMix.end();
791 
792  // Default values
793 
794  StackBoneMap::const_iterator stackBone = ctx.stackBones.find(polygon.defaultStack);
795  ModelNode_Sonic *defaultNode = (stackBone != ctx.stackBones.end()) ? stackBone->second->modelNode : 0;
796 
797  if (defaultNode)
798  vertex.nodes.push_back(PrimitiveNode(defaultNode, 1.0f));
799 
800  glm::vec3 primScale(ctx.defaultScale, ctx.defaultScale, ctx.defaultScale);
801 
802  // Texture dimensions, to convert the texture coordinates to OpenGL notation
803 
804  const double tWidth = !geometry.texture.empty() ? geometry.texture.getTexture().getWidth() : 1.0f;
805  const double tHeight = !geometry.texture.empty() ? geometry.texture.getTexture().getHeight() : 1.0f;
806 
807 
808  for (PolygonCommands::const_iterator c = polygon.commands.begin(); c != polygon.commands.end(); ++c) {
809  switch (c->command) {
810  case kPolygonNOP:
811  break;
812 
814  geometry.primitives.push_back(Primitive((PrimitiveType) c->parameters[0]));
815  primitive = &geometry.primitives.back();
816 
817  // We calculated the maximum length of a primitive earlier
818  primitive->vertices.reserve(polygon.primitiveSize);
819  break;
820 
821  case kPolygonEndVertices:
822  // We don't strictly need to observe the EndVertices command.
823  // In fact, according to some people, the Nintendo DS itself doesn't either.
824  break;
825 
826 
827  case kPolygonColor:
828  // BGR555
829  vertex.color[0] = ( c->parameters[0] & 0x1F) / 31.0f;
830  vertex.color[1] = ((c->parameters[0] >> 5) & 0x1F) / 31.0f;
831  vertex.color[2] = ((c->parameters[0] >> 10) & 0x1F) / 31.0f;
832  break;
833 
834  case kPolygonTexCoord:
835  // One unit ^= one texel!
836  vertex.texCoord[0] = readNintendoFixedPoint( c->parameters[0] & 0xFFFF, true, 11, 4) / tWidth;
837  vertex.texCoord[1] = readNintendoFixedPoint((c->parameters[0] >> 16) & 0xFFFF, true, 11, 4) / tHeight;
838  break;
839 
840  case kPolygonNormal:
841  vertex.normal[0] = readNintendoFixedPoint( c->parameters[0] & 0x03FF, true, 0, 9);
842  vertex.normal[1] = readNintendoFixedPoint((c->parameters[0] >> 10) & 0x03FF, true, 0, 9);
843  vertex.normal[2] = readNintendoFixedPoint((c->parameters[0] >> 20) & 0x03FF, true, 0, 9);
844  break;
845 
846 
847  case kPolygonVertex16:
848  vertex.vertex[0] = readNintendoFixedPoint( c->parameters[0] & 0xFFFF, true, 3, 12);
849  vertex.vertex[1] = readNintendoFixedPoint((c->parameters[0] >> 16) & 0xFFFF, true, 3, 12);
850  vertex.vertex[2] = readNintendoFixedPoint( c->parameters[1] & 0xFFFF, true, 3, 12);
851 
852  hasVertex = true;
853  break;
854 
855  case kPolygonVertex10:
856  vertex.vertex[0] = readNintendoFixedPoint( c->parameters[0] & 0x03FF, true, 3, 6);
857  vertex.vertex[1] = readNintendoFixedPoint((c->parameters[0] >> 10) & 0x03FF, true, 3, 6);
858  vertex.vertex[2] = readNintendoFixedPoint((c->parameters[0] >> 20) & 0x03FF, true, 3, 6);
859 
860  hasVertex = true;
861  break;
862 
863  case kPolygonVertexXY:
864  vertex.vertex[0] = readNintendoFixedPoint( c->parameters[0] & 0xFFFF, true, 3, 12);
865  vertex.vertex[1] = readNintendoFixedPoint((c->parameters[0] >> 16) & 0xFFFF, true, 3, 12);
866 
867  hasVertex = true;
868  break;
869 
870  case kPolygonVertexXZ:
871  vertex.vertex[0] = readNintendoFixedPoint( c->parameters[0] & 0xFFFF, true, 3, 12);
872  vertex.vertex[2] = readNintendoFixedPoint((c->parameters[0] >> 16) & 0xFFFF, true, 3, 12);
873 
874  hasVertex = true;
875  break;
876 
877  case kPolygonVertexYZ:
878  vertex.vertex[1] = readNintendoFixedPoint( c->parameters[0] & 0xFFFF, true, 3, 12);
879  vertex.vertex[2] = readNintendoFixedPoint((c->parameters[0] >> 16) & 0xFFFF, true, 3, 12);
880 
881  hasVertex = true;
882  break;
883 
884  case kPolygonVertexDiff:
885  vertex.vertex[0] += readNintendoFixedPoint( c->parameters[0] & 0x03FF, true, 0, 9) / 8;
886  vertex.vertex[1] += readNintendoFixedPoint((c->parameters[0] >> 10) & 0x03FF, true, 0, 9) / 8;
887  vertex.vertex[2] += readNintendoFixedPoint((c->parameters[0] >> 20) & 0x03FF, true, 0, 9) / 8;
888 
889  hasVertex = true;
890  break;
891 
892 
894  if ((stackMix = ctx.stackMix.find(c->parameters[0])) != ctx.stackMix.end()) {
895  // Was this stack position filled with kBoneLoadStack?
896 
897  vertex.nodes.clear();
898  vertex.nodes.reserve(stackMix->second.size());
899  for (StackMixes::const_iterator m = stackMix->second.begin(); m != stackMix->second.end(); ++m)
900  vertex.nodes.push_back(PrimitiveNode(*m));
901  } else if ((stackBone = ctx.stackBones.find(c->parameters[0])) != ctx.stackBones.end()) {
902  // Is this a regular bone position?
903 
904  vertex.nodes.clear();
905  vertex.nodes.push_back(PrimitiveNode(stackBone->second->modelNode, 1.0f));
906  }
907 
908  // Reset the scale
909  primScale[0] = 1.0f;
910  primScale[1] = 1.0f;
911  primScale[2] = 1.0f;
912  break;
913 
914  case kPolygonMatrixScale:
915  // The NDS probably directly scales the current matrix?
916  primScale[0] *= readNintendoFixedPoint(c->parameters[0], true, 19, 12);
917  primScale[1] *= readNintendoFixedPoint(c->parameters[1], true, 19, 12);
918  primScale[2] *= readNintendoFixedPoint(c->parameters[2], true, 19, 12);
919  break;
920 
921  // Commands we hopefully won't need
922  case kPolygonMatrixMode:
923  case kPolygonMatrixPush:
924  case kPolygonMatrixPop:
925  case kPolygonMatrixStore:
935  case kPolygonPaletteBase:
938  case kPolygonLightVector:
939  case kPolygonLightColor:
940  case kPolygonShininess:
941  case kPolygonSwapBuffers:
942  case kPolygonViewport:
943  case kPolygonBoxTest:
944  case kPolygonPosTest:
945  case kPolygonVecTest:
946  throw Common::Exception("Unsupported polygon command: 0x%02X", (uint) c->command);
947 
948  default:
949  throw Common::Exception("Invalid polygon command: 0x%02X", (uint) c->command);
950  }
951 
952  if (hasVertex && primitive) {
953  /* TODO: We're disabling primitives with mixed stack positions here.
954  * To support this, we need a way to properly average several
955  * matrices in evaluatePrimitive(). */
956  if (vertex.nodes.size() > 1)
957  primitive->invalid = true;
958 
959  primitive->vertices.push_back(vertex);
960  primitive->vertices.back().vertex *= primScale;
961 
962  hasVertex = false;
963  }
964  }
965 
966 }
967 
969  /* Create index lists for the vertices, according to the type. */
970 
971  switch (primitive.type) {
973  createIndicesTriangles(primitive);
974  return;
975 
976  case kPrimitiveTypeQuads:
977  createIndicesQuads(primitive);
978  break;
979 
981  createIndicesTriangleStrip(primitive);
982  break;
983 
985  createIndicesQuadStrip(primitive);
986  break;
987 
988  default:
989  throw Common::Exception("Invalid primitive type %u", (uint) primitive.type);
990  }
991 }
992 
994  /* Just plain old triangles. */
995 
996  primitive.indices.resize(primitive.vertices.size());
997 
998  for (size_t i = 0; i < primitive.vertices.size(); i++)
999  primitive.indices[i] = i;
1000 }
1001 
1003  /* Triangle strip. Create indices to unfold the strip into a triangle list. */
1004 
1005  if (primitive.vertices.size() < 3)
1006  return;
1007 
1008  primitive.indices.reserve((primitive.vertices.size() - 2) * 3);
1009 
1010  for (size_t i = 0; i < primitive.vertices.size() - 2; i++) {
1011  if (i & 1) {
1012  primitive.indices.push_back(i);
1013  primitive.indices.push_back(i + 2);
1014  primitive.indices.push_back(i + 1);
1015  } else {
1016  primitive.indices.push_back(i);
1017  primitive.indices.push_back(i + 1);
1018  primitive.indices.push_back(i + 2);
1019  }
1020  }
1021 }
1022 
1024  /* Quads. Create indices to unfold the quads into a triangle list. */
1025 
1026  if (primitive.vertices.size() < 4)
1027  return;
1028 
1029  primitive.indices.reserve((primitive.vertices.size() / 4) * 6);
1030 
1031  for (size_t i = 0; i < primitive.vertices.size() - 3; i += 4) {
1032  primitive.indices.push_back(i);
1033  primitive.indices.push_back(i + 1);
1034  primitive.indices.push_back(i + 2);
1035 
1036  primitive.indices.push_back(i + 2);
1037  primitive.indices.push_back(i + 3);
1038  primitive.indices.push_back(i);
1039  }
1040 }
1041 
1043  /* Quad strip. Create indices to unfold the quads into a triangle list. */
1044 
1045  if (primitive.vertices.size() < 4)
1046  return;
1047 
1048  primitive.indices.reserve(((primitive.vertices.size() - 2) / 2) * 6);
1049 
1050  for (size_t i = 0; i < primitive.vertices.size() - 2; i += 2) {
1051  primitive.indices.push_back(i);
1052  primitive.indices.push_back(i + 1);
1053  primitive.indices.push_back(i + 2);
1054 
1055  primitive.indices.push_back(i + 1);
1056  primitive.indices.push_back(i + 3);
1057  primitive.indices.push_back(i + 2);
1058  }
1059 }
1060 
1062  /* Manually recreate the bounding box by inspecting the vertex buffers. */
1063 
1064  _boundBox.clear();
1065 
1066  for (Geometries::const_iterator g = _geometries.begin(); g != _geometries.end(); ++g) {
1067  for (Primitives::const_iterator p = g->primitives.begin(); p != g->primitives.end(); ++p) {
1068  if (p->vertexBuffer.getVertexDecl().empty())
1069  continue;
1070 
1071  const VertexAttrib &vpos = p->vertexBuffer.getVertexDecl()[0];
1072  assert(vpos.index == VPOSITION);
1073  assert(vpos.type == GL_FLOAT);
1074 
1075  uint32 stride = MAX<uint32>(vpos.size, vpos.stride / sizeof(float));
1076 
1077  const float *vertexData = reinterpret_cast<const float *>(vpos.pointer);
1078 
1079  const float *vX = vertexData + 0;
1080  const float *vY = vertexData + 1;
1081  const float *vZ = vertexData + 2;
1082 
1083  for (uint32 v = 0; v < p->vertexBuffer.getCount(); v++)
1084  _boundBox.add(vX[v * stride], vY[v * stride], vZ[v * stride]);
1085  }
1086  }
1087 
1088  float minX, minY, minZ, maxX, maxY, maxZ;
1089  _boundBox.getMin(minX, minY, minZ);
1090  _boundBox.getMax(maxX, maxY, maxZ);
1091 
1092  _center[0] = minX + ((maxX - minX) / 2.0f);
1093  _center[1] = minY + ((maxY - minY) / 2.0f);
1094  _center[2] = minZ + ((maxZ - minZ) / 2.0f);
1095 
1099 }
1100 
1102  ctx.clear();
1103 
1104  ctx.state = new State;
1105 }
1106 
1108  if (!ctx.state || ctx.nodes.empty()) {
1109  ctx.clear();
1110  return;
1111  }
1112 
1113  for (std::list<ModelNode_Sonic *>::iterator n = ctx.nodes.begin(); n != ctx.nodes.end(); ++n) {
1114  ctx.state->nodeList.push_back(*n);
1115  ctx.state->nodeMap.insert(std::make_pair((*n)->getName(), *n));
1116 
1117  if (!(*n)->getParent())
1118  ctx.state->rootNodes.push_back(*n);
1119  }
1120 
1121  _stateList.push_back(ctx.state);
1122  _stateMap.insert(std::make_pair(ctx.state->name, ctx.state));
1123 
1124  if (!_currentState)
1125  _currentState = ctx.state;
1126 
1127  ctx.state = 0;
1128 
1129  ctx.nodes.clear();
1130 }
1131 
1132 // --- Run-time methods ---
1133 
1135  for (Geometries::iterator g = _geometries.begin(); g != _geometries.end(); ++g)
1136  for (Primitives::iterator p = g->primitives.begin(); p != g->primitives.end(); ++p)
1137  evaluatePrimitive(*p);
1138 }
1139 
1141  /* Create the actual IBO and VBO structures. */
1142 
1143  if (primitive.invalid || primitive.indices.empty() || primitive.vertices.empty())
1144  return;
1145 
1146  // Create the index buffer
1147 
1148  primitive.indexBuffer.setSize(primitive.indices.size(), sizeof(uint16), GL_UNSIGNED_SHORT);
1149 
1150  uint16 *indices = reinterpret_cast<uint16 *>(primitive.indexBuffer.getData());
1151  memcpy(indices, &primitive.indices[0], primitive.indices.size() * sizeof(uint16));
1152 
1153  // Create vertex buffer
1154 
1155  VertexDecl vertexDecl;
1156 
1157  vertexDecl.push_back(VertexAttrib(VPOSITION, 3, GL_FLOAT));
1158  vertexDecl.push_back(VertexAttrib(VNORMAL , 3, GL_FLOAT));
1159  vertexDecl.push_back(VertexAttrib(VCOLOR , 4, GL_FLOAT));
1160  vertexDecl.push_back(VertexAttrib(VTCOORD , 2, GL_FLOAT));
1161 
1162  primitive.vertexBuffer.setVertexDeclInterleave(primitive.vertices.size(), vertexDecl);
1163 
1164  float *vData = reinterpret_cast<float *>(primitive.vertexBuffer.getData());
1165  for (PrimitiveVertices::const_iterator v = primitive.vertices.begin(); v != primitive.vertices.end(); ++v) {
1166  /* To get the absolute position of the vertex, transform it by the absolute
1167  * position of its base node. Use an identity matrix as a fallback. */
1168 
1169  // TODO: For some primitives, we need to calculate the weighted average of several matrices
1170  glm::mat4 matrix;
1171  if (!v->nodes.empty() && v->nodes[0].node)
1172  matrix = v->nodes[0].node->getAbsolutePosition();
1173 
1174  const glm::vec4 pos = matrix * glm::vec4(v->vertex.x, v->vertex.y, v->vertex.z, 1);
1175 
1176  *vData++ = pos[0];
1177  *vData++ = pos[1];
1178  *vData++ = pos[2];
1179 
1180  *vData++ = v->normal[0];
1181  *vData++ = v->normal[1];
1182  *vData++ = v->normal[2];
1183 
1184  *vData++ = v->color[0];
1185  *vData++ = v->color[1];
1186  *vData++ = v->color[2];
1187  *vData++ = v->color[3];
1188 
1189  *vData++ = v->texCoord[0];
1190  *vData++ = v->texCoord[1];
1191  }
1192 
1193 }
1194 
1195 // --- Utility methods ---
1196 
1198  switch (cmd) {
1199  case kBoneNOP: return 0;
1200  case kBoneEnd: return 0;
1201  case kBoneSetInvisible: return 2;
1202  case kBoneSetPolygonStack: return 1;
1203  case kBoneSetMaterial1: return 1;
1204  case kBoneSetPolygon: return 1;
1205  case kBoneConnect1: return 3;
1206  case kBoneUnknown1: return 1;
1207  case kBoneLoadStack: return count * 3 + 2;
1208  case kBoneBeginPair: return 0;
1209  case kBoneUnknown2: return 2;
1210  case kBoneSetMaterial2: return 1;
1211  case kBoneConnect2: return 4;
1212  case kBoneEndPair: return 0;
1213  case kBoneUnknown3: return 6;
1214  case kBoneSetMaterial3: return 1;
1215  case kBoneConnect3: return 4;
1216  case kBoneConnect4: return 5;
1217 
1218  default:
1219  throw Common::Exception("Invalid bone command: 0x%02X", (uint) cmd);
1220  }
1221 }
1222 
1224  switch (cmd) {
1225  case kPolygonNOP: return 0;
1226 
1227  // Matrix commands
1228  case kPolygonMatrixMode: return 1;
1229  case kPolygonMatrixPush: return 0;
1230  case kPolygonMatrixPop: return 1;
1231  case kPolygonMatrixStore: return 1;
1232  case kPolygonMatrixRestore: return 1;
1233  case kPolygonMatrixIdentity: return 0;
1234  case kPolygonMatrixLoad4x4: return 16;
1235  case kPolygonMatrixLoad4x3: return 12;
1236  case kPolygonMatrixMult4x4: return 16;
1237  case kPolygonMatrixMult4x3: return 12;
1238  case kPolygonMatrixMult3x3: return 9;
1239  case kPolygonMatrixScale: return 3;
1240  case kPolygonMatrixTranslate: return 3;
1241 
1242  // Vertex attributes
1243  case kPolygonColor: return 1;
1244  case kPolygonNormal: return 1;
1245  case kPolygonTexCoord: return 1;
1246 
1247  // Vertex coordinates
1248  case kPolygonVertex16: return 2;
1249  case kPolygonVertex10: return 1;
1250  case kPolygonVertexXY: return 1;
1251  case kPolygonVertexXZ: return 1;
1252  case kPolygonVertexYZ: return 1;
1253  case kPolygonVertexDiff: return 1;
1254 
1255  case kPolygonPolygonAttrib: return 1;
1256  case kPolygonTexImageParam: return 1;
1257 
1258  case kPolygonPaletteBase: return 1;
1259 
1260  // Lighting
1261  case kPolygonDiffuseAmbient: return 1;
1262  case kPolygonSpecularEmit: return 1;
1263  case kPolygonLightVector: return 1;
1264  case kPolygonLightColor: return 1;
1265  case kPolygonShininess: return 32;
1266 
1267  case kPolygonBeginVertices: return 1;
1268  case kPolygonEndVertices: return 0;
1269 
1270  case kPolygonSwapBuffers: return 1;
1271 
1272  case kPolygonViewport: return 1;
1273 
1274  case kPolygonBoxTest: return 3;
1275  case kPolygonPosTest: return 2;
1276  case kPolygonVecTest: return 1;
1277 
1278  default:
1279  throw Common::Exception("Invalid polygon command: 0x%02X", (uint) cmd);
1280  }
1281 }
1282 
1283 glm::mat4 Model_Sonic::createPivot(double a, double b, uint8 select, uint8 negate) {
1284  float pivot[16] = {
1285  0.0f, 0.0f, 0.0f, 0.0f,
1286  0.0f, 0.0f, 0.0f, 0.0f,
1287  0.0f, 0.0f, 0.0f, 0.0f,
1288  0.0f, 0.0f, 0.0f, 1.0f
1289  };
1290 
1291  float one = 1.0f;
1292  float a2 = a;
1293  float b2 = b;
1294 
1295  if ((negate == 1) || (negate == 3) || (negate == 5) || (negate == 7) ||
1296  (negate == 9) || (negate == 11) || (negate == 13) || (negate == 15))
1297  one = -one;
1298 
1299  if ((negate == 2) || (negate == 3) || (negate == 6) || (negate == 7) ||
1300  (negate == 10) || (negate == 11) || (negate == 14) || (negate == 15))
1301  b2 = -b2;
1302 
1303  if ((negate == 4) || (negate == 5) || (negate == 6) || (negate == 7) ||
1304  (negate == 12) || (negate == 13) || (negate == 14) || (negate == 15))
1305  a2 = -a2;
1306 
1307  switch (select) {
1308  case 0:
1309  pivot[ 0] = one; pivot[5] = a; pivot[6] = b; pivot[9] = b2; pivot[10] = a2; break;
1310  case 1:
1311  pivot[ 1] = one; pivot[4] = a; pivot[6] = b; pivot[8] = b2; pivot[10] = a2; break;
1312  case 2:
1313  pivot[ 2] = one; pivot[4] = a; pivot[5] = b; pivot[8] = b2; pivot[ 9] = a2; break;
1314  case 3:
1315  pivot[ 4] = one; pivot[1] = a; pivot[2] = b; pivot[9] = b2; pivot[10] = a2; break;
1316  case 4:
1317  pivot[ 5] = one; pivot[0] = a; pivot[2] = b; pivot[8] = b2; pivot[10] = a2; break;
1318  case 5:
1319  pivot[ 6] = one; pivot[0] = a; pivot[1] = b; pivot[8] = b2; pivot[ 9] = a2; break;
1320  case 6:
1321  pivot[ 8] = one; pivot[1] = a; pivot[2] = b; pivot[5] = b2; pivot[ 6] = a2; break;
1322  case 7:
1323  pivot[ 9] = one; pivot[0] = a; pivot[2] = b; pivot[4] = b2; pivot[ 6] = a2; break;
1324  case 8:
1325  pivot[10] = one; pivot[0] = a; pivot[1] = b; pivot[4] = b2; pivot[ 5] = a2; break;
1326  case 9:
1327  pivot[ 0] = -a; break;
1328 
1329  default:
1330  break;
1331  }
1332 
1333  return glm::make_mat4(pivot);
1334 }
1335 
1337  /* We're overriding Model::render() here, because Model_Sonic keeps the geometry,
1338  * while in other Model classes, the geometry is inside the ModelNodes.
1339  *
1340  * TODO: Find a way to merge this back into Model?
1341  */
1342 
1343  if (!_currentState || (pass > kRenderPassAll))
1344  return;
1345 
1346  if (pass == kRenderPassAll) {
1349  return;
1350  }
1351 
1352  // Apply our global model transformation
1353  glTranslatef(_position[0], _position[1], _position[2]);
1354  glRotatef(_orientation[3], _orientation[0], _orientation[1], _orientation[2]);
1355  glScalef(_scale[0], _scale[1], _scale[2]);
1356 
1357  // Draw the bounding box, if requested
1358  doDrawBound();
1359 
1360  TextureMan.reset();
1361 
1362  for (Geometries::const_iterator g = _geometries.begin(); g != _geometries.end(); ++g) {
1363  TextureMan.set(g->texture);
1364 
1365  for (Primitives::const_iterator p = g->primitives.begin(); p != g->primitives.end(); ++p)
1366  p->vertexBuffer.draw(GL_TRIANGLES, p->indexBuffer);
1367  }
1368 
1369  TextureMan.reset();
1370 
1371  // Draw the skeleton, if requested
1372  doDrawSkeleton();
1373 }
1374 
1375 
1377 }
1378 
1380 }
1381 
1383  bone.modelNode = this;
1384 
1385  _name = bone.name;
1386  _render = true;
1387 
1388  // Decompose the bone's transformation matrix into TRS
1389 
1390  const glm::mat4 &m = bone.transform;
1391 
1392  _position[0] = m[3][0];
1393  _position[1] = m[3][1];
1394  _position[2] = m[3][2];
1395 
1396  glm::vec3 axis;
1397  glm::axisAngle(m, axis, _orientation[3]);
1399  _orientation[0] = axis.x;
1400  _orientation[1] = axis.y;
1401  _orientation[2] = axis.z;
1402 
1403  _scale[0] = sqrtf(m[0][0] * m[0][0] + m[1][0] * m[1][0] + m[2][0] * m[2][0]);
1404  _scale[1] = sqrtf(m[0][1] * m[0][1] + m[1][1] * m[1][1] + m[2][1] * m[2][1]);
1405  _scale[2] = sqrtf(m[0][2] * m[0][2] + m[1][2] * m[1][2] + m[2][2] * m[2][2]);
1406 
1407  if (bone.parent)
1408  setParent(bone.parent->modelNode);
1409 
1410  for (std::list<Model_Sonic::Bone *>::iterator c = bone.children.begin(); c != bone.children.end(); ++c) {
1411  ModelNode_Sonic *childNode = new ModelNode_Sonic(*_model);
1412  ctx.nodes.push_back(childNode);
1413 
1414  childNode->load(ctx, **c);
1415  }
1416 }
1417 
1420 }
1421 
1422 } // End of namespace Aurora
1423 
1424 } // End of namespace Graphics
Connect bones, set child stack, inherit parent stack.
Definition: model_sonic.h:139
#define ResMan
Shortcut for accessing the sound manager.
Definition: resman.h:557
GLvoid * getData()
Access buffer data.
Definition: indexbuffer.cpp:78
void createIndicesQuadStrip(Primitive &primitive)
#define MKTAG(a0, a1, a2, a3)
A wrapper macro used around four character constants, like &#39;DATA&#39;, to ensure portability.
Definition: endianness.h:140
void findStackBones(ParserContext &ctx)
void readBones(ParserContext &ctx)
Vertex XYZ, 16bit fixed-point.
Definition: model_sonic.h:171
NodeMap nodeMap
The nodes within the state, indexed by name.
Definition: model.h:207
static const uint32 kBMD0ID
Definition: model_sonic.cpp:76
void findStackMixes(ParserContext &ctx)
Only render transparent parts.
Definition: types.h:99
Vertex texture coordinates, VTCOORDi = VTCOORD + i.
Definition: vertexbuffer.h:39
Common::BoundingBox _absoluteBoundBox
The model&#39;s box after translate/rotate.
Definition: model.h:248
void createIndices(Primitive &primitive)
void readBone(ParserContext &ctx, Bone &bone, Info &info)
void evaluatePrimitive(Primitive &primitive)
Vertex XY, 16bit fixed-point, re-use Z.
Definition: model_sonic.h:173
uint8 scaleY
Scale (1 or 2) the texture in Y direction.
Definition: model_sonic.h:266
Common::SeekableSubReadStreamEndian * nsbmd
Definition: model_sonic.h:306
A class holding an UTF-8 string.
Definition: ustring.h:48
#define TextureMan
Shortcut for accessing the texture manager.
Definition: textureman.h:127
Structure to represent a StackMix at run-time.
Definition: model_sonic.h:423
Vertex position.
Definition: vertexbuffer.h:36
Connect bones, set parent stack, inherit child stack.
Definition: model_sonic.h:143
uint8 scaleX
Scale (1 or 2) the texture in X direction.
Definition: model_sonic.h:265
Loading Nintendo&#39;s NSBMD files found in Sonic.
static const uint32 kMDL0ID
Definition: model_sonic.cpp:77
GLuint index
Index of the vertex attribute (see VertexAttribIdEnum).
Definition: vertexbuffer.h:44
uint16 parentStack
Matrix stack position the parent uses.
Definition: model_sonic.h:239
uint8_t uint8
Definition: types.h:200
void readMaterialDefinitions(ParserContext &ctx)
The Aurora texture manager.
Common::UString name
The state&#39;s name.
Definition: model.h:204
GLenum type
Data type of each attribute component in the array.
Definition: vertexbuffer.h:46
Mathematical helpers.
Vertex XZ, 16bit fixed-point, re-use Y.
Definition: model_sonic.h:174
Vertex color.
Definition: vertexbuffer.h:38
NodeList rootNodes
The nodes in the state without a parent.
Definition: model.h:209
void setParent(ModelNode *parent)
Set the node&#39;s parent.
Definition: modelnode.cpp:143
void setVertexDeclInterleave(uint32 vertCount, VertexDecl &decl)
Set the interleaved vertex declaration for this buffer.
State * _currentState
The current state.
Definition: model.h:227
Multiply a line vector by the directional vector.
Definition: model_sonic.h:199
void createGeometry(ParserContext &ctx)
Set default stack position for a polygon.
Definition: model_sonic.h:130
size_t pos() const
Obtains the current value of the stream position indicator of the stream.
Definition: readstream.cpp:140
bool _render
Render the node?
Definition: modelnode.h:235
uint32 getHeight() const
Definition: texture.cpp:80
End marker for bone command list.
Definition: model_sonic.h:128
Push current matrix onto stack.
Definition: model_sonic.h:152
Multiply a line vector by the clip matrix.
Definition: model_sonic.h:198
uint16 nodeStack
Matrix stack position this bone uses.
Definition: model_sonic.h:238
const GLvoid * pointer
Offset of the first component of the first generic vertex attribute.
Definition: vertexbuffer.h:48
Common::UString _fileName
The model&#39;s file name.
Definition: model.h:218
std::vector< VertexAttrib > VertexDecl
Vertex data layout.
Definition: vertexbuffer.h:63
void load(Model_Sonic::ParserContext &ctx, Model_Sonic::Bone &bone)
Bone * parent
Pointer to the parent bones.
Definition: model_sonic.h:241
uint8 readInfoOffsetCount(ParserContext &ctx, Infos &infos, uint32 offset)
Utility templates and functions for working with strings and streams.
uint32 primitiveSize
Maximum length of a primitive from this polygon.
Definition: model_sonic.h:277
Connect bones, inherit child and parent stack.
Definition: model_sonic.h:133
bool wrapY
true: wrap, false: clamp.
Definition: model_sonic.h:261
Exception that provides a stack of explanations.
Definition: error.h:36
Multiply current matrix by 3x3 values.
Definition: model_sonic.h:161
size_t size() const
Obtains the total size of the stream, measured in bytes.
Definition: readstream.cpp:144
void readBoneCommands(ParserContext &ctx)
void readMaterials(ParserContext &ctx)
uint32 primitiveCount
Number of primitive this polygon produces.
Definition: model_sonic.h:276
Model * _model
The model this node belongs to.
Definition: modelnode.h:207
void exceptionDispatcherWarning(const char *s,...)
Exception dispatcher that prints the exception as a warning, and adds another reason on top...
Definition: error.cpp:158
Common::UString _name
The model&#39;s name.
Definition: model.h:220
std::list< ModelNode_Sonic * > nodes
Definition: model_sonic.h:343
Vertex normal.
Definition: vertexbuffer.h:37
void render(RenderPass pass)
Render the object.
float _scale[3]
Scale of the node.
Definition: modelnode.h:224
Info structure, specifies names and offsets of all kinds of lists in Nintendo files.
Definition: model_sonic.h:203
void readMaterialDefinition(ParserContext &ctx, Material &material, Info &info)
Common::BoundingBox _boundBox
The model&#39;s bounding box.
Definition: model.h:246
void getMin(float &x, float &y, float &z) const
Definition: boundingbox.cpp:68
Basic exceptions to throw.
Load 4x4 values into current matrix.
Definition: model_sonic.h:157
Store current matrix onto stack.
Definition: model_sonic.h:154
Connect bones, set child and parent stack.
Definition: model_sonic.h:144
const char * c_str() const
Return the (utf8 encoded) string data.
Definition: ustring.cpp:249
RenderPass
Definition: types.h:97
Render all parts.
Definition: types.h:100
float _position[3]
Model&#39;s position.
Definition: model.h:239
size_t seek(ptrdiff_t offset, Origin whence=kOriginBegin)
Sets the stream position indicator for the stream.
Definition: readstream.cpp:148
ModelType
The display type of a model.
Definition: types.h:51
uint16_t uint16
Definition: types.h:202
Set specular reflection and emission colors.
Definition: model_sonic.h:185
Utility templates and functions.
uint8 readInfoOffset(ParserContext &ctx, Infos &infos, uint32 offset)
void getMax(float &x, float &y, float &z) const
Definition: boundingbox.cpp:87
Test if box is inside view volume.
Definition: model_sonic.h:197
bool flipY
true: flip on every 2nd texture wrap.
Definition: model_sonic.h:263
void info(const char *s,...)
Definition: util.cpp:69
void readPolygonCommands(ParserContext &ctx, Polygon &polygon, uint32 listSize)
virtual size_t skip(ptrdiff_t offset)
Skip the specified number of bytes, adding that offset to the current position in the stream...
Definition: readstream.h:317
uint16 nodeID
ID of this bone.
Definition: model_sonic.h:235
void transform(const glm::mat4 &m)
double readNintendoFixedPoint(uint32 value, bool sign, uint8 iBits, uint8 fBits)
Read a fixed-point value, in a format used by the Nintendo DS.
Definition: util.cpp:159
Utility functions for working with differing string encodings.
bool empty() const
Is the string empty?
Definition: ustring.cpp:245
Multiply current matrix by scale matrix.
Definition: model_sonic.h:162
Restore current matrix from stack.
Definition: model_sonic.h:155
StackException Exception
Definition: error.h:59
Vertex XYZ, 10bit fixed-point.
Definition: model_sonic.h:172
void newState(ParserContext &ctx)
void absolutize()
Apply the origin transformations directly to the coordinates.
float _position[3]
Position of the node.
Definition: modelnode.h:221
NodeList nodeList
The nodes within the state.
Definition: model.h:206
void warning(const char *s,...)
Definition: util.cpp:33
std::list< Bone * > children
Pointers to the child bones.
Definition: model_sonic.h:242
Load 4x3 values into current matrix.
Definition: model_sonic.h:158
StateMap _stateMap
All states within this model, index by name.
Definition: model.h:226
Basic reading stream interfaces.
uint32 getWidth() const
Definition: texture.cpp:76
Vertex YZ, 16bit fixed-point, re-use X.
Definition: model_sonic.h:175
float _scale[3]
Model&#39;s scale.
Definition: model.h:237
size_t read(void *dataPtr, size_t dataSize)
Read data from the stream.
Definition: readstream.cpp:114
uint32 readUint32BE()
Read an unsigned 32-bit word stored in big endian (MSB first) order from the stream and return it...
Definition: readstream.h:166
Multiply current matrix by 4x4 values.
Definition: model_sonic.h:159
bool flipX
true: flip on every 2nd texture wrap.
Definition: model_sonic.h:262
void addState(ParserContext &ctx)
Set diffuse and ambient reflection colors.
Definition: model_sonic.h:184
ParserContext(const Common::UString &name)
Definition: model_sonic.cpp:83
void readMaterialResource(ParserContext &ctx, uint textureOrPalette)
glm::mat4 transform
Complete local transformation this bone specifies.
Definition: model_sonic.h:233
GLint size
Number of components per vertex attribute, must be 1, 2, 3, 4.
Definition: vertexbuffer.h:45
void createPrimitives(ParserContext &ctx, Geometry &geometry, Polygon &polygon)
void add(float x, float y, float z)
uint16 parentID
ID of parent bone.
Definition: model_sonic.h:236
Basic type definitions to handle files used in BioWare&#39;s Aurora engine.
Plain, unextended ASCII (7bit clean).
Definition: encoding.h:40
float _orientation[4]
Model&#39;s orientation.
Definition: model.h:238
ModelNode_Sonic * modelNode
Model node this bone represents.
Definition: model_sonic.h:244
UString toLower() const
Return a lowercased copy of the string.
Definition: ustring.cpp:481
void readModelHeader(ParserContext &ctx)
Set specular reflection shininess.
Definition: model_sonic.h:188
Common::UString _name
The node&#39;s name.
Definition: modelnode.h:216
void createIndicesTriangleStrip(Primitive &primitive)
Intermediate structure to (re)create the VBO/IBO from.
Definition: model_sonic.h:435
Multiply current matrix by 4x3 values.
Definition: model_sonic.h:160
A texture as used in the Aurora engines.
uint32_t uint32
Definition: types.h:204
UString debugTag(uint32 tag, bool trim)
Create an elaborate string from an integer tag, for debugging purposes.
Definition: strutil.cpp:117
static uint8 getBoneParameterCount(BoneCommandID cmd, uint8 count)
Return the number of parameters required for this bone command.
GLvoid * getData()
Access buffer data.
float _orientation[4]
Orientation of the node.
Definition: modelnode.h:223
float _center[3]
Model&#39;s center.
Definition: model.h:241
glm::mat4 _absolutePosition
Definition: model.h:243
void parseBoneCommands(ParserContext &ctx)
void load(ParserContext &ctx)
bool wrapX
true: wrap, false: clamp.
Definition: model_sonic.h:260
static float rad2deg(float rad)
Definition: maths.h:93
std::vector< StackMix > StackMixes
Definition: model_sonic.h:297
void finalize()
Finalize the loading procedure.
Definition: model.cpp:807
void createIndicesTriangles(Primitive &primitive)
void readPolygon(ParserContext &ctx, Polygon &polygon, Info &info)
UString readStringFixed(SeekableReadStream &stream, Encoding encoding, size_t length)
Read length bytes as a string with the given encoding out of a stream.
Definition: encoding.cpp:297
#define pass
Definition: fft.cpp:257
void createAbsoluteBound()
Forward to ModelNode::createAbsoluteBound(), because we need this during the loading.
void createIndicesQuads(Primitive &primitive)
Common::UString texture
Name of texture within NSBTX.
Definition: model_sonic.h:254
void readPolygons(ParserContext &ctx)
void createModelNodes(ParserContext &ctx)
void findRootBones(ParserContext &ctx)
std::vector< Info > Infos
Definition: model_sonic.h:209
void readHeader(ParserContext &ctx)
T MAX(T a, T b)
Definition: util.h:71
GLsizei stride
Byte offset between consecutive vertex attributes.
Definition: vertexbuffer.h:47
StateList _stateList
All states within this model.
Definition: model.h:225
static Common::SeekableSubReadStreamEndian * open(Common::SeekableReadStream &stream)
Treat this stream as a Nitro file and return an endian&#39;d stream according to its BOM.
Definition: nitrofile.cpp:52
Generic vertex attribute data.
Definition: vertexbuffer.h:43
Interface for a seekable & readable data stream.
Definition: readstream.h:265
byte readByte()
Read an unsigned byte from the stream and return it.
Definition: readstream.h:92
Multiply current matrix by translation matrix.
Definition: model_sonic.h:163
void setSize(uint32 indexCount, uint32 indexSize, GLenum indexType)
Change buffer size.
Definition: indexbuffer.cpp:65
Model_Sonic(const Common::UString &name, ModelType type=kModelTypeObject)
The global resource manager for Aurora resources.
Only render opaque parts.
Definition: types.h:98
void readModel(ParserContext &ctx)
static glm::mat4 createPivot(double a, double b, uint8 select, uint8 negate)
Create a specific pivot matrix.
unsigned int uint
Definition: types.h:211
static uint8 getPolygonParameterCount(PolygonCommandID cmd)
Return the number of parameters required for this geometry command.
Vertex XYZ delta, 10bit fixed-point.
Definition: model_sonic.h:176