58 #include "glm/gtc/type_ptr.hpp" 59 #include "glm/gtc/matrix_transform.hpp" 60 #include "glm/gtx/matrix_interpolation.hpp" 98 for (std::list<ModelNode_Sonic *>::iterator n = nodes.begin(); n != nodes.end(); ++n)
194 ctx.
nsbmd->
skip(2 + 2 + 4 + count * (2 + 2));
198 for (
uint i = 0; i < count; i++) {
203 for (
uint i = 0; i < count; i++)
218 ctx.
nsbmd->
skip(2 + 2 + 4 + count * (2 + 2));
222 for (
uint i = 0; i < count; i++) {
229 for (
uint i = 0; i < count; i++)
250 if ((versionMajor != 2) || (versionMinor != 0))
251 throw Common::Exception(
"Unsupported version %u.%u", versionMajor, versionMinor);
258 if (headerSize != 16)
262 if (sectionCount != 1)
285 _name = models[0].name;
298 ctx.
bones.resize(boneCount);
299 for (
uint i = 0; i < boneCount; i++)
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;
320 const uint8 pivotSelect = (flags >> 4) & 0xF;
321 const uint8 pivotNegate = (flags >> 8) & 0xF;
337 const glm::mat4 pivot =
createPivot(pivotA, pivotB, pivotSelect, pivotNegate);
343 glm::mat4 rotateMatrix;
345 rotateMatrix[0][0] = rotate0;
390 for (
uint i = 0; i < paramCount; i++)
420 for (
uint i = 0; i < materialCount; i++)
436 material.
wrapX = (flags & 0x0001) != 0;
437 material.
wrapY = (flags & 0x0002) != 0;
438 material.
flipX = (flags & 0x0004) != 0;
439 material.
flipY = (flags & 0x0008) != 0;
441 const bool doubleX = (flags & 0x1000) != 0;
442 const bool doubleY = (flags & 0x4000) != 0;
444 material.
scaleX = 1 << (doubleX ? 1 : 0);
445 material.
scaleY = 1 << (doubleY ? 1 : 0);
459 for (
uint i = 0; i < resCount; i++) {
462 for (
uint j = 0; j < resources[i].count; j++) {
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;
484 for (
uint i = 0; i < polygonCount; i++)
520 polygon.
commands.reserve(listSize / 4);
521 while (listSize >= 4) {
525 for (
uint i = 0; i < 4; i++) {
532 for (
uint j = 0; (j < paramCount) && (listSize >= 4); j++, listSize -= 4)
556 uint16 polygonStack = 0xFFFF;
560 uint16 parentStack = 0xFFFF;
570 switch (c->command) {
585 polygonStack = c->parameters[0];
589 polygonStack = c->parameters[0];
592 ctx.
stackMix[polygonStack].resize(c->parameters[1]);
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;
600 warning(
"TODO: Bone command LoadStack: %u (\"%s\")", c->parameters[0],
_name.
c_str());
604 polygon = c->parameters[0];
611 ctx.
polygons[polygon].defaultStack = polygonStack;
617 material = c->parameters[0];
624 nodeID = c->parameters[0];
625 parentID = c->parameters[1];
627 node = (nodeID < ctx.
bones.size()) ? &ctx.
bones[nodeID] : 0;
628 parent = (parentID < ctx.
bones.size()) ? &ctx.
bones[parentID] : 0;
631 warning(
"No such node %u in bone command connect 0x%02X (\"%s\")",
632 c->parameters[0], c->parameters[1],
_name.
c_str());
639 parentStack = parent ? parent->
nodeStack : 0xFFFF;
641 switch (c->command) {
647 nodeStack = c->parameters[3];
651 parentStack = c->parameters[3];
655 nodeStack = c->parameters[3];
656 parentStack = c->parameters[4];
663 polygonStack = nodeStack;
666 if (parent && (parentStack != parent->
nodeStack))
667 warning(
"Parent stack != node parent stack (%u:%u, %u:%u) (\"%s\")",
690 for (Bones::iterator j = ctx.
bones.begin(); j != ctx.
bones.end(); ++j)
702 for (Bones::iterator j = ctx.
bones.begin(); j != ctx.
bones.end(); ++j) {
703 if (j->nodeStack == 0xFFFF)
706 std::pair<StackBoneMap::iterator, bool> result;
707 result = ctx.
stackBones.insert(std::make_pair(j->nodeStack, &*j));
710 warning(
"Stack collision: %u for node %u and %u (\"%s\")", j->nodeStack,
711 result.first->second->nodeID, j->nodeID,
_name.
c_str());
723 ctx.
nodes.push_back(rootNode);
732 StackBoneMap::iterator b = ctx.
stackBones.find(v->first);
734 b->second->modelNode->setInvisible(v->second);
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);
747 m->node = b->second->modelNode;
754 for (
size_t i = 0; i < ctx.
polygons.size(); i++) {
787 bool hasVertex =
false;
790 StackMixMap::const_iterator stackMix = ctx.
stackMix.end();
808 for (PolygonCommands::const_iterator c = polygon.
commands.begin(); c != polygon.
commands.end(); ++c) {
809 switch (c->command) {
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;
894 if ((stackMix = ctx.
stackMix.find(c->parameters[0])) != ctx.
stackMix.end()) {
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)
904 vertex.
nodes.clear();
952 if (hasVertex && primitive) {
956 if (vertex.
nodes.size() > 1)
959 primitive->
vertices.push_back(vertex);
960 primitive->
vertices.back().vertex *= primScale;
971 switch (primitive.
type) {
998 for (
size_t i = 0; i < primitive.
vertices.size(); i++)
1010 for (
size_t i = 0; i < primitive.
vertices.size() - 2; i++) {
1012 primitive.
indices.push_back(i);
1013 primitive.
indices.push_back(i + 2);
1014 primitive.
indices.push_back(i + 1);
1016 primitive.
indices.push_back(i);
1017 primitive.
indices.push_back(i + 1);
1018 primitive.
indices.push_back(i + 2);
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);
1036 primitive.
indices.push_back(i + 2);
1037 primitive.
indices.push_back(i + 3);
1038 primitive.
indices.push_back(i);
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);
1055 primitive.
indices.push_back(i + 1);
1056 primitive.
indices.push_back(i + 3);
1057 primitive.
indices.push_back(i + 2);
1067 for (Primitives::const_iterator p = g->primitives.begin(); p != g->primitives.end(); ++p) {
1068 if (p->vertexBuffer.getVertexDecl().empty())
1071 const VertexAttrib &vpos = p->vertexBuffer.getVertexDecl()[0];
1073 assert(vpos.
type == GL_FLOAT);
1077 const float *vertexData =
reinterpret_cast<const float *
>(vpos.
pointer);
1079 const float *vX = vertexData + 0;
1080 const float *vY = vertexData + 1;
1081 const float *vZ = vertexData + 2;
1083 for (
uint32 v = 0; v < p->vertexBuffer.getCount(); v++)
1084 _boundBox.
add(vX[v * stride], vY[v * stride], vZ[v * stride]);
1088 float minX, minY, minZ, maxX, maxY, maxZ;
1092 _center[0] = minX + ((maxX - minX) / 2.0f);
1093 _center[1] = minY + ((maxY - minY) / 2.0f);
1094 _center[2] = minZ + ((maxZ - minZ) / 2.0f);
1113 for (std::list<ModelNode_Sonic *>::iterator n = ctx.
nodes.begin(); n != ctx.
nodes.end(); ++n) {
1115 ctx.
state->
nodeMap.insert(std::make_pair((*n)->getName(), *n));
1117 if (!(*n)->getParent())
1136 for (Primitives::iterator p = g->primitives.begin(); p != g->primitives.end(); ++p)
1165 for (PrimitiveVertices::const_iterator v = primitive.
vertices.begin(); v != primitive.
vertices.end(); ++v) {
1171 if (!v->nodes.empty() && v->nodes[0].node)
1172 matrix = v->nodes[0].node->getAbsolutePosition();
1174 const glm::vec4 pos = matrix * glm::vec4(v->vertex.x, v->vertex.y, v->vertex.z, 1);
1180 *vData++ = v->normal[0];
1181 *vData++ = v->normal[1];
1182 *vData++ = v->normal[2];
1184 *vData++ = v->color[0];
1185 *vData++ = v->color[1];
1186 *vData++ = v->color[2];
1187 *vData++ = v->color[3];
1189 *vData++ = v->texCoord[0];
1190 *vData++ = v->texCoord[1];
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
1295 if ((negate == 1) || (negate == 3) || (negate == 5) || (negate == 7) ||
1296 (negate == 9) || (negate == 11) || (negate == 13) || (negate == 15))
1299 if ((negate == 2) || (negate == 3) || (negate == 6) || (negate == 7) ||
1300 (negate == 10) || (negate == 11) || (negate == 14) || (negate == 15))
1303 if ((negate == 4) || (negate == 5) || (negate == 6) || (negate == 7) ||
1304 (negate == 12) || (negate == 13) || (negate == 14) || (negate == 15))
1309 pivot[ 0] = one; pivot[5] = a; pivot[6] = b; pivot[9] = b2; pivot[10] = a2;
break;
1311 pivot[ 1] = one; pivot[4] = a; pivot[6] = b; pivot[8] = b2; pivot[10] = a2;
break;
1313 pivot[ 2] = one; pivot[4] = a; pivot[5] = b; pivot[8] = b2; pivot[ 9] = a2;
break;
1315 pivot[ 4] = one; pivot[1] = a; pivot[2] = b; pivot[9] = b2; pivot[10] = a2;
break;
1317 pivot[ 5] = one; pivot[0] = a; pivot[2] = b; pivot[8] = b2; pivot[10] = a2;
break;
1319 pivot[ 6] = one; pivot[0] = a; pivot[1] = b; pivot[8] = b2; pivot[ 9] = a2;
break;
1321 pivot[ 8] = one; pivot[1] = a; pivot[2] = b; pivot[5] = b2; pivot[ 6] = a2;
break;
1323 pivot[ 9] = one; pivot[0] = a; pivot[2] = b; pivot[4] = b2; pivot[ 6] = a2;
break;
1325 pivot[10] = one; pivot[0] = a; pivot[1] = b; pivot[4] = b2; pivot[ 5] = a2;
break;
1327 pivot[ 0] = -a;
break;
1333 return glm::make_mat4(pivot);
1365 for (Primitives::const_iterator p = g->primitives.begin(); p != g->primitives.end(); ++p)
1366 p->vertexBuffer.draw(GL_TRIANGLES, p->indexBuffer);
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]);
1410 for (std::list<Model_Sonic::Bone *>::iterator c = bone.
children.begin(); c != bone.
children.end(); ++c) {
1412 ctx.
nodes.push_back(childNode);
1414 childNode->
load(ctx, **c);
Connect bones, set child stack, inherit parent stack.
#define ResMan
Shortcut for accessing the sound manager.
GLvoid * getData()
Access buffer data.
void createIndicesQuadStrip(Primitive &primitive)
#define MKTAG(a0, a1, a2, a3)
A wrapper macro used around four character constants, like 'DATA', to ensure portability.
void findStackBones(ParserContext &ctx)
Set the palette base address.
void readBones(ParserContext &ctx)
Vertex XYZ, 16bit fixed-point.
NodeMap nodeMap
The nodes within the state, indexed by name.
static const uint32 kBMD0ID
void findStackMixes(ParserContext &ctx)
Only render transparent parts.
Vertex texture coordinates, VTCOORDi = VTCOORD + i.
Common::BoundingBox _absoluteBoundBox
The model's box after translate/rotate.
Texture & getTexture() const
void createIndices(Primitive &primitive)
void readBone(ParserContext &ctx, Bone &bone, Info &info)
void evaluatePrimitive(Primitive &primitive)
Vertex XY, 16bit fixed-point, re-use Z.
uint8 scaleY
Scale (1 or 2) the texture in Y direction.
Common::SeekableSubReadStreamEndian * nsbmd
A class holding an UTF-8 string.
#define TextureMan
Shortcut for accessing the texture manager.
Structure to represent a StackMix at run-time.
Connect bones, set parent stack, inherit child stack.
uint8 scaleX
Scale (1 or 2) the texture in X direction.
Loading Nintendo's NSBMD files found in Sonic.
static const uint32 kMDL0ID
GLuint index
Index of the vertex attribute (see VertexAttribIdEnum).
uint16 parentStack
Matrix stack position the parent uses.
Pop current matrix from stack.
void readMaterialDefinitions(ParserContext &ctx)
The Aurora texture manager.
Common::UString name
The state's name.
GLenum type
Data type of each attribute component in the array.
Vertex XZ, 16bit fixed-point, re-use Y.
VertexBuffer vertexBuffer
NodeList rootNodes
The nodes in the state without a parent.
void setParent(ModelNode *parent)
Set the node's parent.
Swap render engine buffers.
void setVertexDeclInterleave(uint32 vertCount, VertexDecl &decl)
Set the interleaved vertex declaration for this buffer.
State * _currentState
The current state.
Multiply a line vector by the directional vector.
PrimitiveVertices vertices
Set polygon to operate on.
void createGeometry(ParserContext &ctx)
Set default stack position for a polygon.
size_t pos() const
Obtains the current value of the stream position indicator of the stream.
bool _render
Render the node?
End marker for bone command list.
Push current matrix onto stack.
Multiply a line vector by the clip matrix.
uint16 nodeStack
Matrix stack position this bone uses.
const GLvoid * pointer
Offset of the first component of the first generic vertex attribute.
Common::UString _fileName
The model's file name.
std::vector< VertexAttrib > VertexDecl
Vertex data layout.
void load(Model_Sonic::ParserContext &ctx, Model_Sonic::Bone &bone)
Bone * parent
Pointer to the parent bones.
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.
Connect bones, inherit child and parent stack.
bool wrapY
true: wrap, false: clamp.
Exception that provides a stack of explanations.
Multiply current matrix by 3x3 values.
size_t size() const
Obtains the total size of the stream, measured in bytes.
void readBoneCommands(ParserContext &ctx)
std::vector< uint32 > parameters
BoneCommands boneCommands
void readMaterials(ParserContext &ctx)
uint32 primitiveCount
Number of primitive this polygon produces.
ModelNode_Sonic(Model &model)
Model * _model
The model this node belongs to.
void exceptionDispatcherWarning(const char *s,...)
Exception dispatcher that prints the exception as a warning, and adds another reason on top...
Common::UString _name
The model's name.
std::list< ModelNode_Sonic * > nodes
void render(RenderPass pass)
Render the object.
float _scale[3]
Scale of the node.
Info structure, specifies names and offsets of all kinds of lists in Nintendo files.
void readMaterialDefinition(ParserContext &ctx, Material &material, Info &info)
Common::BoundingBox _boundBox
The model's bounding box.
void getMin(float &x, float &y, float &z) const
Basic exceptions to throw.
Load 4x4 values into current matrix.
Store current matrix onto stack.
Connect bones, set child and parent stack.
const char * c_str() const
Return the (utf8 encoded) string data.
float _position[3]
Model's position.
size_t seek(ptrdiff_t offset, Origin whence=kOriginBegin)
Sets the stream position indicator for the stream.
ModelType
The display type of a model.
Set specular reflection and emission colors.
Utility templates and functions.
uint8 readInfoOffset(ParserContext &ctx, Infos &infos, uint32 offset)
void getMax(float &x, float &y, float &z) const
Test if box is inside view volume.
bool flipY
true: flip on every 2nd texture wrap.
void info(const char *s,...)
void createAbsoluteBound()
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...
uint16 nodeID
ID of this bone.
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.
Utility functions for working with differing string encodings.
BoneInvisible boneInvisible
bool empty() const
Is the string empty?
Multiply current matrix by scale matrix.
Restore current matrix from stack.
Vertex XYZ, 10bit fixed-point.
void newState(ParserContext &ctx)
void absolutize()
Apply the origin transformations directly to the coordinates.
float _position[3]
Position of the node.
NodeList nodeList
The nodes within the state.
void warning(const char *s,...)
std::list< Bone * > children
Pointers to the child bones.
Load 4x3 values into current matrix.
StateMap _stateMap
All states within this model, index by name.
Basic reading stream interfaces.
Set current matrix to identity.
Vertex YZ, 16bit fixed-point, re-use X.
float _scale[3]
Model's scale.
size_t read(void *dataPtr, size_t dataSize)
Read data from the stream.
uint32 readUint32BE()
Read an unsigned 32-bit word stored in big endian (MSB first) order from the stream and return it...
Multiply current matrix by 4x4 values.
bool flipX
true: flip on every 2nd texture wrap.
void addState(ParserContext &ctx)
Set diffuse and ambient reflection colors.
ParserContext(const Common::UString &name)
void readMaterialResource(ParserContext &ctx, uint textureOrPalette)
glm::mat4 transform
Complete local transformation this bone specifies.
GLint size
Number of components per vertex attribute, must be 1, 2, 3, 4.
void createPrimitives(ParserContext &ctx, Geometry &geometry, Polygon &polygon)
void add(float x, float y, float z)
uint16 parentID
ID of parent bone.
Basic type definitions to handle files used in BioWare's Aurora engine.
Plain, unextended ASCII (7bit clean).
float _orientation[4]
Model's orientation.
ModelNode_Sonic * modelNode
Model node this bone represents.
UString toLower() const
Return a lowercased copy of the string.
void readModelHeader(ParserContext &ctx)
Set specular reflection shininess.
Common::UString _name
The node's name.
void createIndicesTriangleStrip(Primitive &primitive)
Intermediate structure to (re)create the VBO/IBO from.
Multiply current matrix by 4x3 values.
A texture as used in the Aurora engines.
UString debugTag(uint32 tag, bool trim)
Create an elaborate string from an integer tag, for debugging purposes.
friend class ModelNode_Sonic
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.
float _center[3]
Model's center.
glm::mat4 _absolutePosition
void parseBoneCommands(ParserContext &ctx)
void load(ParserContext &ctx)
bool wrapX
true: wrap, false: clamp.
static float rad2deg(float rad)
std::vector< StackMix > StackMixes
void finalize()
Finalize the loading procedure.
uint32 offsetBoneCommands
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.
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.
void readPolygons(ParserContext &ctx)
void createModelNodes(ParserContext &ctx)
void findRootBones(ParserContext &ctx)
std::vector< Info > Infos
void readHeader(ParserContext &ctx)
std::vector< uint8 > parameters
GLsizei stride
Byte offset between consecutive vertex attributes.
StateList _stateList
All states within this model.
static Common::SeekableSubReadStreamEndian * open(Common::SeekableReadStream &stream)
Treat this stream as a Nitro file and return an endian'd stream according to its BOM.
Generic vertex attribute data.
Interface for a seekable & readable data stream.
byte readByte()
Read an unsigned byte from the stream and return it.
Multiply current matrix by translation matrix.
void setSize(uint32 indexCount, uint32 indexSize, GLenum indexType)
Change buffer size.
Model_Sonic(const Common::UString &name, ModelType type=kModelTypeObject)
The global resource manager for Aurora resources.
Only render opaque parts.
void readModel(ParserContext &ctx)
static glm::mat4 createPivot(double a, double b, uint8 select, uint8 negate)
Create a specific pivot matrix.
static uint8 getPolygonParameterCount(PolygonCommandID cmd)
Return the number of parameters required for this geometry command.
Vertex XYZ delta, 10bit fixed-point.
const Material * material