// ============================================================== // This file is part of Glest Shared Library (www.glest.org) // // Copyright (C) 2001-2008 MartiƱo Figueroa // // You can redistribute this code 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 // ============================================================== #include "model.h" #include #include #include #include "interpolation.h" #include "conversion.h" #include "util.h" #include "platform_common.h" #include "opengl.h" #include "platform_util.h" #include #include "leak_dumper.h" using namespace Shared::Platform; using namespace Shared::PlatformCommon; using namespace Shared::Graphics::Gl; using namespace std; using namespace Shared::Util; namespace Shared{ namespace Graphics{ using namespace Util; // ===================================================== // class Mesh // ===================================================== // ==================== constructor & destructor ==================== Mesh::Mesh() { textureManager = NULL; frameCount= 0; vertexCount= 0; indexCount= 0; texCoordFrameCount = 0; vertices= NULL; normals= NULL; texCoords= NULL; tangents= NULL; indices= NULL; interpolationData= NULL; for(int i=0; igetPath().c_str(),i); textureManager->endTexture(textures[i]); textures[i] = NULL; } } } textureManager = NULL; } // ========================== shadows & interpolation ========================= void Mesh::buildInterpolationData(){ interpolationData= new InterpolationData(this); } void Mesh::updateInterpolationData(float t, bool cycle) { if(interpolationData != NULL) { interpolationData->update(t, cycle); } } void Mesh::updateInterpolationVertices(float t, bool cycle) { if(interpolationData != NULL) { interpolationData->updateVertices(t, cycle); } } void Mesh::BuildVBOs() { if(getVBOSupported() == true) { if(hasBuiltVBOs == false) { //printf("In [%s::%s Line: %d] setting up a VBO...\n",__FILE__,__FUNCTION__,__LINE__); // Generate And Bind The Vertex Buffer glGenBuffersARB( 1,(GLuint*) &m_nVBOVertices ); // Get A Valid Name glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nVBOVertices ); // Bind The Buffer // Load The Data glBufferDataARB( GL_ARRAY_BUFFER_ARB, sizeof(Vec3f)*frameCount*vertexCount, getInterpolationData()->getVertices(), GL_STATIC_DRAW_ARB ); glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); // Generate And Bind The Texture Coordinate Buffer glGenBuffersARB( 1, (GLuint*)&m_nVBOTexCoords ); // Get A Valid Name glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nVBOTexCoords ); // Bind The Buffer // Load The Data glBufferDataARB( GL_ARRAY_BUFFER_ARB, sizeof(Vec2f)*vertexCount, texCoords, GL_STATIC_DRAW_ARB ); glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); // Generate And Bind The Normal Buffer glGenBuffersARB( 1, (GLuint*)&m_nVBONormals ); // Get A Valid Name glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nVBONormals ); // Bind The Buffer // Load The Data glBufferDataARB( GL_ARRAY_BUFFER_ARB, sizeof(Vec3f)*frameCount*vertexCount, getInterpolationData()->getNormals(), GL_STATIC_DRAW_ARB ); glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); // Generate And Bind The Index Buffer glGenBuffersARB( 1, (GLuint*)&m_nVBOIndexes ); // Get A Valid Name glBindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, m_nVBOIndexes ); // Bind The Buffer // Load The Data glBufferDataARB( GL_ELEMENT_ARRAY_BUFFER_ARB, sizeof(uint32)*indexCount, indices, GL_STATIC_DRAW_ARB ); glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0); // Our Copy Of The Data Is No Longer Necessary, It Is Safe In The Graphics Card delete [] vertices; vertices = NULL; delete [] texCoords; texCoords = NULL; delete [] normals; normals = NULL; delete [] indices; indices = NULL; delete interpolationData; interpolationData = NULL; hasBuiltVBOs = true; } } } void Mesh::ReleaseVBOs() { if(getVBOSupported() == true) { if(hasBuiltVBOs == true) { glDeleteBuffersARB( 1, (GLuint*)&m_nVBOVertices ); // Get A Valid Name glDeleteBuffersARB( 1, (GLuint*)&m_nVBOTexCoords ); // Get A Valid Name glDeleteBuffersARB( 1, (GLuint*)&m_nVBONormals ); // Get A Valid Name glDeleteBuffersARB( 1, (GLuint*)&m_nVBOIndexes ); // Get A Valid Name hasBuiltVBOs = false; } } } // ==================== load ==================== string Mesh::findAlternateTexture(vector conversionList, string textureFile) { string result = textureFile; string fileExt = extractExtension(textureFile); for(unsigned int i = 0; i < conversionList.size(); ++i) { string convertTo = conversionList[i]; if(fileExt != convertTo) { string alternateTexture = textureFile; replaceAll(alternateTexture, "." + fileExt, "." + convertTo); if(fileExists(alternateTexture) == true) { result = alternateTexture; break; } } } return result; } void Mesh::loadV2(int meshIndex, const string &dir, FILE *f, TextureManager *textureManager, bool deletePixMapAfterLoad, std::map > > *loadedFileList, string sourceLoader) { this->textureManager = textureManager; //read header MeshHeaderV2 meshHeader; size_t readBytes = fread(&meshHeader, sizeof(MeshHeaderV2), 1, f); if(meshHeader.normalFrameCount != meshHeader.vertexFrameCount) { char szBuf[4096]=""; sprintf(szBuf,"Old v2 model: vertex frame count different from normal frame count [v = %d, n = %d] meshIndex = %d",meshHeader.vertexFrameCount,meshHeader.normalFrameCount,meshIndex); throw runtime_error(szBuf); } if(meshHeader.texCoordFrameCount != 1) { char szBuf[4096]=""; sprintf(szBuf,"Old v2 model: texture coord frame count is not 1 [t = %d] meshIndex = %d",meshHeader.texCoordFrameCount,meshIndex); throw runtime_error(szBuf); } //init frameCount= meshHeader.vertexFrameCount; vertexCount= meshHeader.pointCount; indexCount= meshHeader.indexCount; texCoordFrameCount = meshHeader.texCoordFrameCount; init(); //misc twoSided= false; customColor= false; if(SystemFlags::VERBOSE_MODE_ENABLED) printf("Load v2, this = %p Found meshHeader.hasTexture = %d, texName [%s] mtDiffuse = %d meshIndex = %d\n",this,meshHeader.hasTexture,toLower(reinterpret_cast(meshHeader.texName)).c_str(),mtDiffuse,meshIndex); textureFlags= 0; if(meshHeader.hasTexture) { textureFlags= 1; } //texture if(meshHeader.hasTexture && textureManager!=NULL){ texturePaths[mtDiffuse]= toLower(reinterpret_cast(meshHeader.texName)); string texPath= dir; if(texPath != "") { endPathWithSlash(texPath); } texPath += texturePaths[mtDiffuse]; textures[mtDiffuse]= dynamic_cast(textureManager->getTexture(texPath)); if(textures[mtDiffuse] == NULL) { if(fileExists(texPath) == false) { vector conversionList; conversionList.push_back("png"); conversionList.push_back("jpg"); conversionList.push_back("tga"); conversionList.push_back("bmp"); texPath = findAlternateTexture(conversionList, texPath); } if(fileExists(texPath) == true) { textures[mtDiffuse]= textureManager->newTexture2D(); textures[mtDiffuse]->load(texPath); if(loadedFileList) { (*loadedFileList)[texPath].push_back(make_pair(sourceLoader,sourceLoader)); } texturesOwned[mtDiffuse]=true; textures[mtDiffuse]->init(textureManager->getTextureFilter(),textureManager->getMaxAnisotropy()); if(deletePixMapAfterLoad == true) { textures[mtDiffuse]->deletePixels(); } } else { SystemFlags::OutputDebug(SystemFlags::debugError,"In [%s::%s Line: %d] Error v2 model is missing texture [%s] meshIndex = %d\n",__FILE__,__FUNCTION__,__LINE__,texPath.c_str(),meshIndex); } } } //read data readBytes = fread(vertices, sizeof(Vec3f)*frameCount*vertexCount, 1, f); readBytes = fread(normals, sizeof(Vec3f)*frameCount*vertexCount, 1, f); if(textures[mtDiffuse]!=NULL){ readBytes = fread(texCoords, sizeof(Vec2f)*vertexCount, 1, f); } readBytes = fread(&diffuseColor, sizeof(Vec3f), 1, f); readBytes = fread(&opacity, sizeof(float32), 1, f); fseek(f, sizeof(Vec4f)*(meshHeader.colorFrameCount-1), SEEK_CUR); readBytes = fread(indices, sizeof(uint32)*indexCount, 1, f); } void Mesh::loadV3(int meshIndex, const string &dir, FILE *f, TextureManager *textureManager,bool deletePixMapAfterLoad, std::map > > *loadedFileList, string sourceLoader) { this->textureManager = textureManager; //read header MeshHeaderV3 meshHeader; size_t readBytes = fread(&meshHeader, sizeof(MeshHeaderV3), 1, f); if(meshHeader.normalFrameCount != meshHeader.vertexFrameCount) { char szBuf[4096]=""; sprintf(szBuf,"Old v3 model: vertex frame count different from normal frame count [v = %d, n = %d] meshIndex = %d",meshHeader.vertexFrameCount,meshHeader.normalFrameCount,meshIndex); throw runtime_error(szBuf); } //init frameCount= meshHeader.vertexFrameCount; vertexCount= meshHeader.pointCount; indexCount= meshHeader.indexCount; texCoordFrameCount = meshHeader.texCoordFrameCount; init(); //misc twoSided= (meshHeader.properties & mp3TwoSided) != 0; customColor= (meshHeader.properties & mp3CustomColor) != 0; textureFlags= 0; if((meshHeader.properties & mp3NoTexture) != mp3NoTexture) { textureFlags= 1; } if(SystemFlags::VERBOSE_MODE_ENABLED) printf("Load v3, this = %p Found meshHeader.properties = %d, textureFlags = %d, texName [%s] mtDiffuse = %d meshIndex = %d\n",this,meshHeader.properties,textureFlags,toLower(reinterpret_cast(meshHeader.texName)).c_str(),mtDiffuse,meshIndex); //texture if((meshHeader.properties & mp3NoTexture) != mp3NoTexture && textureManager!=NULL){ texturePaths[mtDiffuse]= toLower(reinterpret_cast(meshHeader.texName)); string texPath= dir; if(texPath != "") { endPathWithSlash(texPath); } texPath += texturePaths[mtDiffuse]; textures[mtDiffuse]= dynamic_cast(textureManager->getTexture(texPath)); if(textures[mtDiffuse] == NULL) { if(fileExists(texPath) == false) { vector conversionList; conversionList.push_back("png"); conversionList.push_back("jpg"); conversionList.push_back("tga"); conversionList.push_back("bmp"); texPath = findAlternateTexture(conversionList, texPath); } if(fileExists(texPath) == true) { textures[mtDiffuse]= textureManager->newTexture2D(); textures[mtDiffuse]->load(texPath); if(loadedFileList) { (*loadedFileList)[texPath].push_back(make_pair(sourceLoader,sourceLoader)); } texturesOwned[mtDiffuse]=true; textures[mtDiffuse]->init(textureManager->getTextureFilter(),textureManager->getMaxAnisotropy()); if(deletePixMapAfterLoad == true) { textures[mtDiffuse]->deletePixels(); } } else { SystemFlags::OutputDebug(SystemFlags::debugError,"In [%s::%s Line: %d] Error v3 model is missing texture [%s] meshHeader.properties = %d meshIndex = %d\n",__FILE__,__FUNCTION__,__LINE__,texPath.c_str(),meshHeader.properties,meshIndex); } } } //read data readBytes = fread(vertices, sizeof(Vec3f)*frameCount*vertexCount, 1, f); readBytes = fread(normals, sizeof(Vec3f)*frameCount*vertexCount, 1, f); if(textures[mtDiffuse]!=NULL){ for(unsigned int i=0; i > > *loadedFileList, string sourceLoader) { if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s] #1 load texture [%s]\n",__FUNCTION__,textureFile.c_str()); Texture2D* texture = dynamic_cast(textureManager->getTexture(textureFile)); if(texture == NULL) { if(fileExists(textureFile) == false) { vector conversionList; conversionList.push_back("png"); conversionList.push_back("jpg"); conversionList.push_back("tga"); conversionList.push_back("bmp"); textureFile = findAlternateTexture(conversionList, textureFile); if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s] #2 load texture [%s]\n",__FUNCTION__,textureFile.c_str()); } if(fileExists(textureFile) == true) { //if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s] texture exists loading [%s]\n",__FUNCTION__,textureFile.c_str()); texture = textureManager->newTexture2D(); if(textureChannelCount != -1) { texture->getPixmap()->init(textureChannelCount); } texture->load(textureFile); if(loadedFileList) { (*loadedFileList)[textureFile].push_back(make_pair(sourceLoader,sourceLoader)); } //if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s] texture loaded [%s]\n",__FUNCTION__,textureFile.c_str()); textureOwned = true; texture->init(textureManager->getTextureFilter(),textureManager->getMaxAnisotropy()); if(deletePixMapAfterLoad == true) { texture->deletePixels(); } //if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s] texture inited [%s]\n",__FUNCTION__,textureFile.c_str()); } else { if(SystemFlags::VERBOSE_MODE_ENABLED) printf("In [%s] #3 cannot load texture [%s]\n",__FUNCTION__,textureFile.c_str()); SystemFlags::OutputDebug(SystemFlags::debugError,"In [%s::%s Line: %d] Error v4 model is missing texture [%s] textureFlags = %d meshIndex = %d textureIndex = %d\n",__FILE__,__FUNCTION__,__LINE__,textureFile.c_str(),textureFlags,meshIndex,textureIndex); } } return texture; } void Mesh::load(int meshIndex, const string &dir, FILE *f, TextureManager *textureManager, bool deletePixMapAfterLoad,std::map > > *loadedFileList, string sourceLoader) { this->textureManager = textureManager; //read header MeshHeader meshHeader; size_t readBytes = fread(&meshHeader, sizeof(MeshHeader), 1, f); name = reinterpret_cast(meshHeader.name); //printf("Load, Found meshTextureCount = %d, meshHeader.textures = %d\n",meshTextureCount,meshHeader.textures); //init frameCount= meshHeader.frameCount; vertexCount= meshHeader.vertexCount; indexCount= meshHeader.indexCount; init(); //properties customColor= (meshHeader.properties & mpfCustomColor) != 0; twoSided= (meshHeader.properties & mpfTwoSided) != 0; //material diffuseColor= Vec3f(meshHeader.diffuseColor); specularColor= Vec3f(meshHeader.specularColor); specularPower= meshHeader.specularPower; opacity= meshHeader.opacity; textureFlags= meshHeader.textures; if(SystemFlags::VERBOSE_MODE_ENABLED) printf("Load v4, this = %p Found meshHeader.textures = %d meshIndex = %d\n",this,meshHeader.textures,meshIndex); //maps uint32 flag= 1; for(int i = 0; i < meshTextureCount; ++i) { if((meshHeader.textures & flag) && textureManager != NULL) { uint8 cMapPath[mapPathSize]; readBytes = fread(cMapPath, mapPathSize, 1, f); string mapPath= toLower(reinterpret_cast(cMapPath)); if(SystemFlags::VERBOSE_MODE_ENABLED) printf("mapPath [%s] meshHeader.textures = %d flag = %d (meshHeader.textures & flag) = %d meshIndex = %d i = %d\n",mapPath.c_str(),meshHeader.textures,flag,(meshHeader.textures & flag),meshIndex,i); string mapFullPath= dir; if(mapFullPath != "") { endPathWithSlash(mapFullPath); } mapFullPath += mapPath; textures[i] = loadMeshTexture(meshIndex, i, textureManager, mapFullPath, meshTextureChannelCount[i],texturesOwned[i], deletePixMapAfterLoad, loadedFileList, sourceLoader); } flag *= 2; } //read data readBytes = fread(vertices, sizeof(Vec3f)*frameCount*vertexCount, 1, f); readBytes = fread(normals, sizeof(Vec3f)*frameCount*vertexCount, 1, f); if(meshHeader.textures!=0){ readBytes = fread(texCoords, sizeof(Vec2f)*vertexCount, 1, f); } readBytes = fread(indices, sizeof(uint32)*indexCount, 1, f); //tangents if(textures[mtNormal]!=NULL){ computeTangents(); } } void Mesh::save(int meshIndex, const string &dir, FILE *f, TextureManager *textureManager, string convertTextureToFormat, std::map &textureDeleteList, bool keepsmallest) { MeshHeader meshHeader; memset(&meshHeader, 0, sizeof(struct MeshHeader)); strncpy((char*)meshHeader.name, (char*)name.c_str(), name.length()); meshHeader.frameCount= frameCount; meshHeader.vertexCount= vertexCount; meshHeader.indexCount = indexCount; //material memcpy((float32*)meshHeader.diffuseColor, (float32*)diffuseColor.ptr(), sizeof(float32) * 3); memcpy((float32*)meshHeader.specularColor, (float32*)specularColor.ptr(), sizeof(float32) * 3); meshHeader.specularPower = specularPower; meshHeader.opacity = opacity; //properties meshHeader.properties = 0; if(customColor) { meshHeader.properties |= mpfCustomColor; } if(twoSided) { meshHeader.properties |= mpfTwoSided; } meshHeader.textures = textureFlags; fwrite(&meshHeader, sizeof(MeshHeader), 1, f); if(SystemFlags::VERBOSE_MODE_ENABLED) printf("Save, this = %p, Found meshTextureCount = %d, meshHeader.textures = %d meshIndex = %d\n",this,meshTextureCount,meshHeader.textures,meshIndex); //maps uint32 flag= 1; for(int i = 0; i < meshTextureCount; ++i) { if((meshHeader.textures & flag)) { uint8 cMapPath[mapPathSize]; memset(&cMapPath[0],0,mapPathSize); Texture2D *texture = textures[i]; if(SystemFlags::VERBOSE_MODE_ENABLED) printf("Save, [%d] mesh texture ptr [%p]\n",i,texture); if(texture != NULL) { string file = toLower(texture->getPath()); if(SystemFlags::VERBOSE_MODE_ENABLED) printf("Save, Found mesh texture [%s]\n",file.c_str()); if(toLower(convertTextureToFormat) != "" && EndsWith(file, "." + convertTextureToFormat) == false) { long originalSize = getFileSize(file); long newSize = originalSize; string fileExt = extractExtension(file); replaceAll(file, "." + fileExt, "." + convertTextureToFormat); if(SystemFlags::VERBOSE_MODE_ENABLED) printf("Save, Convert from [%s] to [%s]\n",texture->getPath().c_str(),file.c_str()); if(convertTextureToFormat == "tga") { texture->getPixmap()->saveTga(file); newSize = getFileSize(file); if(keepsmallest == false || newSize <= originalSize) { textureDeleteList[texture->getPath()] = textureDeleteList[texture->getPath()] + 1; } else { printf("Texture will not be converted, keeping smallest texture [%s]\n",texture->getPath().c_str()); textureDeleteList[file] = textureDeleteList[file] + 1; } } else if(convertTextureToFormat == "bmp") { texture->getPixmap()->saveBmp(file); newSize = getFileSize(file); if(keepsmallest == false || newSize <= originalSize) { textureDeleteList[texture->getPath()] = textureDeleteList[texture->getPath()] + 1; } else { printf("Texture will not be converted, keeping smallest texture [%s]\n",texture->getPath().c_str()); textureDeleteList[file] = textureDeleteList[file] + 1; } } else if(convertTextureToFormat == "jpg") { texture->getPixmap()->saveJpg(file); newSize = getFileSize(file); if(keepsmallest == false || newSize <= originalSize) { textureDeleteList[texture->getPath()] = textureDeleteList[texture->getPath()] + 1; } else { printf("Texture will not be converted, keeping smallest texture [%s]\n",texture->getPath().c_str()); textureDeleteList[file] = textureDeleteList[file] + 1; } } else if(convertTextureToFormat == "png") { texture->getPixmap()->savePng(file); newSize = getFileSize(file); if(keepsmallest == false || newSize <= originalSize) { textureDeleteList[texture->getPath()] = textureDeleteList[texture->getPath()] + 1; } else { printf("Texture will not be converted, keeping smallest texture [%s]\n",texture->getPath().c_str()); textureDeleteList[file] = textureDeleteList[file] + 1; } } else { throw runtime_error("Unsupported texture format: [" + convertTextureToFormat + "]"); } //textureManager->endTexture(texture); if(SystemFlags::VERBOSE_MODE_ENABLED) printf("Save, load new texture [%s] originalSize [%ld] newSize [%ld]\n",file.c_str(),originalSize,newSize); if(keepsmallest == false || newSize <= originalSize) { texture = loadMeshTexture(meshIndex, i, textureManager,file, meshTextureChannelCount[i], texturesOwned[i], false); } } file = extractFileFromDirectoryPath(texture->getPath()); if(file.length() > mapPathSize) { throw runtime_error("file.length() > mapPathSize, file.length() = " + intToStr(file.length())); } else if(file.length() == 0) { throw runtime_error("file.length() == 0"); } if(SystemFlags::VERBOSE_MODE_ENABLED) printf("Save, new texture file [%s]\n",file.c_str()); memset(&cMapPath[0],0,mapPathSize); memcpy(&cMapPath[0],file.c_str(),file.length()); } fwrite(cMapPath, mapPathSize, 1, f); } flag*= 2; } //read data fwrite(vertices, sizeof(Vec3f)*frameCount*vertexCount, 1, f); fwrite(normals, sizeof(Vec3f)*frameCount*vertexCount, 1, f); if(meshHeader.textures != 0) { fwrite(texCoords, sizeof(Vec2f)*vertexCount, 1, f); } fwrite(indices, sizeof(uint32)*indexCount, 1, f); } void Mesh::computeTangents(){ delete [] tangents; tangents= new Vec3f[vertexCount]; for(unsigned int i=0; ideletePixels(); } } } // =============================================== // class Model // =============================================== // ==================== constructor & destructor ==================== Model::Model() { assert(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == false); meshCount = 0; meshes = NULL; textureManager = NULL; lastTData = -1; lastCycleData = false; lastTVertex = -1; lastCycleVertex = false; } Model::~Model() { delete [] meshes; meshes = NULL; } // ==================== data ==================== void Model::buildInterpolationData() const{ for(unsigned int i=0; i > > *loadedFileList, string *sourceLoader) { this->sourceLoader = (sourceLoader != NULL ? *sourceLoader : ""); this->fileName = path; if(GlobalStaticFlags::getIsNonGraphicalModeEnabled() == true) { return; } string extension= path.substr(path.find_last_of('.') + 1); if(extension=="g3d" || extension=="G3D") { loadG3d(path,deletePixMapAfterLoad,loadedFileList, this->sourceLoader); } else { throw runtime_error("Unknown model format: " + extension); } } void Model::save(const string &path, string convertTextureToFormat, bool keepsmallest) { string extension= path.substr(path.find_last_of('.')+1); if(extension=="g3d" ||extension=="G3D") { saveG3d(path,convertTextureToFormat,keepsmallest); } else { throw runtime_error("Unknown model format: " + extension); } } //load a model from a g3d file void Model::loadG3d(const string &path, bool deletePixMapAfterLoad, std::map > > *loadedFileList, string sourceLoader) { try{ #ifdef WIN32 FILE *f= _wfopen(utf8_decode(path).c_str(), L"rb"); #else FILE *f=fopen(path.c_str(),"rb"); #endif if (f == NULL) { printf("In [%s::%s] cannot load file = [%s]\n",__FILE__,__FUNCTION__,path.c_str()); throw runtime_error("Error opening g3d model file [" + path + "]"); } if(loadedFileList) { (*loadedFileList)[path].push_back(make_pair(sourceLoader,sourceLoader)); } string dir= extractDirectoryPathFromFile(path); //file header FileHeader fileHeader; size_t readBytes = fread(&fileHeader, sizeof(FileHeader), 1, f); if(strncmp(reinterpret_cast(fileHeader.id), "G3D", 3) != 0) { fclose(f); f = NULL; printf("In [%s::%s] file = [%s] fileheader.id = [%s][%c]\n",__FILE__,__FUNCTION__,path.c_str(),reinterpret_cast(fileHeader.id),fileHeader.id[0]); throw runtime_error("Not a valid G3D model"); } fileVersion= fileHeader.version; if(SystemFlags::VERBOSE_MODE_ENABLED) printf("Load model, fileVersion = %d\n",fileVersion); //version 4 if(fileHeader.version == 4) { //model header ModelHeader modelHeader; readBytes = fread(&modelHeader, sizeof(ModelHeader), 1, f); meshCount= modelHeader.meshCount; if(SystemFlags::VERBOSE_MODE_ENABLED) printf("meshCount = %d\n",meshCount); if(modelHeader.type != mtMorphMesh) { throw runtime_error("Invalid model type"); } //load meshes meshes= new Mesh[meshCount]; for(uint32 i = 0; i < meshCount; ++i) { meshes[i].load(i, dir, f, textureManager,deletePixMapAfterLoad, loadedFileList,sourceLoader); meshes[i].buildInterpolationData(); } } //version 3 else if(fileHeader.version == 3) { readBytes = fread(&meshCount, sizeof(meshCount), 1, f); if(SystemFlags::VERBOSE_MODE_ENABLED) printf("meshCount = %d\n",meshCount); meshes= new Mesh[meshCount]; for(uint32 i = 0; i < meshCount; ++i) { meshes[i].loadV3(i, dir, f, textureManager,deletePixMapAfterLoad, loadedFileList,sourceLoader); meshes[i].buildInterpolationData(); } } //version 2 else if(fileHeader.version == 2) { readBytes = fread(&meshCount, sizeof(meshCount), 1, f); if(SystemFlags::VERBOSE_MODE_ENABLED) printf("meshCount = %d\n",meshCount); meshes= new Mesh[meshCount]; for(uint32 i = 0; i < meshCount; ++i){ meshes[i].loadV2(i,dir, f, textureManager,deletePixMapAfterLoad, loadedFileList,sourceLoader); meshes[i].buildInterpolationData(); } } else { throw runtime_error("Invalid model version: "+ intToStr(fileHeader.version)); } fclose(f); } catch(exception &e){ SystemFlags::OutputDebug(SystemFlags::debugError,"In [%s::%s Line: %d] Error [%s]\n",__FILE__,__FUNCTION__,__LINE__,e.what()); throw runtime_error("Exception caught loading 3d file: " + path +"\n"+ e.what()); } } //save a model to a g3d file void Model::saveG3d(const string &path, string convertTextureToFormat, bool keepsmallest) { string tempModelFilename = path + "cvt"; #ifdef WIN32 FILE *f= _wfopen(utf8_decode(tempModelFilename).c_str(), L"wb"); #else FILE *f= fopen(tempModelFilename.c_str(), "wb"); #endif if(f == NULL) { throw runtime_error("Cant open file for writing: [" + tempModelFilename + "]"); } convertTextureToFormat = toLower(convertTextureToFormat); //file header FileHeader fileHeader; fileHeader.id[0]= 'G'; fileHeader.id[1]= '3'; fileHeader.id[2]= 'D'; fileHeader.version= 4; fwrite(&fileHeader, sizeof(FileHeader), 1, f); // file versions if(fileHeader.version == 4 || fileHeader.version == 3 || fileHeader.version == 2) { //model header ModelHeader modelHeader; modelHeader.meshCount = meshCount; modelHeader.type = mtMorphMesh; fwrite(&modelHeader, sizeof(ModelHeader), 1, f); std::map textureDeleteList; for(uint32 i = 0; i < meshCount; ++i) { meshes[i].save(i,tempModelFilename, f, textureManager, convertTextureToFormat,textureDeleteList, keepsmallest); } removeFile(path); if(renameFile(tempModelFilename,path) == true) { // Now delete old textures since they were converted to a new format for(std::map::iterator iterMap = textureDeleteList.begin(); iterMap != textureDeleteList.end(); ++iterMap) { removeFile(iterMap->first); } } } else { throw runtime_error("Invalid model version: "+ intToStr(fileHeader.version)); } fclose(f); } void Model::deletePixels() { for(uint32 i = 0; i < meshCount; ++i) { meshes[i].deletePixels(); } } bool PixelBufferWrapper::isPBOEnabled = false; int PixelBufferWrapper::index = 0; vector PixelBufferWrapper::pboIds; PixelBufferWrapper::PixelBufferWrapper(int pboCount,int bufferSize) { //if(isGlExtensionSupported("GL_ARB_pixel_buffer_object") == true && if(GLEW_ARB_pixel_buffer_object) { PixelBufferWrapper::isPBOEnabled = true; cleanup(); // For some wacky reason this fails in VC++ 2008 //pboIds.reserve(pboCount); //glGenBuffersARB(pboCount, (GLuint*)&pboIds[0]); // for(int i = 0; i < pboCount; ++i) { pboIds.push_back(0); glGenBuffersARB(1, (GLuint*)&pboIds[i]); // create pixel buffer objects, you need to delete them when program exits. // glBufferDataARB with NULL pointer reserves only memory space. glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, pboIds[i]); glBufferDataARB(GL_PIXEL_PACK_BUFFER_ARB, bufferSize, 0, GL_STREAM_READ_ARB); } glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0); } } Pixmap2D *PixelBufferWrapper::getPixelBufferFor(int x,int y,int w,int h, int colorComponents) { Pixmap2D *pixmapScreenShot = NULL; if(PixelBufferWrapper::isPBOEnabled == true) { // increment current index first then get the next index // "index" is used to read pixels from a framebuffer to a PBO // "nextIndex" is used to process pixels in the other PBO index = (index + 1) % 2; // pbo index used for next frame int nextIndex = (index + 1) % 2; // read framebuffer /////////////////////////////// // copy pixels from framebuffer to PBO // Use offset instead of pointer. // OpenGL should perform asynch DMA transfer, so glReadPixels() will return immediately. glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, pboIds[index]); //glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, pboIds[nextIndex]); //glPixelStorei(GL_PACK_ALIGNMENT, 1); glReadPixels(x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, 0); //glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // measure the time reading framebuffer //t1.stop(); //readTime = t1.getElapsedTimeInMilliSec(); // process pixel data ///////////////////////////// //t1.start(); // map the PBO that contain framebuffer pixels before processing it //glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, pboIds[nextIndex]); glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, pboIds[index]); GLubyte* src = (GLubyte*)glMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY_ARB); if(src) { pixmapScreenShot = new Pixmap2D(w+1, h+1, colorComponents); memcpy(pixmapScreenShot->getPixels(),src,(size_t)pixmapScreenShot->getPixelByteCount()); glUnmapBufferARB(GL_PIXEL_PACK_BUFFER_ARB); // release pointer to the mapped buffer //pixmapScreenShot->save("debugPBO.png"); } // measure the time reading framebuffer //t1.stop(); //processTime = t1.getElapsedTimeInMilliSec(); glBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, 0); } return pixmapScreenShot; } void PixelBufferWrapper::begin() { if(PixelBufferWrapper::isPBOEnabled == true) { // set the framebuffer to read //glReadBuffer(GL_FRONT); } } void PixelBufferWrapper::end() { if(PixelBufferWrapper::isPBOEnabled == true) { // set the framebuffer to read //glReadBuffer(GL_BACK); } } void PixelBufferWrapper::cleanup() { if(PixelBufferWrapper::isPBOEnabled == true) { if(pboIds.empty() == false) { glDeleteBuffersARB(pboIds.size(), &pboIds[0]); pboIds.clear(); } } } PixelBufferWrapper::~PixelBufferWrapper() { cleanup(); } //unsigned char BaseColorPickEntity::nextColorID[COLOR_COMPONENTS] = {1, 1, 1, 1}; unsigned char BaseColorPickEntity::nextColorID[COLOR_COMPONENTS] = { 1, 1, 1 }; Mutex BaseColorPickEntity::mutexNextColorID; auto_ptr BaseColorPickEntity::pbo; BaseColorPickEntity::BaseColorPickEntity() { MutexSafeWrapper safeMutex(&mutexNextColorID); uniqueColorID[0] = nextColorID[0]; uniqueColorID[1] = nextColorID[1]; uniqueColorID[2] = nextColorID[2]; //uniqueColorID[3] = nextColorID[3]; const int colorSpacing = 2; if(nextColorID[0] + colorSpacing <= 255) { nextColorID[0] += colorSpacing; } else { nextColorID[0] = 1; if(nextColorID[1] + colorSpacing <= 255) { nextColorID[1] += colorSpacing; } else { nextColorID[1] = 1; if(nextColorID[2] + colorSpacing <= 255) { nextColorID[2] += colorSpacing; } else { //printf("Color rolled over on 3rd level!\n"); nextColorID[0] = 1; nextColorID[1] = 1; nextColorID[2] = 1; // nextColorID[2] = 1; // nextColorID[3]+=colorSpacing; // // if(nextColorID[3] > 255) { // nextColorID[0] = 1; // nextColorID[1] = 1; // nextColorID[2] = 1; // nextColorID[3] = 1; // } } } } } void BaseColorPickEntity::init(int bufferSize) { if(BaseColorPickEntity::pbo.get() == NULL) { BaseColorPickEntity::pbo.reset(new PixelBufferWrapper(2,bufferSize)); } } string BaseColorPickEntity::getColorDescription() const { string result = ""; char szBuf[100]=""; //sprintf(szBuf,"%d.%d.%d.%d",uniqueColorID[0],uniqueColorID[1],uniqueColorID[2],uniqueColorID[3]); sprintf(szBuf,"%d.%d.%d",uniqueColorID[0],uniqueColorID[1],uniqueColorID[2]); result = szBuf; return result; } void BaseColorPickEntity::beginPicking() { // turn off texturing, lighting and fog //glClearColor (0.0,0.0,0.0,0.0); //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //glClear(GL_COLOR_BUFFER_BIT); glDisable(GL_TEXTURE_2D); glDisable(GL_FOG); glDisable(GL_LIGHTING); glDisable(GL_BLEND); glDisable(GL_MULTISAMPLE); glDisable(GL_DITHER); } void BaseColorPickEntity::endPicking() { // turn off texturing, lighting and fog glEnable(GL_TEXTURE_2D); glEnable(GL_FOG); glEnable(GL_LIGHTING); glEnable(GL_BLEND); glEnable(GL_MULTISAMPLE); glEnable(GL_DITHER); } vector BaseColorPickEntity::getPickedList(int x,int y,int w,int h, const vector &rendererModels) { vector pickedModels; //printf("In [%s::%s] Line: %d\n",__FILE__,__FUNCTION__,__LINE__); //static auto_ptr cachedPixels; static auto_ptr cachedPixels; //static int cachedPixelsW = -1; //static int cachedPixelsH = -1; //printf("In [%s::%s] Line: %d\n",__FILE__,__FUNCTION__,__LINE__); static Chrono lastSnapshot(true); const int selectionMillisecondUpdate = 100; if(PixelBufferWrapper::getIsPBOEnable() == true) { // Only update the pixel buffer every x milliseconds or as required if(cachedPixels.get() == NULL || cachedPixels->getW() != w+1 ||cachedPixels->getH() != h+1 || lastSnapshot.getMillis() > selectionMillisecondUpdate) { //printf("Updating selection millis = %ld\n",lastSnapshot.getMillis()); lastSnapshot.reset(); //Pixmap2D *pixmapScreenShot = BaseColorPickEntity::pbo->getPixelBufferFor(x,y,w,h, COLOR_COMPONENTS); cachedPixels.reset(BaseColorPickEntity::pbo->getPixelBufferFor(x,y,w,h, COLOR_COMPONENTS)); //cachedPixels.reset(new unsigned char[(unsigned int)pixmapScreenShot->getPixelByteCount()]); //memcpy(cachedPixels.get(),pixmapScreenShot->getPixels(),(size_t)pixmapScreenShot->getPixelByteCount()); //cachedPixelsW = w+1; //cachedPixelsH = h+1; //delete pixmapScreenShot; } } else { // Only update the pixel buffer every x milliseconds or as required if(cachedPixels.get() == NULL || cachedPixels->getW() != w+1 ||cachedPixels->getH() != h+1 || lastSnapshot.getMillis() > selectionMillisecondUpdate) { //printf("Updating selection millis = %ld\n",lastSnapshot.getMillis()); lastSnapshot.reset(); //Pixmap2D *pixmapScreenShot = new Pixmap2D(w+1, h+1, COLOR_COMPONENTS); cachedPixels.reset(new Pixmap2D(w+1, h+1, COLOR_COMPONENTS)); //glPixelStorei(GL_PACK_ALIGNMENT, 1); //glReadPixels(x, y, w, h, GL_RGB, GL_UNSIGNED_BYTE, pixmapScreenShot->getPixels()); //glReadPixels(x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixmapScreenShot->getPixels()); glReadPixels(x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, cachedPixels->getPixels()); //glPixelStorei(GL_UNPACK_ALIGNMENT, 1); //cachedPixels.reset(new unsigned char[(unsigned int)pixmapScreenShot->getPixelByteCount()]); //memcpy(cachedPixels.get(),pixmapScreenShot->getPixels(),(size_t)pixmapScreenShot->getPixelByteCount()); //cachedPixelsW = w+1; //cachedPixelsH = h+1; //delete pixmapScreenShot; } } unsigned char *pixelBuffer = cachedPixels->getPixels(); // Enable screenshots to debug selection scene //pixmapScreenShot->save("debug.png"); //printf("In [%s::%s] Line: %d x,y,w,h [%d,%d,%d,%d] pixels = %d\n",__FILE__,__FUNCTION__,__LINE__,x,y,w,h,pixmapScreenShot->getPixelByteCount()); // now our picked screen pixel color is stored in pixel[3] // so we search through our object list looking for the object that was selected map modelAlreadyPickedList; map > > colorAlreadyPickedList; int nEnd = w * h; for(int x = 0; x < nEnd && pickedModels.size() < rendererModels.size(); ++x) { int index = x * COLOR_COMPONENTS; unsigned char *pixel = &pixelBuffer[index]; // Skip duplicate scanned colors map > >::iterator iterFind1 = colorAlreadyPickedList.find(pixel[0]); if(iterFind1 != colorAlreadyPickedList.end()) { map >::iterator iterFind2 = iterFind1->second.find(pixel[1]); if(iterFind2 != iterFind1->second.end()) { map::iterator iterFind3 = iterFind2->second.find(pixel[2]); if(iterFind3 != iterFind2->second.end()) { continue; } } } for(unsigned int i = 0; i < rendererModels.size(); ++i) { // Skip models already selected if(modelAlreadyPickedList.find(i) != modelAlreadyPickedList.end()) { continue; } const BaseColorPickEntity *model = rendererModels[i]; if( model != NULL && model->isUniquePickingColor(pixel) == true) { //printf("Found match pixel [%d.%d.%d] for model [%s] ptr [%p][%s]\n",pixel[0],pixel[1],pixel[2],model->getColorDescription().c_str(), model,model->getUniquePickName().c_str()); pickedModels.push_back(i); modelAlreadyPickedList[i]=true; colorAlreadyPickedList[pixel[0]][pixel[1]][pixel[2]]=true; break; } } } //printf("In [%s::%s] Line: %d\n",__FILE__,__FUNCTION__,__LINE__); //delete pixmapScreenShot; //printf("In [%s::%s] Line: %d\n",__FILE__,__FUNCTION__,__LINE__); return pickedModels; } bool BaseColorPickEntity::isUniquePickingColor(unsigned char *pixel) const { bool result = false; if( uniqueColorID[0] == pixel[0] && uniqueColorID[1] == pixel[1] && uniqueColorID[2] == pixel[2]) { //uniqueColorID[3] == pixel[3]) { result = true; } return result; } void BaseColorPickEntity::setUniquePickingColor() const { glColor3f( uniqueColorID[0] / 255.0f, uniqueColorID[1] / 255.0f, uniqueColorID[2] / 255.0f); //uniqueColorID[3] / 255.0f); } }}//end namespace