/* Copyright (C) 2007, 2010 - Bit-Blot This file is part of Aquaria. Aquaria 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 "Model.h" #include "Core.h" #include "cal3d/cal3d.h" #include "../ExternalLibs/glpng.h" const std::string modelPath = "models/"; Model::Model() : RenderObject() { m_calModel = 0; //, coreModel("poot") m_calCoreModel = new CalCoreModel("model"); if (!m_calCoreModel) { errorLog("Model: failed: new CalCoreModel"); } m_motionBlend[0] = 0.6f; m_motionBlend[1] = 0.1f; m_motionBlend[2] = 0.3f; m_animationCount = 0; m_meshCount = 0; m_renderScale = 1.0f; m_lodLevel = 1.0f; cull = false; } void Model::destroy() { RenderObject::destroy(); } bool Model::load(const std::string& _strFilename) { // open the model configuration file std::ifstream file; std::string strFilename(core->adjustFilenameCase(_strFilename)); file.open(strFilename.c_str(), std::ios::in | std::ios::binary); if(!file) { std::ostringstream os; os << "Failed to open model configuration file '" << strFilename << "'." << std::endl; errorLog(os.str()); return false; } // initialize the data path std::string strPath = m_path; // initialize the animation count int animationCount; animationCount = 0; // parse all lines from the model configuration file int line; for(line = 1; ; line++) { // read the next model configuration line std::string strBuffer; std::getline(file, strBuffer); // stop if we reached the end of file if(file.eof()) break; // check if an error happend while reading from the file if(!file) { std::cerr << "Error while reading from the model configuration file '" << strFilename << "'." << std::endl; return false; } // find the first non-whitespace character std::string::size_type pos; pos = strBuffer.find_first_not_of(" \t"); // check for empty lines if((pos == std::string::npos) || (strBuffer[pos] == '\n') || (strBuffer[pos] == '\r') || (strBuffer[pos] == 0)) continue; // check for comment lines if(strBuffer[pos] == '#') continue; // get the key std::string strKey; strKey = strBuffer.substr(pos, strBuffer.find_first_of(" =\t\n\r", pos) - pos); pos += strKey.size(); // get the '=' character pos = strBuffer.find_first_not_of(" \t", pos); if((pos == std::string::npos) || (strBuffer[pos] != '=')) { std::cerr << strFilename << "(" << line << "): Invalid syntax." << std::endl; return false; } // find the first non-whitespace character after the '=' character pos = strBuffer.find_first_not_of(" \t", pos + 1); // get the data std::string strData; strData = strBuffer.substr(pos, strBuffer.find_first_of("\n\r", pos) - pos); // handle the model creation if(strKey == "scale") { // set rendering scale factor m_renderScale = atof(strData.c_str()); } else if(strKey == "path") { // set the new path for the data files if one hasn't been set already if (m_path == "") strPath = strData; } else if(strKey == "skeleton") { // load core skeleton std::cout << "Loading skeleton '" << strData << "'..." << std::endl; if(!m_calCoreModel->loadCoreSkeleton(strPath + strData)) { CalError::printLastError(); return false; } } else if(strKey == "animation") { // load core animation std::cout << "Loading animation '" << strData << "'..." << std::endl; m_animationId[animationCount] = m_calCoreModel->loadCoreAnimation(strPath + strData); if(m_animationId[animationCount] == -1) { CalError::printLastError(); return false; } animationCount++; } else if(strKey == "mesh") { // load core mesh std::cout << "Loading mesh '" << strData << "'..." << std::endl; if(m_calCoreModel->loadCoreMesh(strPath + strData) == -1) { CalError::printLastError(); return false; } } else if(strKey == "material") { // load core material std::cout << "Loading material '" << strData << "'..." << std::endl; if(m_calCoreModel->loadCoreMaterial(strPath + strData) == -1) { CalError::printLastError(); return false; } } else { std::cerr << strFilename << "(" << line << "): Invalid syntax." << std::endl; return false; } } // explicitely close the file file.close(); // load all textures and store the opengl texture id in the corresponding map in the material int materialId; for(materialId = 0; materialId < m_calCoreModel->getCoreMaterialCount(); materialId++) { // get the core material CalCoreMaterial *pCoreMaterial; pCoreMaterial = m_calCoreModel->getCoreMaterial(materialId); // loop through all maps of the core material int mapId; for(mapId = 0; mapId < pCoreMaterial->getMapCount(); mapId++) { // get the filename of the texture std::string strFilename; strFilename = pCoreMaterial->getMapFilename(mapId); // load the texture from the file GLuint textureId; pngInfo info; textureId = pngBind((strPath + strFilename).c_str(), PNG_BUILDMIPMAPS, PNG_ALPHA, &info, GL_CLAMP, GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR); //loadTexture(strPath + strFilename); // store the opengl texture id in the user data of the map pCoreMaterial->setMapUserData(mapId, (Cal::UserData)textureId); } } // make one material thread for each material // NOTE: this is not the right way to do it, but this viewer can't do the right // mapping without further information on the model etc. for(materialId = 0; materialId < m_calCoreModel->getCoreMaterialCount(); materialId++) { // create the a material thread m_calCoreModel->createCoreMaterialThread(materialId); // initialize the material thread m_calCoreModel->setCoreMaterialId(materialId, 0, materialId); } // Calculate Bounding Boxes m_calCoreModel->getCoreSkeleton()->calculateBoundingBoxes(m_calCoreModel); m_calModel = new CalModel(m_calCoreModel); // attach all meshes to the model int meshId; for(meshId = 0; meshId < m_calCoreModel->getCoreMeshCount(); meshId++) { m_calModel->attachMesh(meshId); } // set the material set of the whole model m_calModel->setMaterialSet(0); // set initial animation state /* m_state = STATE_MOTION; m_calModel->getMixer()->blendCycle(m_animationId[STATE_MOTION], m_motionBlend[0], 0.0f); m_calModel->getMixer()->blendCycle(m_animationId[STATE_MOTION + 1], m_motionBlend[1], 0.0f); m_calModel->getMixer()->blendCycle(m_animationId[STATE_MOTION + 2], m_motionBlend[2], 0.0f); */ return true; } void Model::onUpdate(float dt) { RenderObject::onUpdate(dt); if (m_calModel) m_calModel->update(dt); } void Model::renderMesh(bool bWireframe, bool bLight) { // get the renderer of the model CalRenderer *pCalRenderer; pCalRenderer = m_calModel->getRenderer(); // begin the rendering loop if(!pCalRenderer->beginRendering()) return; // set wireframe mode if necessary if(bWireframe) { glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); } // set the global OpenGL states glEnable(GL_DEPTH_TEST); glShadeModel(GL_SMOOTH); // set the lighting mode if necessary if(bLight) { glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); } // we will use vertex arrays, so enable them glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); // get the number of meshes int meshCount; meshCount = pCalRenderer->getMeshCount(); // render all meshes of the model int meshId; for(meshId = 0; meshId < meshCount; meshId++) { // get the number of submeshes int submeshCount; submeshCount = pCalRenderer->getSubmeshCount(meshId); // render all submeshes of the mesh int submeshId; for(submeshId = 0; submeshId < submeshCount; submeshId++) { // select mesh and submesh for further data access if(pCalRenderer->selectMeshSubmesh(meshId, submeshId)) { unsigned char meshColor[4]; GLfloat materialColor[4]; // set the material ambient color pCalRenderer->getAmbientColor(&meshColor[0]); materialColor[0] = meshColor[0] / 255.0f; materialColor[1] = meshColor[1] / 255.0f; materialColor[2] = meshColor[2] / 255.0f; materialColor[3] = meshColor[3] / 255.0f; glMaterialfv(GL_FRONT, GL_AMBIENT, materialColor); // set the material diffuse color pCalRenderer->getDiffuseColor(&meshColor[0]); materialColor[0] = meshColor[0] / 255.0f; materialColor[1] = meshColor[1] / 255.0f; materialColor[2] = meshColor[2] / 255.0f; materialColor[3] = meshColor[3] / 255.0f; glMaterialfv(GL_FRONT, GL_DIFFUSE, materialColor); // set the vertex color if we have no lights if(!bLight) { glColor4fv(materialColor); } // set the material specular color pCalRenderer->getSpecularColor(&meshColor[0]); materialColor[0] = meshColor[0] / 255.0f; materialColor[1] = meshColor[1] / 255.0f; materialColor[2] = meshColor[2] / 255.0f; materialColor[3] = meshColor[3] / 255.0f; glMaterialfv(GL_FRONT, GL_SPECULAR, materialColor); // set the material shininess factor float shininess; shininess = 50.0f; //TODO: pCalRenderer->getShininess(); glMaterialfv(GL_FRONT, GL_SHININESS, &shininess); // get the transformed vertices of the submesh static float meshVertices[30000][3]; int vertexCount; vertexCount = pCalRenderer->getVertices(&meshVertices[0][0]); // get the transformed normals of the submesh static float meshNormals[30000][3]; pCalRenderer->getNormals(&meshNormals[0][0]); // get the texture coordinates of the submesh static float meshTextureCoordinates[30000][2]; int textureCoordinateCount; textureCoordinateCount = pCalRenderer->getTextureCoordinates(0, &meshTextureCoordinates[0][0]); // get the faces of the submesh static CalIndex meshFaces[50000][3]; int faceCount; faceCount = pCalRenderer->getFaces(&meshFaces[0][0]); // set the vertex and normal buffers glVertexPointer(3, GL_FLOAT, 0, &meshVertices[0][0]); glNormalPointer(GL_FLOAT, 0, &meshNormals[0][0]); // set the texture coordinate buffer and state if necessary if((pCalRenderer->getMapCount() > 0) && (textureCoordinateCount > 0)) { glEnable(GL_TEXTURE_2D); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glEnable(GL_COLOR_MATERIAL); // set the texture id we stored in the map user data glBindTexture(GL_TEXTURE_2D, (GLuint)pCalRenderer->getMapUserData(0)); // set the texture coordinate buffer glTexCoordPointer(2, GL_FLOAT, 0, &meshTextureCoordinates[0][0]); glColor4f(1.0f, 1.0f, 1.0f, 1.0f); } // draw the submesh if(sizeof(CalIndex)==2) glDrawElements(GL_TRIANGLES, faceCount * 3, GL_UNSIGNED_SHORT, &meshFaces[0][0]); else glDrawElements(GL_TRIANGLES, faceCount * 3, GL_UNSIGNED_INT, &meshFaces[0][0]); // disable the texture coordinate state if necessary if((pCalRenderer->getMapCount() > 0) && (textureCoordinateCount > 0)) { glDisable(GL_COLOR_MATERIAL); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisable(GL_TEXTURE_2D); } // DEBUG-CODE ////////////////////////////////////////////////////////////////// /* glBegin(GL_LINES); glColor3f(1.0f, 1.0f, 1.0f); int vertexId; for(vertexId = 0; vertexId < vertexCount; vertexId++) { const float scale = 0.3f; glVertex3f(meshVertices[vertexId][0], meshVertices[vertexId][1], meshVertices[vertexId][2]); glVertex3f(meshVertices[vertexId][0] + meshNormals[vertexId][0] * scale, meshVertices[vertexId][1] + meshNormals[vertexId][1] * scale, meshVertices[vertexId][2] + meshNormals[vertexId][2] * scale); } glEnd(); */ //////////////////////////////////////////////////////////////////////////////// } } } // clear vertex array state glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); // reset the lighting mode if(bLight) { glDisable(GL_LIGHTING); glDisable(GL_LIGHT0); } // reset the global OpenGL states glDisable(GL_DEPTH_TEST); // reset wireframe mode if necessary if(bWireframe) { glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); } // end the rendering pCalRenderer->endRendering(); } void Model::onRender() { /* glClearDepth(1.0f); core->resize3D(); */ glEnable(GL_DEPTH_TEST); // Enables Depth Testing glDepthFunc(GL_LEQUAL); // The Type Of Depth Testing To Do glDisable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, 0); glShadeModel(GL_SMOOTH); // check if we need to render the mesh renderMesh(false, false); glEnable(GL_TEXTURE_2D); glDisable(GL_DEPTH_TEST); }