/* =========================================================================== maya2q3 - export .md3 files from maya Copyright (C) 2005 HermitWorks Entertainment Corporation This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. =========================================================================== */ #ifndef INC__x42_file_h_ #define INC__x42_file_h_ /* * x42 types and constants... * */ /* x42 files are laid out as sequential data blocks. Each block (line in the following list) must begin on a 16-byte boundary to make memory-mapping the data more efficient. Padding is insterted between blocks iff the block is not already aligned (don't arbitrarily round the addresses up). x42Header_t header x42Bone_t bones[ header.numBones ] ushort posTrackTime[ header.numPosTrackElems ] vec3_t posTrackData[ header.numPosTrackElems ] ushort rotTrackTime[ header.numRotTrackElems ] quat_t rotTrackData[ header.numRotTrackElems ] ushort scaleTrackTime[ header.numScaleTrackElems ] vec3_t scaleTrackData[ header.numScaleTrackElems ] x42Influence_t influences[ header.numInfluences ] x42Tag_t tags[ header.numTags ] x42Group_t groups[ header.numGroups ] x42VertAnim_t vertAnims[ header.numVerts ] x42VertNormal_t vertNorms[ header.numVerts ] x42VertTangent_t vertTangents[ header.numVerts ] x42VertAttrib_t vertAttribs[ header.numVerts ] ushort indices[ header.numIndices ] [extension data 0] [extension data 1] [...] [extension data 31] The bones array must be sorted such that a bone's parent index is always smaller than the bone's index. This allows a renderer to compute all bone matrices with only a single forward pass through the array. The bones array is also sorted by x42Bone_t.animGroup, in ascending order. The rule about animation groups is that a bone's animGroup index must be greater than or equal to that of its parent bone. Not meeting this requirement is considered to be an error. The animation groups correspond to model elements like arms, legs, head, tail, etc that can be playing seperate tracks at the same time. There's no real rule that says that all bones in a given animation group must be playing the same animation at the same time, but grouping and sorting the bones (and subsequently tracks) this way helps when implementing the renderer. The track infos for all the bones are packed together into three pairs of arrays: position time and value, rotation time and value, and scale time and valie. Time and value arrays are parallel. Track times are stored in normalized unsigned short form where [0, max_short] corresponds to [0, 1]. Multiplying by header.numFrames scales the normalized time back into the modelling tool's frame numbers. Each track within the array must contain at least one element at time equal to exactly 1 and this must be the last element within the track. If it contains more elements then the first must be at time 0. The tracks are packed in the same order as their corresponding bones. All of the vertices are packed into one big array, same with the indices. The groups contain the information needed to exec draw commands on these arrays. Each group must reference a *unique* range in the vertices and indices arrays. Vertices are not bound directly to the bones. Influences are attached to bones and define the required object to bone transform. Vertices are bound to the influences. Tags are an extension of bones. There must always be at least one bone and one influence. If an extension is present it must set its bit in header.extensions (see X42_EXTENSION_BIT). It may set its entry in header.extFlags as it chooses. The extensions must add their data blocks in order. If an extension adds a data block it must also set its extSizes entry to its data size. Each bone's matrix is computed as follows (note: post-multiply notation is used in these examples, so p' = p * M): -1 Mb = S * R * Sp * T Where: S is the bone's scale, derived from the scale track. R is the bone's rotation, derived from the rotation track. Sp is dependent on boneFlags_t::UseInvParentScale. If the flag is set then Sp is the parent bone's scale (note that this value is inverted in the equation). If the flag is not set, this is set to the identity matrix and the term is effectively dropped. T is the bone's translation, derived from the position track. Vertices are then attached to the bone via the influences. The final matrix that goes on the card is: M = Mi * Mb[n] * Mb[n-1] * ... * Mb[0] Where: Mi is the associated influence's matrix. Mb is a bone matrix. Mb[n] is the influence's associated bone matrix. Mb[n-1] is the matrix of the parent bone to Mb[n]. Mb[0] is the root of the tree Mb[n] is part of. The weights on each vert must be set up such that: - There is always at least one valid weight and influence index. - The weights must always add up to 1. - If a vertex has n influences and n is less than 4, it must use the first n elements of the weights and indices arrays to store the information. The remaining elements *must* be zero. - Weight/influence pairs are sorted such that the highest absolute weight value is first (at position 0), followed by the next highest, etc. */ const uint X42_IDENT = (('2' << 24) + ('4' << 16) + ('W' << 8) + 'H'); //this number is incremented whenever a breaking binary change is made to the core file const uint X42_VERSION = 14; inline uint X42_EXTENSION_BIT( int extNum ) { return 1 << extNum; } //extNum is 0..31 typedef float quat_t[4]; const uint X42_WEIGHTS_PER_VERT = 4; typedef vec_s16eb< 1 > vec1_sn; typedef vec_s16eb< 3 > vec3_sn; struct x42Header_t { uint ident; //must be X42_IDENT uint version; //must be X42_VERSION uint numFrames; uint numBones; //must be at least 1 uint numPosTrackElems; uint numRotTrackElems; uint numScaleTrackElems; uint centerBone; //used for culling info uint numInfluences; //must be at least 1 uint numTags; uint numGroups; uint numVerts; uint numIndices; uint extensions; uint extSizes[32]; //data sizes for each extension uint extFlags[32]; //misc flags for each extension }; const uint X42_MODEL_BONE = 0xFFFFFFFF; //this bone index is the logical equivalent of the model-to-world matrix DECLARE_ENUM( boneFlags_t ) None = 0x00000000, UseInvParentScale = 0x00000001, END_ENUM_WITH_TYPE( boneFlags_t, uint ); struct x42Bone_t { qname name; uint animGroup; //animation track sorting info uint parentIdx; //index of the parent bone, must be less than this bone's index //X42_MODEL_BONE denotes a top-level bone float extent; //the furthest reaches of this bone's influence boneFlags_t flags; uint posTrackIdx; //index to the first element of the bone's position track info uint posTrackLen; //number of elements in the position track, must be >= 1 uint rotTrackIdx; //index to the first element of the bone's rotation track info uint rotTrackLen; //number of elements in the rotation track, must be >= 1 uint scaleTrackIdx; //index to the first element of the bone's scale track info uint scaleTrackLen; //number of elements in the scale track, must be >= 1 }; struct x42Influence_t { uint bone; //indexes into the bones array affine_t meshToBone; //transforms points from bind pose into bone space }; struct x42VertAnim_t { vec3_t pos; byte idx[4]; float wt[4]; }; struct x42VertNormal_t { vec3_sn norm; short pad; }; struct x42VertTangent_t { vec3_sn tan; vec1_sn nfac0; vec3_sn bin; vec1_sn nfac1; //same as nfac0, just repeated }; struct x42VertAttrib_t { vec2_t tc; argb color; }; struct x42Tag_t { qname name; uint bone; affine_t tagMatrix; }; DECLARE_ENUM( primType_t ) Points = GL_POINTS, Lines = GL_LINES, LineStrip = GL_LINE_STRIP, Triangles = GL_TRIANGLES, TriangleStrip = GL_TRIANGLE_STRIP, TriangleFan = GL_TRIANGLE_FAN, //not supporting quads or polygon for DX portability reasons END_ENUM_WITH_TYPE_OPEN( primType_t, uint ) static inline uint GetNumPrims( primType_t primType, uint numIndices ) { if( numIndices == 0 ) return 0; switch( primType ) { case Points: return numIndices; case Lines: return numIndices / 2; case LineStrip: return numIndices - 1; case Triangles: return numIndices / 3; case TriangleStrip: case TriangleFan: return numIndices - 2; default: fail( "invalid prim type" ); return 0; //shut up the warning } } static inline uint GetNumIndices( primType_t primType, uint numPrims ) { if( numPrims == 0 ) return 0; switch( primType ) { case Points: return numPrims; case Lines: return numPrims * 2; case LineStrip: return numPrims + 1; case Triangles: return numPrims * 3; case TriangleStrip: case TriangleFan: return numPrims + 2; default: fail( "invalid prim type" ); return 0; //shut up the warning } } END_OPEN_ENUM( primType_t ); const uint X42_MAX_INFLUENCES_PER_BATCH = 30; //this can't exceed 0xFF struct x42Group_t { qname material; qname surfaceName; /* This is an optimization hint for the renderer. It is set to 1, 2, 3, or 4. If the value is one, then each vertex in this group will have one influence index in the first element of its indices array and the first element of its weights array will be set to 1.0F. The last three elements of its weights and indices arrays will be zero. If the value is two, then each vertex in this group will have two influence indices in the first two elements of its indices array and the first two elements of its weights array will add up to 1.0F. The last two elements of the its weights and indices arrays will be zero. And so on. */ uint maxVertInfluences; /* Indices into the global influences array. Note that vertices in this group index into *this* influence array, not the global one, that is to say that given: a vertex weighted against influence 0 and an influences array starting with 4, 3, 9, 0, 2... the vertex will be written with an influence index of 3 since influence 0 is loaded as the fourth (zero-indexed) influence of this group. */ uint numInfluences; uint influences[X42_MAX_INFLUENCES_PER_BATCH]; primType_t primType; //the type of primitive for this group /* These values define the part or the vertex and index array that belongs to this group. Note that in the case of primType_t::Points there is *no* index data: firstIndex gets ignored and the renderer draws numPrims verts starting at firstVert and going in sequence. This is a DX compatibility thing - get over it. */ uint firstVert; //index of the first vertex for this group uint numVerts; //number of vertices referenced in this group uint firstIndex; //index of the first index for this group, //if this is max_uint then the group's vertices aren't indexed uint numPrims; //number of primitives in this group }; #endif