/* -*- Objc -*- */ /* * $Id: MdlModel.m,v 1.1 2003/06/30 14:19:55 c-leo Exp $ * * Copyright (C) 2003 Free Software Foundation, Inc. * * This file is part of GNU Hégémonie. * * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include "Common/Coord.h" #include "Common/Camera.h" #include "Common/Quaternion.h" #include "Common/TextureLoader.h" #include "Model/MD3Data.h" #include "Model/MdlModel.h" #include "Model/MdlAnimation.h" @interface MdlModel (Private) - (void) _loadModel: (NSString *)fileMdl; - (void) _loadAnimations: (NSString *)fileCfg; - (unsigned) _mesh : (NSString *)name; - (GLuint) _texture: (int32_t)noMesh; - (void) _loadTextures: (TextureLoader *)texLoader withSkin: (NSString *)texName; - (void) _calcModelNbElements; - (void) _calcModelNbVertice; - (void) _calcModelVerticeIndices; - (void) _calcModelInfomations; - (void) _calcTextures; - (void) _calcNormales; @end @implementation MdlModel /** * This method builds an instance of MdlModel. * The parameter gives us files names to load model data, the model name MD3, * the config file name. The texture name is a SKIN file. It may exist, * otherwise textures are read in MD3 file. */ - (id) initWithName: (NSString *)modelName withTexName: (NSString *)texName withConfigName: (NSString *)cfgName { NSParameterAssert (modelName); NSParameterAssert ([modelName length] != 0); NSParameterAssert (cfgName); NSParameterAssert ([cfgName length] != 0); self = [super init]; if (self != nil) { [self _loadModel: modelName]; [self _loadAnimations: cfgName]; TextureLoader *textureLoader = [[TextureLoader alloc] init]; [self _loadTextures: textureLoader withSkin: texName]; } return self; } - (void) dealloc { free(_verticeIndices); free(_vertices); free(_texturesCoord); free(_normales); RELEASE(_data); RELEASE(_animations); [super dealloc]; } /** * This method loads data for the model. */ - (void) _loadModel: (NSString *)fileMdl { NSParameterAssert (fileMdl); NSParameterAssert ([fileMdl length] != 0); _data = [[MD3Data alloc] initWithFile: fileMdl]; if (_data == nil) { fprintf (stderr, "unknown file name %s \n", [fileMdl cString]); abort (); } [self _calcModelInfomations]; } /** * This method loads the model animations. The file is a ".cfg" file. */ - (void) _loadAnimations: (NSString *)fileCfg { NSParameterAssert (fileCfg); NSParameterAssert ([fileCfg length] != 0); NSDictionary *animsCfg = [NSDictionary dictionaryWithContentsOfFile: fileCfg]; _animations = [[NSMutableDictionary alloc] initWithCapacity: [animsCfg count]]; NSEnumerator *enumerator = [animsCfg keyEnumerator]; NSString *name; while ( (name = [enumerator nextObject]) ) { NSDictionary *animInfos = [animsCfg objectForKey: name]; MdlAnimation *anim = [MdlAnimation alloc] ; anim = [anim initWithAnimationName: name start: [[animInfos objectForKey: @"start"] intValue] nb: [[animInfos objectForKey: @"nb"] intValue] loop: [[animInfos objectForKey: @"loop"] intValue] frame: [[animInfos objectForKey: @"frame"] intValue]]; [_animations setObject: anim forKey: name]; } } /** * This method is used to load textures contained in the MD3Data. * The texture name is a SKIN file. It may exist, otherwise textures * are read in MD3 file. * The parameter texLoader contains textures we want to load. */ - (void) _loadTextures: (TextureLoader *)texLoader withSkin: (NSString *)texName { NSParameterAssert (texLoader); NSDictionary *tex; int32_t noMesh; GLuint texture; _textures = [NSMutableArray arrayWithCapacity: [_data nbMeshes]]; if (texName != nil && ([texName length] != 0)) { tex = [NSDictionary alloc]; if ([tex initWithContentsOfFile: texName] == nil) { fprintf (stderr, "unknown file name %s \n", [texName cString]); abort (); } NSArray *key = [tex allKeys]; for (noMesh = 0; noMesh < [_data nbMeshes]; noMesh++) { texture = [texLoader addTexture: [tex objectForKey: [key objectAtIndex: noMesh]]]; [_textures addObject: [NSNumber numberWithInt: texture]]; } } else { for (noMesh = 0; noMesh < [_data nbMeshes]; noMesh++) { NSString *name = [NSString stringWithCString: [_data meshTexName: noMesh withNumber: 0]]; if([name length] >= 2) { texture = [texLoader addTexture: name]; [_textures addObject: [NSNumber numberWithInt: texture]]; } else [_textures addObject: [NSNumber numberWithInt: 0]]; } } } /** * This private method gives number of element in model. * (for all meshes, it counts the number of vertice in it) */ - (void) _calcModelNbElements { int noMesh; _nbElements = 0; for (noMesh = 0; noMesh < [_data nbMeshes]; noMesh++) _nbElements += [_data meshNbTriangles: noMesh] * 3; } /** * This private methods gives number of vertice in model. */ - (void) _calcModelNbVertice { int noMesh; _nbVertices = 0; for (noMesh = 0; noMesh < [_data nbMeshes]; noMesh++) _nbVertices += [_data meshNbVertice: noMesh]; } /** * This private method returns in _verticeIndices all indices of vertice. */ - (void) _calcModelVerticeIndices { int noMesh; if ( !(_verticeIndices = malloc (_nbElements * sizeof (int32_t))) ) { perror ("Failled allocation verticeIndices"); abort (); } int cpt = 0; int meshOffset = 0; for (noMesh = 0; noMesh < [_data nbMeshes]; noMesh++) { int noTriangle; for (noTriangle = 0; noTriangle < [_data meshNbTriangles: noMesh]; noTriangle++) { triangle_t triangle = [_data meshTriangle: noMesh withNumber: noTriangle]; _verticeIndices[cpt] = triangle.vertex1 + meshOffset; _verticeIndices[cpt+1] = triangle.vertex2 + meshOffset; _verticeIndices[cpt+2] = triangle.vertex3 + meshOffset; cpt += 3; } meshOffset += [_data meshNbVertice: noMesh]; } } /** * This private method calculates number of vertice in model for all frames. */ - (void) _calcVertices { if ( !(_vertices = malloc (_nbVertices * [_data nbFrames] * sizeof (coord_t))) ) { perror ("Failed allocation vertices array"); abort (); } int noFrame; for (noFrame = 0; noFrame < [_data nbFrames]; noFrame++) { int meshOffset = 0; int noMesh; for (noMesh = 0; noMesh < [_data nbMeshes]; noMesh++) { int noVertex; for (noVertex = 0; noVertex < [_data meshNbVertice: noMesh]; noVertex++) { coord_t *pt = &_vertices[noVertex + meshOffset + noFrame * _nbVertices]; *pt = [_data meshCoordVertex: noMesh withNumber: noVertex inFrame: noFrame]; } meshOffset += [_data meshNbVertice: noMesh]; } } } /** * This private method stores all coordinates of texture in _texturesCoord */ - (void) _calcTextures { if ( !(_texturesCoord = malloc (_nbVertices * sizeof (tex_coord_t))) ) { perror ("Failed allocation textures array"); abort (); } int meshOffset = 0; int noMesh; for (noMesh = 0; noMesh < [_data nbMeshes]; noMesh++) { int noVertex; for (noVertex = 0; noVertex < [_data meshNbVertice: noMesh]; noVertex++) { tex_coord_t *pt = &_texturesCoord[noVertex + meshOffset]; *pt = [_data meshTexCoord: noMesh withNumber: noVertex]; } meshOffset += [_data meshNbVertice: noMesh]; } } /** * This private method stores data for normals in _normal. * (data used to model lighting) */ - (void) _calcNormales { if ( !(_normales = malloc (_nbVertices * [_data nbFrames] * sizeof (coord_t))) ) { perror ("Failed allocation _normales array"); abort (); } int noFrame; for (noFrame = 0; noFrame < [_data nbFrames]; noFrame++) { int meshOffset = 0; int noMesh; for (noMesh = 0; noMesh < [_data nbMeshes]; noMesh++) { int noVertex; for (noVertex = 0; noVertex < [_data meshNbVertice: noMesh]; noVertex++) { coord_t *pt = &_normales[noVertex + meshOffset + noFrame * _nbVertices]; *pt = [_data meshNormalVertex: noMesh withNumber: noVertex inFrame: noFrame]; } meshOffset += [_data meshNbVertice: noMesh]; } } } /** * this private method calls all calculs methods for information we want * to calculate at the beginning. */ - (void) _calcModelInfomations { [self _calcModelNbElements]; [self _calcModelVerticeIndices]; [self _calcModelNbVertice]; [self _calcVertices]; [self _calcNormales]; [self _calcTextures]; } /** * This is method which displays the 3d model. * It displays the animation at the frame given in parameter. * The parameter Camera is used to knowing which part of the model * we want to display. * The time intervenes while interpolating the frames. */ - (void) displayWithCamera: (Camera *) camera currentAnimation: (NSString *) name currentFrame: (unsigned) frame time: (double) time { NSParameterAssert (camera); NSParameterAssert (name); NSParameterAssert ([name length] != 0); MdlAnimation *animation = [self animation: name]; NSParameterAssert ( frame >= [animation startFrame]); NSParameterAssert ( frame < [animation startFrame] + [animation nbFrames]); GLfloat vertice[_nbVertices * 3* 2 + _nbVertices * 2 ]; int nextFrame = (frame+1 - [animation startFrame]) % [animation nbFrames] + [animation startFrame]; int noVertex; for (noVertex = 0; noVertex < _nbVertices; noVertex++) { coord_t currentNormal = _normales [noVertex + frame * _nbVertices]; coord_t nextNormal = _normales [noVertex + nextFrame * _nbVertices]; coord_t normal = InterpolateCoord (currentNormal, nextNormal, time); coord_t currentCoord = _vertices [noVertex + frame * _nbVertices]; coord_t nextCoord = _vertices [noVertex + nextFrame * _nbVertices]; coord_t coord = InterpolateCoord (currentCoord, nextCoord, time); tex_coord_t currentTexture = _texturesCoord [noVertex]; vertice[8*noVertex] = currentTexture.u; vertice[8*noVertex+1] = currentTexture.v; vertice[8*noVertex+2] = (normal.x); vertice[8*noVertex+3] = (normal.y); vertice[8*noVertex+4] = (normal.z); vertice[8*noVertex+5] = (coord.x); vertice[8*noVertex+6] = (coord.y); vertice[8*noVertex+7] = (coord.z); } glEnableClientState (GL_VERTEX_ARRAY); glInterleavedArrays(GL_T2F_N3F_V3F, 0, vertice); int ind = 0; int noMesh; for (noMesh = 0; noMesh < [_data nbMeshes] ; noMesh++) { glEnable(GL_NORMALIZE); int nbElements = [_data meshNbTriangles: noMesh]*3; glBindTexture(GL_TEXTURE_2D, [self _texture: noMesh]); glDrawElements (GL_TRIANGLES, nbElements, GL_UNSIGNED_INT, _verticeIndices + ind); glDisable(GL_NORMALIZE); ind += nbElements; } glDisableClientState (GL_VERTEX_ARRAY); } /** * Convertes 3*3 matrix in a 4*4 matrix . */ static float* m3x32m4x4 (const float *matrix) { float *res = (float*) malloc (16 * sizeof (float)); res[0] = matrix[0]; res[1] = matrix[1]; res[2] = matrix[2]; res[3] = 0; res[4] = matrix[3]; res[5] = matrix[4]; res[6] = matrix[5]; res[7] = 0; res[8] = matrix[6]; res[9] = matrix[7]; res[10] = matrix[8]; res[11] = 0; res[12] = 0; res[13] = 0; res[14] = 0; res[15] = 1; return res; } /** * This method prepares a tag of the 3d model . * It prepares the tag of the animation at the frame given in parameter. * The time intervenes while interpolating the frames. */ - (void) link: (NSString *)tagName currentAnimation: (NSString *) animName currentFrame: (unsigned) frame time: (double) time { NSParameterAssert (tagName); NSParameterAssert ([tagName length] != 0); NSParameterAssert (animName); NSParameterAssert ([animName length] != 0); NSParameterAssert (frame >= [[self animation: animName] startFrame]); NSParameterAssert (frame < [[self animation: animName] startFrame] + [[self animation: animName] nbFrames]); int frameNext = (frame+1 - [[self animation: animName] startFrame]) % [[self animation: animName] nbFrames] + [[self animation: animName] startFrame]; const float *tagMatrix = [_data tagOrientationMatrix: 0 inFrame: frame]; const float *tagMatrixNext = [_data tagOrientationMatrix: 0 inFrame: frameNext]; coord_t tagPosition = [_data tagPosition: 0 inFrame: frame]; coord_t tagPositionNext = [_data tagPosition: 0 inFrame: frameNext]; Quaternion *quat = [[Quaternion alloc] initWithMatrix: m3x32m4x4(tagMatrix)]; Quaternion *quatNext = [[Quaternion alloc] initWithMatrix: m3x32m4x4(tagMatrixNext)]; Quaternion *quatInterpolated = [[Quaternion alloc] init]; [quatInterpolated slerp: quat with: quatNext accordingTime: time]; float * matrix = [quatInterpolated createMatrix]; matrix[12] = tagPosition.x + time * (tagPositionNext.x - tagPosition.x); matrix[13] = tagPosition.y + time * (tagPositionNext.y - tagPosition.y); matrix[14] = tagPosition.z + time * (tagPositionNext.z - tagPosition.z); glMultMatrixf (matrix); } /** * This method gives us an access to animations. */ - (MdlAnimation *) animation: (NSString *)name { NSParameterAssert (name); NSParameterAssert ([name length] != 0); return [_animations objectForKey: name]; } /** * This method gives us an access to textures. */ - (GLuint) _texture: (int32_t)noMesh { NSParameterAssert (noMesh >= 0); NSParameterAssert (noMesh < [_data nbMeshes]); return (GLuint) [[_textures objectAtIndex: noMesh] intValue]; } @end /* MdlModel.m */