//data loading bool ModelData::LoadIn(QString filepath) { unsigned int m; unsigned int t; unsigned int i; Triangle3D newtri; const struct aiFace* face; this->filepath = filepath; if(filepath.isEmpty()) return false; //extract filename from path! filename = QFileInfo(filepath).baseName(); //AI_CONFIG_PP_FD_REMOVE = aiPrimitiveType_POINTS | aiPrimitiveType_LINES; pScene = aiImportFile(filepath.toAscii(), aiProcess_Triangulate);// | aiProcess_JoinIdenticalVertices); //trian if(pScene == NULL)//assimp cant handle the file - lets try our own reader. { //display Assimp Error QMessageBox msgBox; msgBox.setText("Assimp Error: " + QString().fromAscii(aiGetErrorString())); msgBox.exec(); aiReleaseImport(pScene); return false; } qDebug() << "Model imported with " << pScene->mMeshes[0]->mNumFaces << " faces."; for (m = 0; m < pScene->mNumMeshes; m++) { const aiMesh* mesh = pScene->mMeshes[m]; for (t = 0; t < mesh->mNumFaces; t++) { face = &mesh->mFaces[t]; if(face->mNumIndices == 3) { for(i = 0; i < face->mNumIndices; i++) { int index = face->mIndices[i]; newtri.normal.setX(mesh->mNormals[index].x); newtri.normal.setY(mesh->mNormals[index].y); newtri.normal.setZ(mesh->mNormals[index].z); newtri.vertex[i].setX(mesh->mVertices[index].x); newtri.vertex[i].setY(mesh->mVertices[index].y); newtri.vertex[i].setZ(mesh->mVertices[index].z); } newtri.UpdateBounds(); triList.push_back(newtri); } } } aiReleaseImport(pScene); qDebug() << "Loaded triangles: " << triList.size(); //now center it! CenterModel(); //generate a displaylist int displayerror = FormDisplayList(); //check for errors in display list creation (if its a large model the card may run out of memory. if(displayerror){ while(displayerror)//loop and see if there are additional errors as well. { //display Assimp Error qDebug() << "Display List Error: " << displayerror; //write to log as well. QMessageBox msgBox; switch(displayerror) { case GL_OUT_OF_MEMORY: msgBox.setText("OpenGL Error: GL_OUT_OF_MEMORY\nModel is too large to render on your graphics card."); break; case GL_INVALID_ENUM: msgBox.setText("OpenGL Error: GL_INVALID_ENUM"); break; case GL_INVALID_VALUE: msgBox.setText("OpenGL Error: GL_INVALID_VALUE"); break; case GL_INVALID_FRAMEBUFFER_OPERATION: msgBox.setText("OpenGL Error: GL_INVALID_FRAMEBUFFER_OPERATION"); break; case GL_STACK_UNDERFLOW: msgBox.setText("OpenGL Error: GL_STACK_UNDERFLOW"); break; case GL_STACK_OVERFLOW: msgBox.setText("OpenGL Error: GL_STACK_OVERFLOW"); break; default: break; } msgBox.exec(); displayerror = glGetError(); } return false; } return true; }
int main(int argc, char **argv) { FILE *file; const struct aiScene *scene; char *p; int c; char *output = NULL; char *input = NULL; int onlyanim = 0; int onlymesh = 0; while ((c = getopt(argc, argv, "AHMPSabflmn:o:rvxsu:")) != -1) { switch (c) { case 'A': save_all_bones++; break; case 'H': dohips = 1; break; case 'M': list_all_meshes = 1; break; case 'P': list_all_positions = 1; break; case 'S': dostatic = 1; break; case 'a': onlyanim = 1; break; case 'm': onlymesh = 1; break; case 'n': only_one_node = optarg++; break; case 'b': need_to_bake_skin = 1; break; case 'o': output = optarg++; break; case 'f': doflip = 0; break; case 'r': dorigid = 1; break; case 'l': dolowprec = 1; break; case 'v': verbose++; break; case 'x': doaxis = 1; break; case 's': dounscale = 1; break; case 'u': untaglist[numuntags++] = optarg++; break; default: usage(); break; } } if (optind == argc) usage(); input = argv[optind++]; p = strrchr(input, '/'); if (!p) p = strrchr(input, '\\'); if (!p) p = input; else p++; strcpy(basename, p); p = strrchr(basename, '.'); if (p) *p = 0; numtags = argc - optind; taglist = argv + optind; /* Read input file and post process */ int flags = 0; flags |= aiProcess_JoinIdenticalVertices; flags |= aiProcess_GenSmoothNormals; flags |= aiProcess_GenUVCoords; flags |= aiProcess_TransformUVCoords; flags |= aiProcess_LimitBoneWeights; //flags |= aiProcess_FindInvalidData; flags |= aiProcess_ImproveCacheLocality; //flags |= aiProcess_RemoveRedundantMaterials; //flags |= aiProcess_OptimizeMeshes; fprintf(stderr, "loading %s\n", input); scene = aiImportFile(input, flags); if (!scene) { fprintf(stderr, "cannot import '%s': %s\n", input, aiGetErrorString()); exit(1); } if (scene->mNumAnimations > 0) doanim = 1; if (onlymesh) { domesh = 1; doanim = 0; } if (onlyanim) { domesh = 0; doanim = 1; } if (getenv("DOANIM")) doanim = 1; // Convert to Z-UP coordinate system aiMultiplyMatrix4(&scene->mRootNode->mTransformation, &yup_to_zup); // Build a list of bones and compute the bind pose matrices. if (build_bone_list(scene) > 0) dobone = 1; if (dostatic) { dobone = 0; need_to_bake_skin = 0; } if (list_all_meshes) { export_mesh_list(scene); return 0; } if (list_all_positions) { export_position_list(scene); return 0; } // Mesh is split with incompatible bind matrices, so pick a new // bind pose and deform the mesh to fit. if (need_to_bake_skin && !onlyanim) { apply_initial_frame(); // ditch original bind pose bake_scene_skin(scene); } /* * Export scene as mesh/skeleton/animation */ if (output) { fprintf(stderr, "saving %s\n", output); file = fopen(output, "w"); if (!file) { fprintf(stderr, "cannot open output file: '%s'\n", output); exit(1); } } else { file = stdout; } fprintf(file, "# Inter-Quake Export\n"); if (dobone) { export_bone_list(file); } if (domesh) { struct aiMatrix4x4 identity; aiIdentityMatrix4(&identity); export_custom_vertexarrays(file, scene); export_node(file, scene, scene->mRootNode, identity, "SCENE"); } if (dobone) { if (doanim) { export_animations(file, scene); } } if (output) fclose(file); aiReleaseImport(scene); return 0; }
ModelAndTextureLoader::ModelAndTextureLoader(const char* TextureDirectory,const char* fullPathToModel) { m_nbTotalMesh = 0; m_allMeshes = NULL; //force to recompute normal tangent and bitangent //Assimp::Importer::SetPropertyInteger(AI_CONFIG_PP_RVC_FLAGS, aiComponent_NORMALS); //aiSetImportPropertyInteger(AI_CONFIG_PP_RVC_FLAGS, aiComponent_NORMALS); //aiSetImportPropertyInteger(AI_CONFIG_PP_RVC_FLAGS, aiComponent_TANGENTS_AND_BITANGENTS); m_assimpScene = aiImportFile(fullPathToModel, aiProcessPreset_TargetRealtime_MaxQuality|aiProcess_PreTransformVertices); // | aiProcess_RemoveComponent | aiProcess_CalcTangentSpace | aiProcess_GenSmoothNormals //force to recompute normal tangent and bitangent // ); if ( m_assimpScene ) { struct aiVector3D scene_min, scene_max, scene_center; get_bounding_box(&scene_min,&scene_max); m_vCenter.x = (scene_min.x + scene_max.x) / 2.0f; m_vCenter.y = (scene_min.y + scene_max.y) / 2.0f; m_vCenter.z = (scene_min.z + scene_max.z) / 2.0f; m_fSize = scene_max.x-scene_min.x; m_fSize = aisgl_max(scene_max.y - scene_min.y,m_fSize); m_fSize = aisgl_max(scene_max.z - scene_min.z,m_fSize); RecursiveMesh_CountTotalMesh(m_assimpScene,m_assimpScene->mRootNode); m_allMeshes = (MESH*)malloc(sizeof(MESH) * m_nbTotalMesh); unsigned int iMesh = 0; RecursiveMesh_Loading(m_assimpScene,m_assimpScene->mRootNode,&iMesh); //load textures m_textureOfEachMaterial = new MATERIAL_TEXTUREID[m_assimpScene->mNumMaterials]; for (unsigned int m=0; m<m_assimpScene->mNumMaterials; m++) { int texIndex; texIndex = 0; while (true) { aiString path; aiReturn texFound = m_assimpScene->mMaterials[m]->GetTexture(aiTextureType_DIFFUSE, texIndex, &path); if ( texFound == AI_SUCCESS ) { m_textureIdMap[path.data] = NULL; texIndex++; } else{break;} } texIndex = 0; texIndex = 0; while (true) { aiString path; aiReturn texFound = m_assimpScene->mMaterials[m]->GetTexture(aiTextureType_SPECULAR, texIndex, &path); if ( texFound == AI_SUCCESS ) { m_textureIdMap[path.data] = NULL; texIndex++; } else{break;} } texIndex = 0; while (true) { aiString path; aiReturn texFound = m_assimpScene->mMaterials[m]->GetTexture(aiTextureType_AMBIENT, texIndex, &path); if ( texFound == AI_SUCCESS ) { m_textureIdMap[path.data] = NULL; texIndex++; } else{break;} } texIndex = 0; while (true) { aiString path; aiReturn texFound = m_assimpScene->mMaterials[m]->GetTexture(aiTextureType_EMISSIVE, texIndex, &path); if ( texFound == AI_SUCCESS ) { m_textureIdMap[path.data] = NULL; texIndex++; } else{break;} } texIndex = 0; while (true) { aiString path; aiReturn texFound = m_assimpScene->mMaterials[m]->GetTexture(aiTextureType_NORMALS, texIndex, &path); if ( texFound == AI_SUCCESS ) { m_textureIdMap[path.data] = NULL; texIndex++; } else{break;} } texIndex = 0; while (true) { aiString path; aiReturn texFound = m_assimpScene->mMaterials[m]->GetTexture(aiTextureType_HEIGHT, texIndex, &path); if ( texFound == AI_SUCCESS ) { m_textureIdMap[path.data] = NULL; texIndex++; } else{break;} } } int numTextures = int(m_textureIdMap.size()); std::map<std::string, GLuint*>::iterator itr = m_textureIdMap.begin(); m_textureIds = new GLuint[numTextures]; glGenTextures(numTextures, m_textureIds); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); for (int i=0; i<numTextures; i++) { std::string filename = (*itr).first; (*itr).second = &m_textureIds[i]; gli::texture2D textureFileLoaded = gli::load(std::string(TextureDirectory) + filename); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, m_textureIds[i]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); gli::texture2D::format_type formatImage = textureFileLoaded.format(); if ( formatImage == gli::RGB8U ) { glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,textureFileLoaded[0].dimensions().x,textureFileLoaded[0].dimensions().y,0,GL_RGB,GL_UNSIGNED_BYTE,textureFileLoaded[0].data()); } else if ( formatImage == gli::RGBA8U ) { glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,textureFileLoaded[0].dimensions().x,textureFileLoaded[0].dimensions().y,0,GL_RGBA,GL_UNSIGNED_BYTE,textureFileLoaded[0].data()); } glBindTexture(GL_TEXTURE_2D, 0); itr++; } glPixelStorei(GL_UNPACK_ALIGNMENT, 4); for (unsigned int m=0; m<m_assimpScene->mNumMaterials; m++) { aiString path; aiReturn texFound = m_assimpScene->mMaterials[m]->GetTexture(aiTextureType_DIFFUSE, 0, &path); if ( texFound == AI_SUCCESS ) { m_textureOfEachMaterial[m].idDiffuse = *m_textureIdMap[path.data] ; } else{ m_textureOfEachMaterial[m].idDiffuse = 0;} texFound = m_assimpScene->mMaterials[m]->GetTexture(aiTextureType_SPECULAR, 0, &path); if ( texFound == AI_SUCCESS ) { m_textureOfEachMaterial[m].idSpecular = *m_textureIdMap[path.data] ; } else{ m_textureOfEachMaterial[m].idSpecular = 0;} texFound = m_assimpScene->mMaterials[m]->GetTexture(aiTextureType_AMBIENT, 0, &path); if ( texFound == AI_SUCCESS ) { m_textureOfEachMaterial[m].idAmbiant = *m_textureIdMap[path.data] ; } else{ m_textureOfEachMaterial[m].idAmbiant = 0;} texFound = m_assimpScene->mMaterials[m]->GetTexture(aiTextureType_EMISSIVE, 0, &path); if ( texFound == AI_SUCCESS ) { m_textureOfEachMaterial[m].idEmissive = *m_textureIdMap[path.data] ; } else{ m_textureOfEachMaterial[m].idEmissive = 0;} texFound = m_assimpScene->mMaterials[m]->GetTexture(aiTextureType_NORMALS, 0, &path); if ( texFound == AI_SUCCESS ) { m_textureOfEachMaterial[m].idNormal = *m_textureIdMap[path.data] ; } else { //height could be considered as normals texFound = m_assimpScene->mMaterials[m]->GetTexture(aiTextureType_HEIGHT, 0, &path); if ( texFound == AI_SUCCESS ) { m_textureOfEachMaterial[m].idNormal = *m_textureIdMap[path.data] ; } else { m_textureOfEachMaterial[m].idNormal = 0; } } } } }
bool Animated_Mesh::Load_Assimp(const std::string& file){ const aiScene* load =aiImportFile(file.c_str(), aiProcessPreset_TargetRealtime_MaxQuality | aiProcess_ConvertToLeftHanded ); if(load==NULL) { OUTPUT_DEBUG_MSG("Could not load the Model in Animated_Mesh::Load_MyFormat file: '" + file+"'"); return false; } size_t numverts(0), currentvertex(0), currentindex(0), numindices(0); bool hasbones=false; // go through the mesh counting all the verts and indices that I will need for (unsigned int i = 0; i < load->mNumMeshes;++i) { numverts+=load->mMeshes[i]->mNumVertices; numindices+=load->mMeshes[i]->mNumFaces*3; if(load->mMeshes[i]->HasBones()) hasbones=true; } if(!hasbones) { aiReleaseImport(load);// free the resrouces OUTPUT_DEBUG_MSG("Could not load the Model in Animated_Mesh::Load_MyFormat file: '" + file+"', there were no bones deteected."); return false; } IB.Stride=2; if(numverts >= 65536) IB.Stride=4; Vertices.resize(numverts); Indices.resize((IB.Stride/2)*numindices); std::vector<Vertex_Types::Pos_Tex_Norm_Tang_Bone_Weight> tempverts(numverts); std::vector<std::string> bonenames; for (unsigned int i = 0; i < load->mNumMeshes;++i){ Graphics::Texture diffuse, normal; const aiMesh* mesh = load->mMeshes[i]; if(mesh->mPrimitiveTypes != aiPrimitiveType_TRIANGLE) { OUTPUT_DEBUG_MSG("There are errors with this submesh, named: "<<mesh->mName.data<<" Please, fix it"); if(mesh->mPrimitiveTypes == aiPrimitiveType_LINE){ OUTPUT_DEBUG_MSG("Problem: The mesh containes lines when it should only contain triangles"); }else { OUTPUT_DEBUG_MSG("Problem: The mesh containes points when it should only contain triangles"); } continue; } if (!mesh->HasTextureCoords(0)) { OUTPUT_DEBUG_MSG("There are errors with this submesh, named: "<<mesh->mName.data<<" Please, fix it"); OUTPUT_DEBUG_MSG("Problem: The mesh containes no texcoords, which means there will just be color displayed. This engine does not support color mesh displays, only textured mesh!"); continue; } if(!mesh->HasTangentsAndBitangents()) { OUTPUT_DEBUG_MSG("There are errors with this submesh, named: "<<mesh->mName.data<<" Please, fix it"); OUTPUT_DEBUG_MSG("Problem: Tangents were not created. No known fix"); continue; } OUTPUT_DEBUG_MSG("Loading "<<mesh->mNumBones<<" bones . . ."); for( unsigned int a = 0; a < mesh->mNumBones; a++) { const aiBone* bone = mesh->mBones[a]; size_t bonein(-1); for(size_t ib(0); ib< bonenames.size(); ib++){ std::string tname = bone->mName.data; if(tname == bonenames[ib]){// found the bone.. break bonein=ib; break; } } if(bonein ==-1){// did not find the bone, this is a new one push back bonein = bonenames.size();// get the index before insertion bonenames.push_back(bone->mName.data); } // there should only be 4 per vertex here because assimp guaranteees it, but if there are more, we are ok for( unsigned int b = 0; b < bone->mNumWeights; b++){ if( tempverts[bone->mWeights[b].mVertexId+ currentvertex].Weights.x <= 0.f) { tempverts[bone->mWeights[b].mVertexId+ currentvertex].Bones[0] = static_cast<float>(bonein); tempverts[bone->mWeights[b].mVertexId+ currentvertex].Weights.x = bone->mWeights[b].mWeight; } else if( tempverts[bone->mWeights[b].mVertexId+ currentvertex].Weights.y <= 0.f){ tempverts[bone->mWeights[b].mVertexId+ currentvertex].Bones[1] = static_cast<float>(bonein); tempverts[bone->mWeights[b].mVertexId+ currentvertex].Weights.y = bone->mWeights[b].mWeight; } else if( tempverts[bone->mWeights[b].mVertexId+ currentvertex].Weights.z <= 0.f){ tempverts[bone->mWeights[b].mVertexId+ currentvertex].Bones[2] = static_cast<float>(bonein); tempverts[bone->mWeights[b].mVertexId+ currentvertex].Weights.z = bone->mWeights[b].mWeight; } else if( tempverts[bone->mWeights[b].mVertexId+ currentvertex].Weights.w <= 0.f){ tempverts[bone->mWeights[b].mVertexId+ currentvertex].Bones[3] = static_cast<float>(bonein); tempverts[bone->mWeights[b].mVertexId+ currentvertex].Weights.w = bone->mWeights[b].mWeight; } } } for (unsigned int x = 0; x < mesh->mNumVertices;++x){ Vertices[x + currentvertex] = tempverts[x + currentvertex].Pos = *reinterpret_cast<vec3*>(&mesh->mVertices[x]); tempverts[x + currentvertex].Tex = *reinterpret_cast<vec2*>(&mesh->mTextureCoords[0][x]); tempverts[x + currentvertex].Norm = *reinterpret_cast<vec3*>(&mesh->mNormals[x]); tempverts[x + currentvertex].Tang = *reinterpret_cast<vec3*>(&mesh->mTangents[x]); } // check whether we can use 16 bit indices for our format... the ASSIMPOBLARBLA uses 32 bit indices for all theirs.. if (IB.Stride == 4){ uint32_t* pbData = reinterpret_cast<uint32_t*>(&Indices[currentindex]); for (unsigned int x = 0; x < mesh->mNumFaces;++x){ for (unsigned int a = 0; a < 3 ;++a) { *pbData++ = static_cast<uint32_t>(mesh->mFaces[x].mIndices[a]+ currentvertex); } } } else { uint16_t* pbData = reinterpret_cast<uint16_t*>(&Indices[currentindex]); for (unsigned int x = 0; x < mesh->mNumFaces;++x){ for (unsigned int a = 0; a < 3 ;++a) { *pbData++ = static_cast<uint16_t>(mesh->mFaces[x].mIndices[a]+ currentvertex); } } } //load the textures std::string pathtomodel(GetPath(file)); Batch *batch = new Batch(); LoadMaterials(mesh, load->mMaterials, batch, pathtomodel, Asset_Dir); batch->NumIndices=mesh->mNumFaces*3; batch->StartIndex = static_cast<uint32_t>(currentindex); batch->NumVerts= mesh->mNumVertices; // make sure to increment the ref count for thesse so they are properly destroyed currentvertex+=mesh->mNumVertices; currentindex+=mesh->mNumFaces*3; //For now, there will be a new shader for each material. I will create a shader cache where the graphics lib will cache the shaders and issue out already created shaders like it does with textures. Graphics::Shader_Macro macro1[] = { {"NORMALMAP", "1" }, {"MATRIX_PALETTE_SIZE_DEFAULT", "60" }, {NULL, NULL} }; Graphics::Shader_Macro macro0[] = { {"NORMALMAP", "0" }, {"MATRIX_PALETTE_SIZE_DEFAULT", "60" }, {NULL, NULL} }; Graphics::Shader_Macro* ptr = nullptr; if(batch->Has_NormalMap()) ptr = macro1; else ptr = macro0; batch->GetVS()->CompileShaderFromFile("Animated_Mesh.fx", "VS", "vs_4_0", ptr); FormatDesc lay[] = { FormatDesc(), FormatDesc(TYPE_TEXCOORD, FORMAT_FLOAT, 2), FormatDesc(TYPE_NORMAL, FORMAT_FLOAT, 3), FormatDesc(TYPE_TANGENT, FORMAT_FLOAT, 3), FormatDesc(TYPE_BONE, FORMAT_FLOAT, 4), FormatDesc(TYPE_WEIGHT, FORMAT_FLOAT, 4) }; batch->GetVS()->CreateInputLayout(lay, sizeof(lay)/sizeof(FormatDesc)); batch->GetPS()->CompileShaderFromFile("Animated_Mesh.fx", "PS", "ps_4_0", ptr); Batches.push_back(batch); } Animatior.Init(load); aiReleaseImport(load);// free the resrouces if(currentvertex==0) {// this could happen, if so GET OUTOF HERE OUTPUT_DEBUG_MSG("Problem loading the mesh, there were no vertices loaded. Failed to load the mesh"); return false; } VB[0].Create(currentvertex, sizeof(Vertex_Types::Pos_Tex_Norm_Tang_Bone_Weight), VERTEX_BUFFER, IMMUTABLE, CPU_NONE, &tempverts[0] ); IB.Create(currentindex, IB.Stride, INDEX_BUFFER, IMMUTABLE, CPU_NONE, &Indices[0]); // create index buffer! OUTPUT_DEBUG_MSG("Finished Loading the Mesh"); Name=FileName =file; return true; }