#define GL_GLEXT_PROTOTYPES #include "SDL_opengl.h" #include #include #include using namespace std; #include "tinyxml.h" #include "m3dmaterial.h" #include "m3dtexture.h" #include "m3dmesh.h" #include "extensions.h" /// Create a new empty mesh /** */ m3dMesh::m3dMesh() { verts = NULL; numVerts = 0; faces = NULL; numFaces = 0; materials = NULL; numMaterials = 0; textures = NULL; numTextures = 0; } /// Destroy this mesh /** */ m3dMesh::~m3dMesh() { delete[] verts; delete[] faces; delete[] materials; delete[] textures; } int m3dMesh::loadFromXML(const TiXmlElement *root) { if(string(root->Value()) != "Mesh") { fprintf(stderr, "Unknown node type: %s (required: %s)\n", root->Value(), "Mesh"); return -1; } // if(readTransformations(root) != 0) return -1; if(root->QueryIntAttribute("numVertices", &numVerts) != TIXML_SUCCESS) return -1; if(root->QueryIntAttribute("numFaces", &numFaces) != TIXML_SUCCESS) return -1; faces = new struct Face[numFaces]; verts = new struct Vertex[numVerts]; int f = 0, v = 0; numMaterials = 0; const TiXmlElement *element = root->FirstChildElement(); string value; while(element) { value = element->Value(); if(value == "Vertex") { if(parseVertex(element, &verts[v]) != 0 || v >= numVerts) { fprintf(stderr, "Invalid vertex!\n"); delete[] verts; delete[] faces; return -1; } v++; } else if(value == "Face") { if(parseFace(element, &faces[f]) != 0 || f >= numFaces) { fprintf(stderr, "Invalid face!\n"); delete[] verts; delete[] faces; return -1; } if(faces[f].material > numMaterials-1) numMaterials = faces[f].material+1; if(faces[f].texture > numTextures-1) numTextures = faces[f].texture+1; f++; } element = element->NextSiblingElement(); } materials = new m3dMaterial[numMaterials]; int mat = 0; element = root->FirstChildElement("Material"); while(element) { if(mat >= numMaterials) { fprintf(stderr, "Invalid mesh: incorrect number of materials!\n"); return -1; } materials[mat].loadFromXML(element); mat++; element = element->NextSiblingElement("Material"); } if(mat != numMaterials) { fprintf(stderr, "Invalid mesh: incorrect number of materials (wanted %d, got %d)!\n", numMaterials, mat); return -1; } textures = new m3dTexture[numTextures]; mat = 0; element = root->FirstChildElement("Texture"); while(element) { if(mat >= numTextures) { fprintf(stderr, "Invalid mesh: incorrect number of textures\n"); return -1; } textures[mat].loadFromXML(element); mat++; element = element->NextSiblingElement("Texture"); } if(mat != numTextures) { fprintf(stderr, "Invalid mesh: incorrect number of textures (wanted %d, got %d)!\n", numTextures, mat); return -1; } std::sort(faces, faces+numFaces, FaceSort()); return 0; } int m3dMesh::parseVertex(const TiXmlElement *root, struct Vertex *vert) { if(string(root->Value()) != "Vertex") { fprintf(stderr, "Unknown node type: %s (required: Vertex)\n", root->Value()); return -1; } if(root->QueryFloatAttribute("x", &vert->co[0]) != TIXML_SUCCESS) return -1; if(root->QueryFloatAttribute("y", &vert->co[1]) != TIXML_SUCCESS) return -1; if(root->QueryFloatAttribute("z", &vert->co[2]) != TIXML_SUCCESS) return -1; if(root->QueryFloatAttribute("nx", &vert->no[0]) != TIXML_SUCCESS) return -1; if(root->QueryFloatAttribute("ny", &vert->no[1]) != TIXML_SUCCESS) return -1; if(root->QueryFloatAttribute("nz", &vert->no[2]) != TIXML_SUCCESS) return -1; return 0; } int m3dMesh::parseFace(const TiXmlElement *root, struct Face *face) { if(string(root->Value()) != "Face") { fprintf(stderr, "Unknown node type: %s (required: Face)\n", root->Value()); return -1; } if(root->QueryIntAttribute("smooth", &face->smooth) != TIXML_SUCCESS) return -1; if(root->QueryFloatAttribute("nx", &face->no[0]) != TIXML_SUCCESS) return -1; if(root->QueryFloatAttribute("ny", &face->no[1]) != TIXML_SUCCESS) return -1; if(root->QueryFloatAttribute("nz", &face->no[2]) != TIXML_SUCCESS) return -1; if(root->QueryIntAttribute("material", &face->material) != TIXML_SUCCESS) return -1; if(root->QueryIntAttribute("texture", &face->texture) != TIXML_SUCCESS) return -1; const TiXmlElement *element = root->FirstChildElement("Vertex"); for(int i = 0; i < 3; i++) { if(element == NULL) { return -1; } if(element->QueryIntAttribute("index", &face->verts[i]) != TIXML_SUCCESS) return -1; if(element->QueryFloatAttribute("u", &face->uv[i][0]) == TIXML_WRONG_TYPE) return -1; if(element->QueryFloatAttribute("v", &face->uv[i][1]) == TIXML_WRONG_TYPE) return -1; element = element->NextSiblingElement("Vertex"); } return 0; } int m3dMesh::loadFromXML(const char *filename) { TiXmlDocument doc; if(!doc.LoadFile(filename)) return -1; return loadFromXML(doc.RootElement()); } const m3dTexture &m3dMesh::getTexture(int n) const { return textures[n]; } void m3dMesh::setTexture(int n, const m3dTexture& tex) { textures[n] = tex; } /// Draw the mesh /** Draws this mesh. No child objects are rendered, nor child lights are enabled. */ void m3dMesh::draw() { glPushMatrix(); // transform(); int prevTexture; int prevMaterial; int numTexUnits; if(faces[0].material != -1) { materials[faces[0].material].bind(); } prevMaterial = faces[0].material; if(faces[0].texture != -1) { textures[faces[0].texture].bind(); numTexUnits = textures[faces[0].texture].getNumTexUnits(); glEnable(GL_TEXTURE_2D); } else { glDisable(GL_TEXTURE_2D); numTexUnits = 0; } prevTexture = faces[0].texture; glBegin(GL_TRIANGLES); for(int i = 0; i < numFaces; i++) { struct Face *face; face = &faces[i]; if(prevMaterial != face->material || prevTexture != face->texture) glEnd(); if(prevMaterial != face->material) { if(face->material != -1) { materials[face->material].bind(); } else { m3dMaterial().bind(); } prevMaterial = face->material; if(prevTexture == face->texture) glBegin(GL_TRIANGLES); } if(prevTexture != face->texture) { if(face->texture != -1) { textures[face->texture].bind(); numTexUnits = textures[face->texture].getNumTexUnits(); if(prevTexture == -1) glEnable(GL_TEXTURE_2D); } else { numTexUnits = 0; glDisable(GL_TEXTURE_2D); } prevTexture = face->texture; glBegin(GL_TRIANGLES); } if(!face->smooth) { glNormal3fv(face->no); } for(int j = 0; j < 3; j++) { struct Vertex *vert; vert = &verts[face->verts[j]]; #ifdef HAVE_MULTITEX for(int t = 0; t < numTexUnits; t++) { mglMultiTexCoord2fv(GL_TEXTURE0_ARB + t, face->uv[j]); } #else glTexCoord2fv(face->uv[j]); #endif if(face->smooth) glNormal3fv(vert->no); glVertex3fv(vert->co); } } glEnd(); glPopMatrix(); }