dhewm3/neo/renderer/Model_ma.cpp
dhewg 736ec20d4d Untangle the epic precompiled.h mess
Don't include the lazy precompiled.h everywhere, only what's
required for the compilation unit.
platform.h needs to be included instead to provide all essential
defines and types.
All includes use the relative path to the neo or the game
specific root.
Move all idlib related includes from idlib/Lib.h to precompiled.h.
precompiled.h still exists for the MFC stuff in tools/.
Add some missing header guards.
2011-12-19 23:21:47 +01:00

1110 lines
28 KiB
C++

/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
Doom 3 Source Code 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 3 of the License, or
(at your option) any later version.
Doom 3 Source Code 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 Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#include "sys/platform.h"
#include "idlib/Parser.h"
#include "framework/Common.h"
#include "framework/FileSystem.h"
#include "framework/Session.h"
#include "renderer/Model_ma.h"
/*
======================================================================
Parses Maya ASCII files.
======================================================================
*/
#define MA_VERBOSE( x ) { if ( maGlobal.verbose ) { common->Printf x ; } }
// working variables used during parsing
typedef struct {
bool verbose;
maModel_t *model;
maObject_t *currentObject;
} ma_t;
static ma_t maGlobal;
void MA_ParseNodeHeader(idParser& parser, maNodeHeader_t* header) {
memset(header, 0, sizeof(maNodeHeader_t));
idToken token;
while(parser.ReadToken(&token)) {
if(!token.Icmp("-")) {
parser.ReadToken(&token);
if (!token.Icmp("n")) {
parser.ReadToken(&token);
strcpy(header->name, token.c_str());
} else if (!token.Icmp("p")) {
parser.ReadToken(&token);
strcpy(header->parent, token.c_str());
}
} else if (!token.Icmp(";")) {
break;
}
}
}
bool MA_ParseHeaderIndex(maAttribHeader_t* header, int& minIndex, int& maxIndex, const char* headerType, const char* skipString) {
idParser miniParse;
idToken token;
miniParse.LoadMemory(header->name, strlen(header->name), headerType);
if(skipString) {
miniParse.SkipUntilString(skipString);
}
if(!miniParse.SkipUntilString("[")) {
//This was just a header
return false;
}
minIndex = miniParse.ParseInt();
miniParse.ReadToken(&token);
if(!token.Icmp("]")) {
maxIndex = minIndex;
} else {
maxIndex = miniParse.ParseInt();
}
return true;
}
bool MA_ParseAttribHeader(idParser &parser, maAttribHeader_t* header) {
idToken token;
memset(header, 0, sizeof(maAttribHeader_t));
parser.ReadToken(&token);
if(!token.Icmp("-")) {
parser.ReadToken(&token);
if (!token.Icmp("s")) {
header->size = parser.ParseInt();
parser.ReadToken(&token);
}
}
strcpy(header->name, token.c_str());
return true;
}
bool MA_ReadVec3(idParser& parser, idVec3& vec) {
idToken token;
if(!parser.SkipUntilString("double3")) {
throw idException( va("Maya Loader '%s': Invalid Vec3", parser.GetFileName()) );
return false;
}
//We need to flip y and z because of the maya coordinate system
vec.x = parser.ParseFloat();
vec.z = parser.ParseFloat();
vec.y = parser.ParseFloat();
return true;
}
bool IsNodeComplete(idToken& token) {
if(!token.Icmp("createNode") || !token.Icmp("connectAttr") || !token.Icmp("select")) {
return true;
}
return false;
}
bool MA_ParseTransform(idParser& parser) {
maNodeHeader_t header;
maTransform_t* transform;
memset(&header, 0, sizeof(header));
//Allocate room for the transform
transform = (maTransform_t *)Mem_Alloc( sizeof( maTransform_t ) );
memset(transform, 0, sizeof(maTransform_t));
transform->scale.x = transform->scale.y = transform->scale.z = 1;
//Get the header info from the transform
MA_ParseNodeHeader(parser, &header);
//Read the transform attributes
idToken token;
while(parser.ReadToken(&token)) {
if(IsNodeComplete(token)) {
parser.UnreadToken(&token);
break;
}
if(!token.Icmp("setAttr")) {
parser.ReadToken(&token);
if(!token.Icmp(".t")) {
if(!MA_ReadVec3(parser, transform->translate)) {
return false;
}
transform->translate.y *= -1;
} else if (!token.Icmp(".r")) {
if(!MA_ReadVec3(parser, transform->rotate)) {
return false;
}
} else if (!token.Icmp(".s")) {
if(!MA_ReadVec3(parser, transform->scale)) {
return false;
}
} else {
parser.SkipRestOfLine();
}
}
}
if(header.parent[0] != 0) {
//Find the parent
maTransform_t** parent;
maGlobal.model->transforms.Get(header.parent, &parent);
if(parent) {
transform->parent = *parent;
}
}
//Add this transform to the list
maGlobal.model->transforms.Set(header.name, transform);
return true;
}
bool MA_ParseVertex(idParser& parser, maAttribHeader_t* header) {
maMesh_t* pMesh = &maGlobal.currentObject->mesh;
idToken token;
//Allocate enough space for all the verts if this is the first attribute for verticies
if(!pMesh->vertexes) {
pMesh->numVertexes = header->size;
pMesh->vertexes = (idVec3 *)Mem_Alloc( sizeof( idVec3 ) * pMesh->numVertexes );
}
//Get the start and end index for this attribute
int minIndex, maxIndex;
if(!MA_ParseHeaderIndex(header, minIndex, maxIndex, "VertexHeader", NULL)) {
//This was just a header
return true;
}
//Read each vert
for(int i = minIndex; i <= maxIndex; i++) {
pMesh->vertexes[i].x = parser.ParseFloat();
pMesh->vertexes[i].z = parser.ParseFloat();
pMesh->vertexes[i].y = -parser.ParseFloat();
}
return true;
}
bool MA_ParseVertexTransforms(idParser& parser, maAttribHeader_t* header) {
maMesh_t* pMesh = &maGlobal.currentObject->mesh;
idToken token;
//Allocate enough space for all the verts if this is the first attribute for verticies
if(!pMesh->vertTransforms) {
if(header->size == 0) {
header->size = 1;
}
pMesh->numVertTransforms = header->size;
pMesh->vertTransforms = (idVec4 *)Mem_Alloc( sizeof( idVec4 ) * pMesh->numVertTransforms );
pMesh->nextVertTransformIndex = 0;
}
//Get the start and end index for this attribute
int minIndex, maxIndex;
if(!MA_ParseHeaderIndex(header, minIndex, maxIndex, "VertexTransformHeader", NULL)) {
//This was just a header
return true;
}
parser.ReadToken(&token);
if(!token.Icmp("-")) {
idToken tk2;
parser.ReadToken(&tk2);
if(!tk2.Icmp("type")) {
parser.SkipUntilString("float3");
} else {
parser.UnreadToken(&tk2);
parser.UnreadToken(&token);
}
} else {
parser.UnreadToken(&token);
}
//Read each vert
for(int i = minIndex; i <= maxIndex; i++) {
pMesh->vertTransforms[pMesh->nextVertTransformIndex].x = parser.ParseFloat();
pMesh->vertTransforms[pMesh->nextVertTransformIndex].z = parser.ParseFloat();
pMesh->vertTransforms[pMesh->nextVertTransformIndex].y = -parser.ParseFloat();
//w hold the vert index
pMesh->vertTransforms[pMesh->nextVertTransformIndex].w = i;
pMesh->nextVertTransformIndex++;
}
return true;
}
bool MA_ParseEdge(idParser& parser, maAttribHeader_t* header) {
maMesh_t* pMesh = &maGlobal.currentObject->mesh;
idToken token;
//Allocate enough space for all the verts if this is the first attribute for verticies
if(!pMesh->edges) {
pMesh->numEdges = header->size;
pMesh->edges = (idVec3 *)Mem_Alloc( sizeof( idVec3 ) * pMesh->numEdges );
}
//Get the start and end index for this attribute
int minIndex, maxIndex;
if(!MA_ParseHeaderIndex(header, minIndex, maxIndex, "EdgeHeader", NULL)) {
//This was just a header
return true;
}
//Read each vert
for(int i = minIndex; i <= maxIndex; i++) {
pMesh->edges[i].x = parser.ParseFloat();
pMesh->edges[i].y = parser.ParseFloat();
pMesh->edges[i].z = parser.ParseFloat();
}
return true;
}
bool MA_ParseNormal(idParser& parser, maAttribHeader_t* header) {
maMesh_t* pMesh = &maGlobal.currentObject->mesh;
idToken token;
//Allocate enough space for all the verts if this is the first attribute for verticies
if(!pMesh->normals) {
pMesh->numNormals = header->size;
pMesh->normals = (idVec3 *)Mem_Alloc( sizeof( idVec3 ) * pMesh->numNormals );
}
//Get the start and end index for this attribute
int minIndex, maxIndex;
if(!MA_ParseHeaderIndex(header, minIndex, maxIndex, "NormalHeader", NULL)) {
//This was just a header
return true;
}
parser.ReadToken(&token);
if(!token.Icmp("-")) {
idToken tk2;
parser.ReadToken(&tk2);
if(!tk2.Icmp("type")) {
parser.SkipUntilString("float3");
} else {
parser.UnreadToken(&tk2);
parser.UnreadToken(&token);
}
} else {
parser.UnreadToken(&token);
}
//Read each vert
for(int i = minIndex; i <= maxIndex; i++) {
pMesh->normals[i].x = parser.ParseFloat();
//Adjust the normals for the change in coordinate systems
pMesh->normals[i].z = parser.ParseFloat();
pMesh->normals[i].y = -parser.ParseFloat();
pMesh->normals[i].Normalize();
}
pMesh->normalsParsed = true;
pMesh->nextNormal = 0;
return true;
}
bool MA_ParseFace(idParser& parser, maAttribHeader_t* header) {
maMesh_t* pMesh = &maGlobal.currentObject->mesh;
idToken token;
//Allocate enough space for all the verts if this is the first attribute for verticies
if(!pMesh->faces) {
pMesh->numFaces = header->size;
pMesh->faces = (maFace_t *)Mem_Alloc( sizeof( maFace_t ) * pMesh->numFaces );
}
//Get the start and end index for this attribute
int minIndex, maxIndex;
if(!MA_ParseHeaderIndex(header, minIndex, maxIndex, "FaceHeader", NULL)) {
//This was just a header
return true;
}
//Read the face data
int currentFace = minIndex-1;
while(parser.ReadToken(&token)) {
if(IsNodeComplete(token)) {
parser.UnreadToken(&token);
break;
}
if(!token.Icmp("f")) {
int count = parser.ParseInt();
if(count != 3) {
throw idException(va("Maya Loader '%s': Face is not a triangle.", parser.GetFileName()));
return false;
}
//Increment the face number because a new face always starts with an "f" token
currentFace++;
//We cannot reorder edges until later because the normal processing
//assumes the edges are in the original order
pMesh->faces[currentFace].edge[0] = parser.ParseInt();
pMesh->faces[currentFace].edge[1] = parser.ParseInt();
pMesh->faces[currentFace].edge[2] = parser.ParseInt();
//Some more init stuff
pMesh->faces[currentFace].vertexColors[0] = pMesh->faces[currentFace].vertexColors[1] = pMesh->faces[currentFace].vertexColors[2] = -1;
} else if(!token.Icmp("mu")) {
/* int uvstIndex = */ parser.ParseInt();
int count = parser.ParseInt();
if(count != 3) {
throw idException(va("Maya Loader '%s': Invalid texture coordinates.", parser.GetFileName()));
return false;
}
pMesh->faces[currentFace].tVertexNum[0] = parser.ParseInt();
pMesh->faces[currentFace].tVertexNum[1] = parser.ParseInt();
pMesh->faces[currentFace].tVertexNum[2] = parser.ParseInt();
} else if(!token.Icmp("mf")) {
int count = parser.ParseInt();
if(count != 3) {
throw idException(va("Maya Loader '%s': Invalid texture coordinates.", parser.GetFileName()));
return false;
}
pMesh->faces[currentFace].tVertexNum[0] = parser.ParseInt();
pMesh->faces[currentFace].tVertexNum[1] = parser.ParseInt();
pMesh->faces[currentFace].tVertexNum[2] = parser.ParseInt();
} else if(!token.Icmp("fc")) {
int count = parser.ParseInt();
if(count != 3) {
throw idException(va("Maya Loader '%s': Invalid vertex color.", parser.GetFileName()));
return false;
}
pMesh->faces[currentFace].vertexColors[0] = parser.ParseInt();
pMesh->faces[currentFace].vertexColors[1] = parser.ParseInt();
pMesh->faces[currentFace].vertexColors[2] = parser.ParseInt();
}
}
return true;
}
bool MA_ParseColor(idParser& parser, maAttribHeader_t* header) {
maMesh_t* pMesh = &maGlobal.currentObject->mesh;
idToken token;
//Allocate enough space for all the verts if this is the first attribute for verticies
if(!pMesh->colors) {
pMesh->numColors = header->size;
pMesh->colors = (byte *)Mem_Alloc( sizeof( byte ) * pMesh->numColors * 4 );
}
//Get the start and end index for this attribute
int minIndex, maxIndex;
if(!MA_ParseHeaderIndex(header, minIndex, maxIndex, "ColorHeader", NULL)) {
//This was just a header
return true;
}
//Read each vert
for(int i = minIndex; i <= maxIndex; i++) {
pMesh->colors[i*4] = parser.ParseFloat() * 255;
pMesh->colors[i*4+1] = parser.ParseFloat() * 255;
pMesh->colors[i*4+2] = parser.ParseFloat() * 255;
pMesh->colors[i*4+3] = parser.ParseFloat() * 255;
}
return true;
}
bool MA_ParseTVert(idParser& parser, maAttribHeader_t* header) {
maMesh_t* pMesh = &maGlobal.currentObject->mesh;
idToken token;
//This is not the texture coordinates. It is just the name so ignore it
if(strstr(header->name, "uvsn")) {
return true;
}
//Allocate enough space for all the data
if(!pMesh->tvertexes) {
pMesh->numTVertexes = header->size;
pMesh->tvertexes = (idVec2 *)Mem_Alloc( sizeof( idVec2 ) * pMesh->numTVertexes );
}
//Get the start and end index for this attribute
int minIndex, maxIndex;
if(!MA_ParseHeaderIndex(header, minIndex, maxIndex, "TextureCoordHeader", "uvsp")) {
//This was just a header
return true;
}
parser.ReadToken(&token);
if(!token.Icmp("-")) {
idToken tk2;
parser.ReadToken(&tk2);
if(!tk2.Icmp("type")) {
parser.SkipUntilString("float2");
} else {
parser.UnreadToken(&tk2);
parser.UnreadToken(&token);
}
} else {
parser.UnreadToken(&token);
}
//Read each tvert
for(int i = minIndex; i <= maxIndex; i++) {
pMesh->tvertexes[i].x = parser.ParseFloat();
pMesh->tvertexes[i].y = 1.0f - parser.ParseFloat();
}
return true;
}
/*
* Quick check to see if the vert participates in a shared normal
*/
bool MA_QuickIsVertShared(int faceIndex, int vertIndex) {
maMesh_t* pMesh = &maGlobal.currentObject->mesh;
int vertNum = pMesh->faces[faceIndex].vertexNum[vertIndex];
for( int i = 0; i < 3; i++) {
int edge = pMesh->faces[faceIndex].edge[i];
if(edge < 0) {
edge = idMath::Fabs(edge)-1;
}
if(pMesh->edges[edge].z == 1 && (pMesh->edges[edge].x == vertNum || pMesh->edges[edge].y == vertNum)) {
return true;
}
}
return false;
}
void MA_GetSharedFace(int faceIndex, int vertIndex, int& sharedFace, int& sharedVert) {
maMesh_t* pMesh = &maGlobal.currentObject->mesh;
int vertNum = pMesh->faces[faceIndex].vertexNum[vertIndex];
sharedFace = -1;
sharedVert = -1;
//Find a shared edge on this face that contains the specified vert
for(int edgeIndex = 0; edgeIndex < 3; edgeIndex++) {
int edge = pMesh->faces[faceIndex].edge[edgeIndex];
if(edge < 0) {
edge = idMath::Fabs(edge)-1;
}
if(pMesh->edges[edge].z == 1 && (pMesh->edges[edge].x == vertNum || pMesh->edges[edge].y == vertNum)) {
for(int i = 0; i < faceIndex; i++) {
for(int j = 0; j < 3; j++) {
if(pMesh->faces[i].vertexNum[j] == vertNum) {
sharedFace = i;
sharedVert = j;
break;
}
}
}
}
if(sharedFace != -1)
break;
}
}
void MA_ParseMesh(idParser& parser) {
maObject_t *object;
object = (maObject_t *)Mem_Alloc( sizeof( maObject_t ) );
memset( object, 0, sizeof( maObject_t ) );
maGlobal.model->objects.Append( object );
maGlobal.currentObject = object;
object->materialRef = -1;
//Get the header info from the mesh
maNodeHeader_t header;
MA_ParseNodeHeader(parser, &header);
//Find my parent
if(header.parent[0] != 0) {
//Find the parent
maTransform_t** parent;
maGlobal.model->transforms.Get(header.parent, &parent);
if(parent) {
maGlobal.currentObject->mesh.transform = *parent;
}
}
strcpy(object->name, header.name);
//Read the transform attributes
idToken token;
while(parser.ReadToken(&token)) {
if(IsNodeComplete(token)) {
parser.UnreadToken(&token);
break;
}
if(!token.Icmp("setAttr")) {
maAttribHeader_t header;
MA_ParseAttribHeader(parser, &header);
if(strstr(header.name, ".vt")) {
MA_ParseVertex(parser, &header);
} else if (strstr(header.name, ".ed")) {
MA_ParseEdge(parser, &header);
} else if (strstr(header.name, ".pt")) {
MA_ParseVertexTransforms(parser, &header);
} else if (strstr(header.name, ".n")) {
MA_ParseNormal(parser, &header);
} else if (strstr(header.name, ".fc")) {
MA_ParseFace(parser, &header);
} else if (strstr(header.name, ".clr")) {
MA_ParseColor(parser, &header);
} else if (strstr(header.name, ".uvst")) {
MA_ParseTVert(parser, &header);
} else {
parser.SkipRestOfLine();
}
}
}
maMesh_t* pMesh = &maGlobal.currentObject->mesh;
//Get the verts from the edge
for(int i = 0; i < pMesh->numFaces; i++) {
for(int j = 0; j < 3; j++) {
int edge = pMesh->faces[i].edge[j];
if(edge < 0) {
edge = idMath::Fabs(edge)-1;
pMesh->faces[i].vertexNum[j] = pMesh->edges[edge].y;
} else {
pMesh->faces[i].vertexNum[j] = pMesh->edges[edge].x;
}
}
}
//Get the normals
if(pMesh->normalsParsed) {
for(int i = 0; i < pMesh->numFaces; i++) {
for(int j = 0; j < 3; j++) {
//Is this vertex shared
int sharedFace = -1;
int sharedVert = -1;
if(MA_QuickIsVertShared(i, j)) {
MA_GetSharedFace(i, j, sharedFace, sharedVert);
}
if(sharedFace != -1) {
//Get the normal from the share
pMesh->faces[i].vertexNormals[j] = pMesh->faces[sharedFace].vertexNormals[sharedVert];
} else {
//The vertex is not shared so get the next normal
if(pMesh->nextNormal >= pMesh->numNormals) {
//We are using more normals than exist
throw idException(va("Maya Loader '%s': Invalid Normals Index.", parser.GetFileName()));
}
pMesh->faces[i].vertexNormals[j] = pMesh->normals[pMesh->nextNormal];
pMesh->nextNormal++;
}
}
}
}
//Now that the normals are good...lets reorder the verts to make the tris face the right way
for(int i = 0; i < pMesh->numFaces; i++) {
int tmp = pMesh->faces[i].vertexNum[1];
pMesh->faces[i].vertexNum[1] = pMesh->faces[i].vertexNum[2];
pMesh->faces[i].vertexNum[2] = tmp;
idVec3 tmpVec = pMesh->faces[i].vertexNormals[1];
pMesh->faces[i].vertexNormals[1] = pMesh->faces[i].vertexNormals[2];
pMesh->faces[i].vertexNormals[2] = tmpVec;
tmp = pMesh->faces[i].tVertexNum[1];
pMesh->faces[i].tVertexNum[1] = pMesh->faces[i].tVertexNum[2];
pMesh->faces[i].tVertexNum[2] = tmp;
tmp = pMesh->faces[i].vertexColors[1];
pMesh->faces[i].vertexColors[1] = pMesh->faces[i].vertexColors[2];
pMesh->faces[i].vertexColors[2] = tmp;
}
//Now apply the pt transformations
for(int i = 0; i < pMesh->numVertTransforms; i++) {
pMesh->vertexes[(int)pMesh->vertTransforms[i].w] += pMesh->vertTransforms[i].ToVec3();
}
MA_VERBOSE((va("MESH %s - parent %s\n", header.name, header.parent)));
MA_VERBOSE((va("\tverts:%d\n",maGlobal.currentObject->mesh.numVertexes)));
MA_VERBOSE((va("\tfaces:%d\n",maGlobal.currentObject->mesh.numFaces)));
}
void MA_ParseFileNode(idParser& parser) {
//Get the header info from the node
maNodeHeader_t header;
MA_ParseNodeHeader(parser, &header);
//Read the transform attributes
idToken token;
while(parser.ReadToken(&token)) {
if(IsNodeComplete(token)) {
parser.UnreadToken(&token);
break;
}
if(!token.Icmp("setAttr")) {
maAttribHeader_t attribHeader;
MA_ParseAttribHeader(parser, &attribHeader);
if(strstr(attribHeader.name, ".ftn")) {
parser.SkipUntilString("string");
parser.ReadToken(&token);
if(!token.Icmp("(")) {
parser.ReadToken(&token);
}
maFileNode_t* fileNode;
fileNode = (maFileNode_t*)Mem_Alloc( sizeof( maFileNode_t ) );
strcpy(fileNode->name, header.name);
strcpy(fileNode->path, token.c_str());
maGlobal.model->fileNodes.Set(fileNode->name, fileNode);
} else {
parser.SkipRestOfLine();
}
}
}
}
void MA_ParseMaterialNode(idParser& parser) {
//Get the header info from the node
maNodeHeader_t header;
MA_ParseNodeHeader(parser, &header);
maMaterialNode_t* matNode;
matNode = (maMaterialNode_t*)Mem_Alloc( sizeof( maMaterialNode_t ) );
memset(matNode, 0, sizeof(maMaterialNode_t));
strcpy(matNode->name, header.name);
maGlobal.model->materialNodes.Set(matNode->name, matNode);
}
void MA_ParseCreateNode(idParser& parser) {
idToken token;
parser.ReadToken(&token);
if(!token.Icmp("transform")) {
MA_ParseTransform(parser);
} else if(!token.Icmp("mesh")) {
MA_ParseMesh(parser);
} else if(!token.Icmp("file")) {
MA_ParseFileNode(parser);
} else if(!token.Icmp("shadingEngine") || !token.Icmp("lambert") || !token.Icmp("phong") || !token.Icmp("blinn") ) {
MA_ParseMaterialNode(parser);
}
}
int MA_AddMaterial(const char* materialName) {
maMaterialNode_t** destNode;
maGlobal.model->materialNodes.Get(materialName, &destNode);
if(destNode) {
maMaterialNode_t* matNode = *destNode;
//Iterate down the tree until we get a file
while(matNode && !matNode->file) {
matNode = matNode->child;
}
if(matNode && matNode->file) {
//Got the file
maMaterial_t *material;
material = (maMaterial_t *)Mem_Alloc( sizeof( maMaterial_t ) );
memset( material, 0, sizeof( maMaterial_t ) );
//Remove the OS stuff
idStr qPath;
qPath = fileSystem->OSPathToRelativePath( matNode->file->path );
strcpy(material->name, qPath.c_str());
maGlobal.model->materials.Append( material );
return maGlobal.model->materials.Num()-1;
}
}
return -1;
}
bool MA_ParseConnectAttr(idParser& parser) {
idStr temp;
idStr srcName;
idStr srcType;
idStr destName;
idStr destType;
idToken token;
parser.ReadToken(&token);
temp = token;
int dot = temp.Find(".");
if(dot == -1) {
throw idException(va("Maya Loader '%s': Invalid Connect Attribute.", parser.GetFileName()));
return false;
}
srcName = temp.Left(dot);
srcType = temp.Right(temp.Length()-dot-1);
parser.ReadToken(&token);
temp = token;
dot = temp.Find(".");
if(dot == -1) {
throw idException(va("Maya Loader '%s': Invalid Connect Attribute.", parser.GetFileName()));
return false;
}
destName = temp.Left(dot);
destType = temp.Right(temp.Length()-dot-1);
if(srcType.Find("oc") != -1) {
//Is this attribute a material node attribute
maMaterialNode_t** matNode;
maGlobal.model->materialNodes.Get(srcName, &matNode);
if(matNode) {
maMaterialNode_t** destNode;
maGlobal.model->materialNodes.Get(destName, &destNode);
if(destNode) {
(*destNode)->child = *matNode;
}
}
//Is this attribute a file node
maFileNode_t** fileNode;
maGlobal.model->fileNodes.Get(srcName, &fileNode);
if(fileNode) {
maMaterialNode_t** destNode;
maGlobal.model->materialNodes.Get(destName, &destNode);
if(destNode) {
(*destNode)->file = *fileNode;
}
}
}
if(srcType.Find("iog") != -1) {
//Is this an attribute for one of our meshes
for(int i = 0; i < maGlobal.model->objects.Num(); i++) {
if(!strcmp(maGlobal.model->objects[i]->name, srcName)) {
//maGlobal.model->objects[i]->materialRef = MA_AddMaterial(destName);
strcpy(maGlobal.model->objects[i]->materialName, destName);
break;
}
}
}
return true;
}
void MA_BuildScale(idMat4& mat, float x, float y, float z) {
mat.Identity();
mat[0][0] = x;
mat[1][1] = y;
mat[2][2] = z;
}
void MA_BuildAxisRotation(idMat4& mat, float ang, int axis) {
float sinAng = idMath::Sin(ang);
float cosAng = idMath::Cos(ang);
mat.Identity();
switch(axis) {
case 0: //x
mat[1][1] = cosAng;
mat[1][2] = sinAng;
mat[2][1] = -sinAng;
mat[2][2] = cosAng;
break;
case 1: //y
mat[0][0] = cosAng;
mat[0][2] = -sinAng;
mat[2][0] = sinAng;
mat[2][2] = cosAng;
break;
case 2://z
mat[0][0] = cosAng;
mat[0][1] = sinAng;
mat[1][0] = -sinAng;
mat[1][1] = cosAng;
break;
}
}
void MA_ApplyTransformation(maModel_t *model) {
for(int i = 0; i < model->objects.Num(); i++) {
maMesh_t* mesh = &model->objects[i]->mesh;
maTransform_t* transform = mesh->transform;
while(transform) {
idMat4 rotx, roty, rotz;
idMat4 scale;
rotx.Identity();
roty.Identity();
rotz.Identity();
if(fabs(transform->rotate.x) > 0.0f) {
MA_BuildAxisRotation(rotx, DEG2RAD(-transform->rotate.x), 0);
}
if(fabs(transform->rotate.y) > 0.0f) {
MA_BuildAxisRotation(roty, DEG2RAD(transform->rotate.y), 1);
}
if(fabs(transform->rotate.z) > 0.0f) {
MA_BuildAxisRotation(rotz, DEG2RAD(-transform->rotate.z), 2);
}
MA_BuildScale(scale, transform->scale.x, transform->scale.y, transform->scale.z);
//Apply the transformation to each vert
for(int j = 0; j < mesh->numVertexes; j++) {
mesh->vertexes[j] = scale * mesh->vertexes[j];
mesh->vertexes[j] = rotx * mesh->vertexes[j];
mesh->vertexes[j] = rotz * mesh->vertexes[j];
mesh->vertexes[j] = roty * mesh->vertexes[j];
mesh->vertexes[j] = mesh->vertexes[j] + transform->translate;
}
transform = transform->parent;
}
}
}
/*
=================
MA_Parse
=================
*/
maModel_t *MA_Parse( const char *buffer, const char* filename, bool verbose ) {
memset( &maGlobal, 0, sizeof( maGlobal ) );
maGlobal.verbose = verbose;
maGlobal.currentObject = NULL;
// NOTE: using new operator because aseModel_t contains idList class objects
maGlobal.model = new maModel_t;
maGlobal.model->objects.Resize( 32, 32 );
maGlobal.model->materials.Resize( 32, 32 );
idParser parser;
parser.SetFlags(LEXFL_NOSTRINGCONCAT);
parser.LoadMemory(buffer, strlen(buffer), filename);
idToken token;
while(parser.ReadToken(&token)) {
if(!token.Icmp("createNode")) {
MA_ParseCreateNode(parser);
} else if(!token.Icmp("connectAttr")) {
MA_ParseConnectAttr(parser);
}
}
//Resolve The Materials
for(int i = 0; i < maGlobal.model->objects.Num(); i++) {
maGlobal.model->objects[i]->materialRef = MA_AddMaterial(maGlobal.model->objects[i]->materialName);
}
//Apply Transformation
MA_ApplyTransformation(maGlobal.model);
return maGlobal.model;
}
/*
=================
MA_Load
=================
*/
maModel_t *MA_Load( const char *fileName ) {
char *buf;
ID_TIME_T timeStamp;
maModel_t *ma;
fileSystem->ReadFile( fileName, (void **)&buf, &timeStamp );
if ( !buf ) {
return NULL;
}
try {
ma = MA_Parse( buf, fileName, false );
ma->timeStamp = timeStamp;
} catch( idException &e ) {
common->Warning("%s", e.error);
if(maGlobal.model) {
MA_Free(maGlobal.model);
}
ma = NULL;
}
fileSystem->FreeFile( buf );
return ma;
}
/*
=================
MA_Free
=================
*/
void MA_Free( maModel_t *ma ) {
int i;
maObject_t *obj;
maMesh_t *mesh;
maMaterial_t *material;
if ( !ma ) {
return;
}
for ( i = 0; i < ma->objects.Num(); i++ ) {
obj = ma->objects[i];
// free the base nesh
mesh = &obj->mesh;
if ( mesh->vertexes ) {
Mem_Free( mesh->vertexes );
}
if ( mesh->vertTransforms ) {
Mem_Free( mesh->vertTransforms );
}
if ( mesh->normals ) {
Mem_Free( mesh->normals );
}
if ( mesh->tvertexes ) {
Mem_Free( mesh->tvertexes );
}
if ( mesh->edges ) {
Mem_Free( mesh->edges );
}
if ( mesh->colors ) {
Mem_Free( mesh->colors );
}
if ( mesh->faces ) {
Mem_Free( mesh->faces );
}
Mem_Free( obj );
}
ma->objects.Clear();
for ( i = 0; i < ma->materials.Num(); i++ ) {
material = ma->materials[i];
Mem_Free( material );
}
ma->materials.Clear();
maTransform_t** trans;
for ( i = 0; i < ma->transforms.Num(); i++ ) {
trans = ma->transforms.GetIndex(i);
Mem_Free( *trans );
}
ma->transforms.Clear();
maFileNode_t** fileNode;
for ( i = 0; i < ma->fileNodes.Num(); i++ ) {
fileNode = ma->fileNodes.GetIndex(i);
Mem_Free( *fileNode );
}
ma->fileNodes.Clear();
maMaterialNode_t** matNode;
for ( i = 0; i < ma->materialNodes.Num(); i++ ) {
matNode = ma->materialNodes.GetIndex(i);
Mem_Free( *matNode );
}
ma->materialNodes.Clear();
delete ma;
}