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