1 /**
2 * Credit for this code is mainly due to:
3 * DigiBen digiben@gametutorials.com
4 * Look up his other great tutorials at:
5 * http://www.gametutorials.com
6 *
7 * glCommands (and thus simplification of this file) is implemented
8 * thanks to David Henry tutorial :
9 * http://tfc.duke.free.fr/us/tutorials/models/md2.htm
10 */
11
12 #include "../common/constants.h"
13 #include "md2shape.h"
14 #include "Md2.h"
15 #include "modelwrapper.h"
16 #include <math.h>
17 #include <string>
18
19 using namespace std;
20
21 // Initializes the md2 structures
CLoadMD2()22 CLoadMD2::CLoadMD2() {
23 memset( &m_Header, 0, sizeof( tMd2Header ) );
24 m_pSkins = NULL;
25 m_pFrames = NULL;
26 }
27
28 // delete all structures associated with this model
DeleteMD2(t3DModel * pModel)29 void CLoadMD2::DeleteMD2( t3DModel *pModel ) {
30 //cerr << "Deleting MD2 model: animations: " << pModel->pAnimations.size() <<
31 // " materials: " << pModel->pMaterials.size() <<
32 //" objects: " << pModel->pObject.size() << endl;
33 pModel->pAnimations.clear();
34 pModel->pMaterials.clear();
35 pModel->pObject.clear();
36 delete[] pModel->vertices;
37 delete[] pModel->pGlCommands;
38 }
39
40 // Called by the client to open the .Md2 file, read it, then clean up
ImportMD2(t3DModel * pModel,string & strFileName)41 bool CLoadMD2::ImportMD2( t3DModel *pModel, string& strFileName ) {
42 // Open the MD2 file in binary
43 m_FilePointer = fopen( strFileName.c_str(), "rb" );
44 if ( !m_FilePointer ) {
45 cerr << "Unable to find the file: " << strFileName << "!" << endl;
46 exit( 1 );
47 }
48
49 // Read the header data
50 fread( &m_Header, 1, sizeof( tMd2Header ), m_FilePointer );
51 if ( SDL_BYTEORDER == SDL_BIG_ENDIAN ) {
52 unsigned int *p = ( unsigned int * ) & m_Header;
53 for ( unsigned int n = 0; n < sizeof( tMd2Header ) / sizeof( unsigned int ); n++ ) {
54 *p = SDL_SwapLE32( *p );
55 p++;
56 }
57 }
58
59 // Make sure version is '8'
60 if ( m_Header.version != 8 ) {
61 cerr << "Invalid file format (Version not 8): " << strFileName << "!" << endl;
62 exit( 1 );
63 }
64
65 // Read in the model and animation data
66 //memset(pModel, 0, sizeof(t3DModel));
67 ModelLoader::clearModel( pModel );
68 ReadMD2Data( pModel );
69
70 // Stores animation info
71 ParseAnimations( pModel );
72
73 // Computes min/max vertices values
74 ComputeMinMaxValues( pModel );
75
76 CleanUp();
77 return true;
78 }
79
80
81 // Reads in all of the model's data, except the animation frames
ReadMD2Data(t3DModel * pModel)82 void CLoadMD2::ReadMD2Data( t3DModel *pModel ) {
83 // Create a larger buffer for the frames of animation (not fully used yet)
84 unsigned char buffer[MD2_MAX_FRAMESIZE];
85 int k;
86
87 // Allocate all of our memory from the header's information
88 m_pSkins = new tMd2String [m_Header.numSkins];
89 m_pFrames = new tMd2String [m_Header.numFrames];
90
91 // Reads skin data
92 fseek( m_FilePointer, m_Header.offsetSkins, SEEK_SET );
93 fread( m_pSkins, sizeof( tMd2String ), m_Header.numSkins, m_FilePointer );
94
95 // pObjects will hold the key frames
96 pModel->numOfObjects = m_Header.numFrames;
97
98 // Read the glCommands
99 fseek( m_FilePointer, m_Header.offsetGlCommands, SEEK_SET );
100 pModel->pGlCommands = new int [m_Header.numGlCommands];
101 fread( pModel->pGlCommands, sizeof( int ), m_Header.numGlCommands, m_FilePointer );
102 if ( SDL_BYTEORDER == SDL_BIG_ENDIAN ) {
103 for ( int i = 0; i < m_Header.numGlCommands; i++ ) {
104 pModel->pGlCommands[i] = SDL_SwapLE32( pModel->pGlCommands[i] );
105 }
106 }
107
108 // set number of vertices of the model
109 pModel->numVertices = m_Header.numVertices;
110
111 // Extract and compute vertices for each frame of the model
112 fseek( m_FilePointer, m_Header.offsetFrames, SEEK_SET );
113 pModel->vertices = new vect3d[ m_Header.numVertices * m_Header.numFrames ];
114 for ( int i = 0; i < m_Header.numFrames; i++ ) {
115 tMd2AliasFrame *pFrame = ( tMd2AliasFrame * ) buffer;
116 fread( pFrame, 1, m_Header.frameSize, m_FilePointer );
117 if ( SDL_BYTEORDER == SDL_BIG_ENDIAN ) {
118 // Note: byteswapping floats works on my powerbook, but I think
119 // this may cause problems in the future. Floats and doubles aren't
120 // as easy to change from LE to BE as ints.
121 float f;
122 for ( int t = 0; t < 3; t++ ) {
123 BSWAPF( pFrame->scale[t], f );
124 pFrame->scale[t] = f;
125 BSWAPF( pFrame->translate[t], f );
126 pFrame->translate[t] = f;
127 }
128 }
129
130 strcpy( m_pFrames[i], pFrame->name );
131 k = m_Header.numVertices * i;
132
133 // Translate and scale each vertex
134 for ( int j = 0; j < m_Header.numVertices; j++ ) {
135 pModel->vertices[k + j][0] = pFrame->aliasVertices[j].vertex[0] * pFrame->scale[0] + pFrame->translate[0];
136 pModel->vertices[k + j][2] = -1 * ( pFrame->aliasVertices[j].vertex[1] * pFrame->scale[1] + pFrame->translate[1] );
137 pModel->vertices[k + j][1] = pFrame->aliasVertices[j].vertex[2] * pFrame->scale[2] + pFrame->translate[2];
138 }
139 }
140 }
141
142
143 // Fills in the animation list for each animation by name and frame
ParseAnimations(t3DModel * pModel)144 void CLoadMD2::ParseAnimations( t3DModel *pModel ) {
145 tAnimationInfo animation;
146 string strLastName = "";
147
148 // A hack because sometimes there is only 1 pain animation and/or
149 // 1 death animation.
150 int nbPain = 0;
151 int nbDeath = 0;
152 bool painDone = false;
153 bool deathDone = false;
154 tAnimationInfo painAnimation;
155 tAnimationInfo deathAnimation;
156 memset( &painAnimation, 0, sizeof( tAnimationInfo ) );
157 memset( &deathAnimation, 0, sizeof( tAnimationInfo ) );
158
159 // Go through all of the frames of animation and parse each animation
160 // There is a standard frame number for each animations of md2 files
161 // TODO : implement it (no need to look for the last frame of each animation,
162 // no need to store the number of frames for each animations ...),
163 // I(Daroth) will do it, someday...
164 for ( int i = 0; i < pModel->numOfObjects; i++ ) {
165 string strName = m_pFrames[i];
166 int frameNum = 0;
167
168 // Erase the frame numbers from the name
169 for ( int j = 0; j < static_cast<int>( strName.length() ); j++ ) {
170 // If the current index is a number and it's one of the last 2 characters
171 if ( isdigit( strName[j] ) && j >= static_cast<int>( strName.length() - 2 ) ) {
172 frameNum = atoi( &strName[j] );
173 strName.erase( j, strName.length() - j );
174 break;
175 }
176 }
177
178 // Check if this animation name is not the same as the last frame,
179 // or if we are on the last frame of animation for this model
180 if ( strName != strLastName || i == pModel->numOfObjects - 1 ) {
181 // If this animation frame is NOT the first frame
182 if ( strLastName != "" ) {
183 strcpy( animation.strName, strLastName.c_str() );
184 animation.endFrame = i;
185
186 // ARRG! Sometimes there is only 1 pain/death animation instead of 3
187 if ( strLastName.substr( 0, 4 ) == "pain" ) {
188 painAnimation.startFrame = animation.startFrame;
189 painAnimation.endFrame = animation.endFrame;
190 strcpy( painAnimation.strName, animation.strName );
191 nbPain++;
192 } else if ( nbPain >= 1 && nbPain < 3 && !painDone ) {
193 // insert 2 pain animation to keep
194 // md2 action <-> pModel->pAnimation bijection
195 pModel->pAnimations.push_back( painAnimation );
196 pModel->pAnimations.push_back( painAnimation );
197 pModel->numOfAnimations += 2;
198 painDone = true;
199 }
200 if ( strLastName.substr( 0, 5 ) == "death" ) {
201 deathAnimation.startFrame = animation.startFrame;
202 deathAnimation.endFrame = animation.endFrame;
203 strcpy( deathAnimation.strName, animation.strName );
204 nbDeath++;
205 } else if ( nbDeath >= 1 && nbDeath < 3 && !deathDone ) {
206 // insert 2 death animation to keep
207 // md2 action <-> pModel->pAnimation bijection
208 pModel->pAnimations.push_back( deathAnimation );
209 pModel->pAnimations.push_back( deathAnimation );
210 pModel->numOfAnimations += 2;
211 deathDone = true;
212 }
213
214 pModel->pAnimations.push_back( animation );
215 memset( &animation, 0, sizeof( tAnimationInfo ) );
216 pModel->numOfAnimations++;
217 }
218
219 // Set the starting frame number to the current frame number we just found,
220 // minus 1 (since 0 is the first frame) and add 'i'.
221 animation.startFrame = frameNum - 1 + i;
222 }
223
224 strLastName = strName;
225 }
226
227 // Death animations are at the end of the file
228 if ( nbDeath >= 1 && nbDeath < 3 && !deathDone ) {
229 // insert 2 death animation to keep
230 // md2 action <-> pModel->pAnimation bijection
231 pModel->pAnimations.push_back( deathAnimation );
232 pModel->pAnimations.push_back( deathAnimation );
233 pModel->numOfAnimations += 2;
234 }
235 }
236
ComputeMinMaxValues(t3DModel * pModel)237 void CLoadMD2::ComputeMinMaxValues( t3DModel *pModel ) {
238
239 // Find the lowest point
240 float minx, miny, minz;
241 float maxx, maxy, maxz;
242 minx = miny = minz = maxx = maxy = maxz = 0;
243 for ( int i = 0; i < pModel->numVertices ; i++ ) {
244 if ( pModel->vertices[i][0] > maxx ) maxx = pModel->vertices[i][0];
245 if ( pModel->vertices[i][1] > maxy ) maxy = pModel->vertices[i][1];
246 if ( pModel->vertices[i][2] > maxz ) maxz = pModel->vertices[i][2];
247 if ( pModel->vertices[i][0] < minx ) minx = pModel->vertices[i][0];
248 if ( pModel->vertices[i][1] < miny ) miny = pModel->vertices[i][1];
249 if ( pModel->vertices[i][2] < minz ) minz = pModel->vertices[i][2];
250 }
251 pModel->movex = maxx - minx;
252 pModel->movey = maxy;
253 pModel->movez = maxz - minz;
254
255 }
256
257 // Cleans up our allocated memory and closes the file
CleanUp()258 void CLoadMD2::CleanUp() {
259 fclose( m_FilePointer );
260 if ( m_pSkins ) delete [] m_pSkins; // Free the skins data
261 if ( m_pFrames ) delete [] m_pFrames; // Free the frames of animation
262 }
263
findBounds(t3DModel * pModel,vect3d min,vect3d max)264 void CLoadMD2::findBounds( t3DModel *pModel, vect3d min, vect3d max ) {
265 // mins/max-s
266 vect3d *point = &pModel->vertices[
267 pModel->numVertices *
268 pModel->pAnimations[MD2_STAND].startFrame ];
269 for ( int i = 0; i < pModel->numVertices; i++ ) {
270 for ( int t = 0; t < 3; t++ ) if ( point[i][t] < min[t] ) min[t] = point[i][t];
271 for ( int t = 0; t < 3; t++ ) if ( point[i][t] >= max[t] ) max[t] = point[i][t];
272 }
273 }
274
normalize(t3DModel * pModel,vect3d min,vect3d max)275 void CLoadMD2::normalize( t3DModel *pModel, vect3d min, vect3d max ) {
276 // normalize and center points
277 map<int, int> seenFrames;
278 // for(int r = 0; r < MD2_CREATURE_ACTION_COUNT; r++) {
279 for ( int r = 0; r < static_cast<int>( pModel->pAnimations.size() ); r++ ) {
280 for ( int a = pModel->pAnimations[r].startFrame; a < pModel->pAnimations[r].endFrame; a++ ) {
281 if ( seenFrames.find( a ) == seenFrames.end() ) {
282 vect3d *point = &pModel->vertices[ pModel->numVertices * a ];
283 for ( int i = 0; i < pModel->numVertices; i++ ) {
284 for ( int t = 0; t < 3; t++ ) point[i][t] -= min[t];
285 for ( int t = 0; t < 3; t++ ) if ( t != 1 ) point[i][t] -= ( max[t] / 2.0f );
286 }
287 seenFrames[a] = 1;
288 }
289 }
290 }
291 }
292
293