/* orxonox - the future of 3D-vertical-scrollers Copyright (C) 2004 orx 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, or (at your option) any later version. ### File Specific: main-programmer: Benjamin Grauer co-programmer: ... */ int verbose = 0; #include "object.h" /** \brief Creates a 3D-Object, but does not load any 3D-models pretty useless */ Object::Object () { initialize(); BoxObject(); finalize(); } /** \brief Crates a 3D-Object and loads in a File \param fileName file to parse and load (must be a .obj file) */ Object::Object(char* fileName) { initialize(); importFile (fileName); finalize(); } /** \brief Crates a 3D-Object, loads in a File and scales it. \param fileName file to parse and load (must be a .obj file) \param scaling The factor that the object will be scaled with. */ Object::Object(char* fileName, float scaling) { initialize(); scaleFactor = scaling; importFile (fileName); finalize(); } /** \brief deletes an Object */ Object::~Object() { if (verbose >= 2) printf ("Deleting display List.\n"); Group* walker = firstGroup; while (walker != NULL) { glDeleteLists (walker->listNumber, 1); Group* lastWalker = walker; walker = walker->nextGroup; delete lastWalker; } } /** \brief initializes the Object This Function initializes all the needed arrays, Lists and clientStates */ bool Object::initialize (void) { if (verbose >=3) printf("new 3D-Object is being created\n"); // setting the start group; firstGroup = new Group; currentGroup = firstGroup; groupCount = 0; initGroup (currentGroup); mtlFileName = ""; scaleFactor = 1; material = new Material(); glEnableClientState (GL_VERTEX_ARRAY); // glEnableClientState (GL_NORMAL_ARRAY); // glEnableClientState (GL_TEXTURE_COORD_ARRAY); return true; } /** \brief Imports a obj file and handles the the relative location \param fileName The file to import */ bool Object::importFile (char* fileName) { if (verbose >=3) printf("preparing to read in file: %s\n", fileName); objFileName = fileName; this->readFromObjFile (objFileName); return true; } /** \brief finalizes an Object. This funcion is needed, to close the glList and all the other lists. */ bool Object::finalize(void) { // if (verbose >=3) printf("finalizing the 3D-Object\n"); finalizeGroup (currentGroup); if (material != NULL) delete material; return true; } /** \brief Draws the Objects of all Groups. It does this by just calling the Lists that must have been created earlier. */ void Object::draw (void) { if (verbose >=2) printf("drawing the 3D-Objects\n"); Group* walker = firstGroup; while (walker != NULL) { if (verbose >= 3) printf ("Drawing object %s\n", walker->name); glCallList (walker->listNumber); walker = walker->nextGroup; } } /** \brief Draws the Object number groupNumber It does this by just calling the List that must have been created earlier. \param groupNumber The number of the group that will be displayed. */ void Object::draw (int groupNumber) { if (groupNumber >= groupCount) { if (verbose>=2) printf ("You requested object number %i, but this File only contains of %i Objects.\n", groupNumber-1, groupCount); return; } if (verbose >=2) printf("drawing the requested 3D-Objects if found.\n"); Group* walker = firstGroup; int counter = 0; while (walker != NULL) { if (counter == groupNumber) { if (verbose >= 2) printf ("Drawing object number %s named %s\n", counter, walker->name); glCallList (walker->listNumber); return; } ++counter; walker = walker->nextGroup; } if (verbose >= 2) printf("Object number %i in %s not Found.\n", groupNumber, objFileName); return; } /** \brief Draws the Object with a specific groupname It does this by just calling the List that must have been created earlier. \param groupName The name of the group that will be displayed. */ void Object::draw (char* groupName) { if (verbose >=2) printf("drawing the requested 3D-Objects if found.\n"); Group* walker = firstGroup; while (walker != NULL) { if (!strcmp(walker->name, groupName)) { if (verbose >= 2) printf ("Drawing object %s\n", walker->name); glCallList (walker->listNumber); return; } walker = walker->nextGroup; } if (verbose >= 2) printf("Object Named %s in %s not Found.\n", groupName, objFileName); return; } /** \returns Count of the Objects in this File */ int Object::getGroupCount (void) { return groupCount; } /** \brief initializes a new Group object */ bool Object::initGroup(Group* group) { if (verbose >= 2) printf("Adding new Group\n"); group->name = ""; group->faceMode = -1; group->faceCount =0; if ((group->listNumber = glGenLists(1)) == 0 ) { printf ("list could not be created for this Object\n"); return false; } if (groupCount == 0) { group->firstVertex = 0; group->firstNormal = 0; group->firstNormal = 0; } else { group->firstVertex = currentGroup->firstVertex + currentGroup->vertices->getCount()/3; group->firstNormal = currentGroup->firstNormal + currentGroup->normals->getCount()/3; group->firstVertexTexture = currentGroup->firstVertexTexture + currentGroup->vTexture->getCount()/2; } if (verbose >=2) printf ("Creating new Arrays, with starting points v:%i, vt:%i, vn:%i .\n", group->firstVertex, group->firstVertexTexture, group->firstNormal); group->vertices = new Array(); group->normals = new Array(); group->vTexture = new Array(); glNewList (group->listNumber, GL_COMPILE); } /** \brief finalizes a Group. \param group the group to finalize. */ bool Object::finalizeGroup(Group* group) { if (verbose >=2) printf ("Finalize group %s.\n", group->name); glEnd(); glEndList(); } /** \brief deletes the Arrays of the Group to save space. \param group the group to delete the arrays from. */ bool Object::cleanupGroup(Group* group) { if (verbose >=2) printf ("cleaning up group %s.\n", group->name); delete group->vertices; delete group->normals; delete group->vTexture; } /** \brief Reads in the .obj File and sets all the Values. This function does read the file, parses it for the occurence of things like vertices, faces and so on, and executes the specific tasks \param fileName the File that will be parsed (.obj-file) */ bool Object::readFromObjFile (char* fileName) { OBJ_FILE = new ifstream(fileName); if (!OBJ_FILE->is_open()) { if (verbose >=1) printf ("unable to open .OBJ file: %s\n Loading Box Object instead.\n", fileName); BoxObject(); return false; } objFileName = fileName; char Buffer[10000]; while(!OBJ_FILE->eof()) { OBJ_FILE->getline(Buffer, 10000); if (verbose >=4) printf ("Read input line: %s\n",Buffer); // case vertice if (!strncmp(Buffer, "v ", 2)) { readVertex(Buffer+2); } // case face else if (!strncmp(Buffer, "f ", 2)) { readFace (Buffer+2); } else if (!strncmp(Buffer, "mtllib", 6)) { readMtlLib (Buffer+7); } else if (!strncmp(Buffer, "usemtl", 6)) { readUseMtl (Buffer+7); } // case VertexNormal else if (!strncmp(Buffer, "vn ", 2)) { readVertexNormal(Buffer+3); } // case VertexTextureCoordinate else if (!strncmp(Buffer, "vt ", 2)) { readVertexTexture(Buffer+3); } // case group else if (!strncmp(Buffer, "g", 1)) { readGroup (Buffer+2); } } OBJ_FILE->close(); return true; } /** \brief parses a vertex-String If a vertex line is found this function will inject it into the vertex-Array \param vertexString The String that will be parsed. */ bool Object::readVertex (char* vertexString) { readingVertices = true; char subbuffer1[20]; char subbuffer2[20]; char subbuffer3[20]; sscanf (vertexString, "%s %s %s", subbuffer1, subbuffer2, subbuffer3); if (verbose >= 3) printf ("reading in a vertex: %s %s %s\n", subbuffer1, subbuffer2, subbuffer3); currentGroup->vertices->addEntry(atof(subbuffer1)*scaleFactor, atof(subbuffer2)*scaleFactor, atof(subbuffer3)*scaleFactor); return true; } /** \brief parses a face-string If a face line is found this function will add it to the glList. The function makes a difference between QUADS and TRIANGLES, and will if changed re-open, set and re-close the gl-processe. \param faceString The String that will be parsed. */ bool Object::readFace (char* faceString) { // finalize the Arrays; if (readingVertices == true) { currentGroup->vertices->finalizeArray(); // glVertexPointer(3, GL_FLOAT, 0, currentGroup->vertices->getArray()); currentGroup->normals->finalizeArray(); // glNormalPointer(GL_FLOAT, 0, currentGroup->normals->getArray()); currentGroup->vTexture->finalizeArray(); } readingVertices = false; currentGroup->faceCount++; int elemCount = 0; FaceElement* firstElem = new FaceElement; FaceElement* tmpElem = firstElem; while(strcmp (faceString, "\0")) { if (elemCount>0) tmpElem = tmpElem->next = new FaceElement; tmpElem->next = NULL; sscanf (faceString, "%s", tmpElem->value); faceString += strlen(tmpElem->value); if (strcmp (faceString, "\0")) faceString++; elemCount++; } if (elemCount == 3) { if (currentGroup->faceMode != 3) { if (currentGroup->faceMode != -1) glEnd(); glBegin(GL_TRIANGLES); } currentGroup->faceMode = 3; if (verbose >=3) printf ("found triag.\n"); } else if (elemCount == 4) { if (currentGroup->faceMode != 4) { if (currentGroup->faceMode != -1) glEnd(); glBegin(GL_QUADS); } currentGroup->faceMode = 4; if (verbose >=3 ) printf ("found quad.\n"); } else if (elemCount > 4) { if (currentGroup->faceMode != -1) glEnd(); glBegin(GL_POLYGON); if (verbose >=3) printf ("Polygon with %i faces found.", elemCount); currentGroup->faceMode = elemCount; } tmpElem = firstElem; while (tmpElem != NULL) { // printf ("%s\n", tmpElem->value); addGLElement(tmpElem->value); tmpElem = tmpElem->next; } } /** \brief Adds a Face-element (one vertex of a face) with all its information. It does this by searching: 1. The Vertex itself 2. The VertexNormale 3. The VertexTextureCoordinate merging this information, the face will be drawn. */ bool Object::addGLElement (char* elementString) { if (verbose >=3) printf ("importing grafical Element to openGL\n"); char* vertex = elementString; char* texture; if ((texture = strstr (vertex, "/")) != NULL) { texture[0] = '\0'; texture ++; if (verbose>=3) printf ("includeing texture #%i, and mapping it to group texture #%i, textureArray has %i entries.\n", atoi(texture), (atoi(texture)-1 - currentGroup->firstVertexTexture)*3, currentGroup->vTexture->getCount()); glTexCoord2fv(currentGroup->vTexture->getArray()+(atoi(texture)-1 - currentGroup->firstVertexTexture)*2); char* normal; if ((normal = strstr (texture, "/")) !=NULL) { normal[0] = '\0'; normal ++; //glArrayElement(atoi(vertex)-1); glNormal3fv(currentGroup->normals->getArray() +(atoi(normal)-1 - currentGroup->firstNormal)*3); } } if (verbose>=3) printf ("includeing vertex #%i, and mapping it to group vertex #%i, vertexArray has %i entries.\n", atoi(vertex), (atoi(vertex)-1 - currentGroup->firstVertex)*3, currentGroup->vertices->getCount()); glVertex3fv(currentGroup->vertices->getArray() +(atoi(vertex)-1 - currentGroup->firstVertex)*3); } /** \brief parses a vertexNormal-String If a vertexNormal line is found this function will inject it into the vertexNormal-Array \param normalString The String that will be parsed. */ bool Object::readVertexNormal (char* normalString) { readingVertices = true; char subbuffer1[20]; char subbuffer2[20]; char subbuffer3[20]; sscanf (normalString, "%s %s %s", subbuffer1, subbuffer2, subbuffer3); if (verbose >=3 ) printf("found vertex-Normal %s, %s, %s\n", subbuffer1,subbuffer2,subbuffer3); currentGroup->normals->addEntry(atof(subbuffer1), atof(subbuffer2), atof(subbuffer3)); return true; } /** \brief parses a vertexTextureCoordinate-String If a vertexTextureCoordinate line is found this function will inject it into the vertexTexture-Array \param vTextureString The String that will be parsed. */ bool Object::readVertexTexture (char* vTextureString) { readingVertices = true; char subbuffer1[20]; char subbuffer2[20]; sscanf (vTextureString, "%s %s", subbuffer1, subbuffer2); if (verbose >=3 ) printf("found vertex-Texture %s, %s\n", subbuffer1,subbuffer2); currentGroup->vTexture->addEntry(atof(subbuffer1)); currentGroup->vTexture->addEntry(atof(subbuffer2)); return true; } /** \brief parses a group String This function initializes a new Group. With it you should be able to import .obj-files with more than one Objects inside. \param groupString the new Group to create */ bool Object::readGroup (char* groupString) { // setting the group name if not default. if (strcmp(currentGroup->name, "default")) { currentGroup->name = (char*) malloc ( strlen(groupString) * sizeof (char)); strcpy(currentGroup->name, groupString); } if (groupCount != 0 && currentGroup->faceCount>0) { Group* newGroup = new Group; finalizeGroup(currentGroup); currentGroup->nextGroup = newGroup; initGroup(newGroup); cleanupGroup(currentGroup); // deletes the arrays of the group; must be after initGroup. currentGroup = newGroup; // must be after init see initGroup for more info } ++groupCount; } /** \brief Function to read in a mtl File. this Function parses all Lines of an mtl File \param mtlFile The .mtl file to read */ bool Object::readMtlLib (char* mtlFile) { MTL_FILE = new ifstream (mtlFile); if (!MTL_FILE->is_open()) { if (verbose >= 1) printf ("unable to open file: %s\n", mtlFile); return false; } mtlFileName = mtlFile; if (verbose >=2) printf ("Opening mtlFile: %s\n", mtlFileName); char Buffer[500]; Material* tmpMat = material; while(!MTL_FILE->eof()) { MTL_FILE->getline(Buffer, 500); if (verbose >= 4) printf("found line in mtlFile: %s\n", Buffer); // create new Material if (!strncmp(Buffer, "newmtl ", 2)) { tmpMat = tmpMat->addMaterial(Buffer+7); // printf ("%s, %p\n", tmpMat->getName(), tmpMat); } // setting a illumMode else if (!strncmp(Buffer, "illum", 5)) { tmpMat->setIllum(Buffer+6); } // setting Diffuse Color else if (!strncmp(Buffer, "Kd", 2)) { tmpMat->setDiffuse(Buffer+3); } // setting Ambient Color else if (!strncmp(Buffer, "Ka", 2)) { tmpMat->setAmbient(Buffer+3); } // setting Specular Color else if (!strncmp(Buffer, "Ks", 2)) { tmpMat->setSpecular(Buffer+3); } // setting The Specular Shininess else if (!strncmp(Buffer, "Ns", 2)) { tmpMat->setShininess(Buffer+3); } // setting up transparency else if (!strncmp(Buffer, "d", 1)) { tmpMat->setTransparency(Buffer+2); } else if (!strncpy(Buffer, "Tf", 2)) { tmpMat->setTransparency(Buffer+3); } } return true; } /** \brief Function that selects a material, if changed in the obj file. \param matString the Material that will be set. */ bool Object::readUseMtl (char* matString) { if (!strcmp (mtlFileName, "")) { if (verbose >= 1) printf ("Not using new defined material, because no mtlFile found yet\n"); return false; } if (currentGroup->faceMode != -1) glEnd(); currentGroup->faceMode = 0; if (verbose >= 2) printf ("using material %s for coming Faces.\n", matString); material->search(matString)->select(); } /** \brief Includes a default object This will inject a Cube, because this is the most basic object. */ void Object::BoxObject(void) { readVertex ("-0.500000 -0.500000 0.500000"); readVertex ("0.500000 -0.500000 0.500000"); readVertex ("-0.500000 0.500000 0.500000"); readVertex ("0.500000 0.500000 0.500000"); readVertex ("-0.500000 0.500000 -0.500000"); readVertex ("0.500000 0.500000 -0.500000"); readVertex ("-0.500000 -0.500000 -0.500000"); readVertex ("0.500000 -0.500000 -0.500000"); readVertexTexture ("0.000000 0.000000"); readVertexTexture ("1.000000 0.000000"); readVertexTexture ("0.000000 1.000000"); readVertexTexture ("1.000000 1.000000"); readVertexTexture ("0.000000 2.000000"); readVertexTexture ("1.000000 2.000000"); readVertexTexture ("0.000000 3.000000"); readVertexTexture ("1.000000 3.000000"); readVertexTexture ("0.000000 4.000000"); readVertexTexture ("1.000000 4.000000"); readVertexTexture ("2.000000 0.000000"); readVertexTexture ("2.000000 1.000000"); readVertexTexture ("-1.000000 0.000000"); readVertexTexture ("-1.000000 1.000000"); readVertexNormal ("0.000000 0.000000 1.000000"); readVertexNormal ("0.000000 0.000000 1.000000"); readVertexNormal ("0.000000 0.000000 1.000000"); readVertexNormal ("0.000000 0.000000 1.000000"); readVertexNormal ("0.000000 1.000000 0.000000"); readVertexNormal ("0.000000 1.000000 0.000000"); readVertexNormal ("0.000000 1.000000 0.000000"); readVertexNormal ("0.000000 1.000000 0.000000"); readVertexNormal ("0.000000 0.000000 -1.000000"); readVertexNormal ("0.000000 0.000000 -1.000000"); readVertexNormal ("0.000000 0.000000 -1.000000"); readVertexNormal ("0.000000 0.000000 -1.000000"); readVertexNormal ("0.000000 -1.000000 0.000000"); readVertexNormal ("0.000000 -1.000000 0.000000"); readVertexNormal ("0.000000 -1.000000 0.000000"); readVertexNormal ("0.000000 -1.000000 0.000000"); readVertexNormal ("1.000000 0.000000 0.000000"); readVertexNormal ("1.000000 0.000000 0.000000"); readVertexNormal ("1.000000 0.000000 0.000000"); readVertexNormal ("1.000000 0.000000 0.000000"); readVertexNormal ("-1.000000 0.000000 0.000000"); readVertexNormal ("-1.000000 0.000000 0.000000"); readVertexNormal ("-1.000000 0.000000 0.000000"); readVertexNormal ("-1.000000 0.000000 0.000000"); readFace ("1/1/1 2/2/2 4/4/3 3/3/4"); readFace ("3/3/5 4/4/6 6/6/7 5/5/8"); readFace ("5/5/9 6/6/10 8/8/11 7/7/12"); readFace ("7/7/13 8/8/14 2/10/15 1/9/16"); readFace ("2/2/17 8/11/18 6/12/19 4/4/20"); readFace ("7/13/21 1/1/22 3/3/23 5/14/24"); }