std::vector<Ygg::ConverterData>* Ygg::Loader::LoadScene(const std::string filename, std::vector<Entity> *entity_list) { Assimp::Importer importer; importer.SetPropertyInteger(AI_CONFIG_PP_SLM_VERTEX_LIMIT, 65536); importer.SetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_POINT | aiPrimitiveType_LINE); const aiScene* ascene = importer.ReadFile(filename, aiProcess_CalcTangentSpace | aiProcess_Triangulate | aiProcess_FindDegenerates | aiProcess_SplitLargeMeshes | aiProcess_JoinIdenticalVertices | aiProcess_SortByPType); if (!ascene) { std::fprintf(stderr, "%s\n", importer.GetErrorString()); return &this->results; } // Get path for relative textures int split = (int)filename.rfind("/"); std::string path = filename.substr(0, split + 1); // Now we can access the file's contents. this->ParseScene(ascene, entity_list); return &this->results; }
Node *Model::load(Scene &scene, Node &root, const std::string &prefix, const std::string &filename, const unsigned int options) { unsigned int pflags; unsigned int remove_flags; std::string full_name = prefix + "/" + filename; this->prefix = prefix; if (!file_exists(full_name)) { std::cout << "Model file '" << full_name << "' does not exist. Exiting ..." << std::endl; exit(-1); } pflags = aiProcess_Triangulate | aiProcess_GenSmoothNormals | aiProcess_CalcTangentSpace | aiProcess_FindDegenerates | aiProcess_JoinIdenticalVertices | aiProcess_FlipUVs | aiProcess_LimitBoneWeights | aiProcess_RemoveComponent | aiProcess_FixInfacingNormals; remove_flags = aiComponent_COLORS | aiComponent_LIGHTS | aiComponent_CAMERAS; if (options & MODEL_IMPORT_OPTIMIZED) { pflags |= aiProcess_OptimizeGraph | aiProcess_OptimizeMeshes | aiProcess_RemoveComponent | aiProcess_FindInstances; } if (options & MODEL_IMPORT_LIGHTS) { remove_flags = aiComponent_COLORS | aiComponent_CAMERAS; } Assimp::Importer importer; importer.SetPropertyInteger(AI_CONFIG_PP_LBW_MAX_WEIGHTS, 3); importer.SetPropertyInteger(AI_CONFIG_PP_RVC_FLAGS, remove_flags); assimp_scene = importer.ReadFile(full_name.c_str(), pflags); if (!assimp_scene) { std::cout << "Error parsing '" << full_name.c_str() << "': " << importer.GetErrorString() << std::endl; exit(-1); } Node *rootPtr = node_map_create(scene, *assimp_scene->mRootNode, &root, root.tree_level_get(), options); // rootPtr->transform_update_global_recursive(*rootPtr); BoneForAssimpBone boneForAssimpBone; bone_map_create(scene, boneForAssimpBone, options); materials_parse(scene); lights_parse(scene); mesh_create_all(scene, *assimp_scene->mRootNode, boneForAssimpBone); key_frames_parse(); return rootPtr; }
int LoadScene(tchar const *filename, aiScene const **scene) { if(scene == null || filename == null) { return E_POINTER; } Assimp::Importer *importer = new Assimp::Importer(); importer->SetIOHandler(new MyIOSystem()); importer->SetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_LINE | aiPrimitiveType_POINT); #if defined(_DEBUG) DefaultLogger::create("", Logger::VERBOSE, aiDefaultLogStream_DEBUGGER); #endif importer->ReadFile(filename, aiProcess_Triangulate | aiProcess_SortByPType); #if defined(_DEBUG) DefaultLogger::kill(); #endif if(importer->GetScene() == null) { TRACE("Error loading %s: %s\n", filename, importer->GetErrorString()); return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); } *scene = importer->GetScene(); return S_OK; }
//////////////////////////////////////////////////////////////////////// /// /// @fn void Modele3D::charger(Path cheminFichier) /// /// Cette fonction charge un modèle 3D à partir d'un fichier supporté /// par la librairie 'assimp'. Les textures OpenGL afférentes sont /// également chargées. /// /// @param[in] nomFichier : nom du fichier modèle (normalement .obj /// ou .dae) /// /// @return Aucune. /// //////////////////////////////////////////////////////////////////////// void Modele3D::charger(Path cheminFichier) { /// Ne pas charger le même fichier inutilement if (cheminFichier_ == cheminFichier) return; cheminFichier_ = std::move(cheminFichier); /// Ne pas conserver les identifiants de texture d'un ancien modèle libererTextures(); Assimp::Importer importer; /// Lors de l'importation, ne pas conserver les lignes et les points. importer.SetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_LINE | aiPrimitiveType_POINT); /// Le flag aiProcess_Triangulate, inclus dans aiProcessPreset_TargetRealtime_Quality, /// fera en sorte que les mesh ne comporteront que des triangles. const aiScene* scene{ importer.ReadFile(cheminFichier_, aiProcessPreset_TargetRealtime_Quality) }; if (scene == nullptr) { utilitaire::afficherErreur(std::string{ "Impossible de charger l'objet 3d : " } +cheminFichier.filename() + std::string{ "." }); return; } /// Charger l'ensemble des textures contenues dans le modèle : chargerTexturesExternes(scene); chargerTexturesIntegrees(scene); /// Chargement des données du modèle 3D racine_ = Noeud{ scene, scene->mRootNode }; }
SharedPointer< Group > SceneImporter::import( std::string filename ) { // check if file exists std::ifstream fin( filename ); bool exists = !fin.fail(); fin.close(); if ( !exists ) { // Is it ok to throw exceptions? throw FileNotFoundException( filename ); } Assimp::Importer importer; importer.SetPropertyInteger( AI_CONFIG_PP_SLM_VERTEX_LIMIT, 15000 ); const aiScene* importedScene = importer.ReadFile( filename, aiProcessPreset_TargetRealtime_MaxQuality ); if ( importedScene == nullptr ) { Log::error( CRIMILD_CURRENT_CLASS_NAME, "Error importing file ", filename, "\n", importer.GetErrorString() ); return nullptr; } auto skinnedMesh = crimild::alloc< SkinnedMesh >(); loadAnimations( importedScene, skinnedMesh ); auto root = crimild::alloc< Group >( filename ); auto basePath = FileSystem::getInstance().extractDirectory( filename ) + "/"; recursiveSceneBuilder( root, importedScene, importedScene->mRootNode, basePath, skinnedMesh ); if ( _skeleton != nullptr ) { Transformation globalInverseTransform; computeTransform( importedScene->mRootNode->mTransformation.Inverse(), globalInverseTransform ); _skeleton->setGlobalInverseTransform( globalInverseTransform ); root->attachComponent( _skeleton ); } return root; }
bool SceneLoader::LoadScene(const std::string& file_name, Scene& scene, std::string& status) { Assimp::Importer importer; importer.SetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_POINT | aiPrimitiveType_LINE); const aiScene* assimp_scene = importer.ReadFile(file_name, aiProcess_Triangulate | aiProcess_JoinIdenticalVertices | aiProcess_FixInfacingNormals | aiProcess_FindDegenerates | aiProcess_ValidateDataStructure); // | aiProcess_ImproveCacheLocality // | aiProcess_RemoveRedundantMaterials // | aiProcess_FixInfacingNormals | aiProcess_FindDegenerates // | aiProcess_OptimizeGraph | aiProcess_OptimizeMeshes); status = std::string(importer.GetErrorString()); if (!status.empty() || NULL == assimp_scene) { return false; } status = "OK"; for (uint32_t i = 0; i < assimp_scene->mNumCameras; ++i) { ImportCamera(scene, assimp_scene->mCameras[i]); } for (uint32_t i = 0; i < assimp_scene->mNumLights; ++i) { ImportLight(scene, assimp_scene->mLights[i]); } std::cout << "mNumMaterials = " << assimp_scene->mNumMaterials << std::endl; for (uint32_t i = 0; i < assimp_scene->mNumMaterials; ++i) { ImportMaterial(scene, assimp_scene->mMaterials[i]); } std::cout << "mNumMeshes = " << assimp_scene->mNumMeshes << std::endl; for (uint32_t i = 0; i < assimp_scene->mNumMeshes; ++i) { ImportMesh(scene, assimp_scene->mMeshes[i]); } return true; }
void LoadMeshFromColladaAssimp(const char* relativeFileName, btAlignedObjectArray<GLInstanceGraphicsShape>& visualShapes, btAlignedObjectArray<ColladaGraphicsInstance>& visualShapeInstances,btTransform& upAxisTrans, float& unitMeterScaling) { upAxisTrans.setIdentity(); unitMeterScaling=1; GLInstanceGraphicsShape* shape = 0; FILE* file = fopen(relativeFileName,"rb"); if (file) { int size=0; if (fseek(file, 0, SEEK_END) || (size = ftell(file)) == EOF || fseek(file, 0, SEEK_SET)) { printf("Error: Cannot access file to determine size of %s\n", relativeFileName); } else { if (size) { printf("Open DAE file of %d bytes\n",size); Assimp::Importer importer; //importer.SetPropertyInteger(AI_CONFIG_PP_RVC_FLAGS, aiComponent_NORMALS | aiComponent_COLORS); importer.SetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_LINE | aiPrimitiveType_POINT); // importer.SetPropertyInteger(AI_CONFIG_IMPORT_COLLADA_IGNORE_UP_DIRECTION, 1); aiScene const* scene = importer.ReadFile(relativeFileName, aiProcess_JoinIdenticalVertices | //aiProcess_RemoveComponent | aiProcess_SortByPType | aiProcess_Triangulate); if (scene) { shape = &visualShapes.expand(); shape->m_scaling[0] = 1; shape->m_scaling[1] = 1; shape->m_scaling[2] = 1; shape->m_scaling[3] = 1; int index = 0; shape->m_indices = new b3AlignedObjectArray<int>(); shape->m_vertices = new b3AlignedObjectArray<GLInstanceVertex>(); aiMatrix4x4 ident; addMeshParts(scene, scene->mRootNode, shape, ident); shape->m_numIndices = shape->m_indices->size(); shape->m_numvertices = shape->m_vertices->size(); ColladaGraphicsInstance& instance = visualShapeInstances.expand(); instance.m_shapeIndex = visualShapes.size()-1; } } } } }
// Loads a model with supported ASSIMP extensions from file and stores the resulting meshes in the meshes vector. void Model::loadModelFromFile(std::string path, bool flipUVs, bool forDistField) { _filename = path.substr(path.find_last_of('/'), path.length()); // Read file via ASSIMP Assimp::Importer importer; unsigned int flags = aiProcessPreset_TargetRealtime_Fast; if (forDistField) { flags = aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_PreTransformVertices | aiProcess_ValidateDataStructure | aiProcess_RemoveRedundantMaterials | aiProcess_FindDegenerates | aiProcess_SortByPType | aiProcess_FixInfacingNormals | aiProcess_GenSmoothNormals; //This with aiProcess_FindDegenerates and aiProcess_SortByPType will get rid of all degenerate triangles and not create lines or points. importer.SetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType::aiPrimitiveType_POINT | aiPrimitiveType::aiPrimitiveType_LINE); //Configures the #aiProcess_PretransformVertices step to normalize all vertex components into the[-1, 1] range. //importer.SetPropertyInteger(AI_CONFIG_PP_PTV_NORMALIZE, 1); } if (flipUVs) { flags |= aiProcess_FlipUVs; } const aiScene* scene = importer.ReadFile(path, flags); // Check for errors if (!scene || scene->mFlags == AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) // if is Not Zero { std::cout << "ERROR::ASSIMP:: " << importer.GetErrorString() << std::endl; return; } // Retrieve the directory path of the filepath this->_directory = path.substr(0, path.find_last_of('/')); //Process the materials this->processMaterials(scene); // Process ASSIMP's root node recursively glm::mat4 identity; this->processNode(scene->mRootNode, scene, identity); for (auto m : _meshes) { m->bindAttributesToVAO(); } }
ModelInterface::ModelInterface(TextureManager* manager, const QString filename) : has_animations(false) , meshes(0) , animations(0) , bones(0) , textureManager(manager) { if(filename.contains("/")) { int index = filename.lastIndexOf("/") + 1; filePath = filename.left(index); fileName = filename.right(filename.size() - index); } else fileName = filename; Assimp::Importer importer; importer.SetPropertyInteger(AI_CONFIG_PP_LBW_MAX_WEIGHTS, 4); const aiScene* scene = importer.ReadFile(filename.toStdString(), aiProcess_GenSmoothNormals | aiProcess_Triangulate | aiProcess_CalcTangentSpace/* | aiProcess_FlipUVs*/); if(!scene || scene->mFlags == AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) { throw QString("The file wasn't successfuly opened"); return; } if(scene->HasAnimations()) { has_animations = true; bones = loadBones(scene); buildSkeleton(scene->mRootNode, NULL); animations = new Animations(); animations->setBones(bones); for(uint i = 0; i < scene->mNumAnimations; ++i) animations->add(loadAnimation(scene->mAnimations[i], i)); } meshes = new Meshes(); for(uint i = 0; i < scene->mNumMeshes; ++i) meshes->add(loadMesh(scene->mMeshes[i], scene->mMaterials[scene->mMeshes[i]->mMaterialIndex], i)); initialize(scene); // try delete scene probably crash }
std::vector<std::shared_ptr<RenderingObject>> LoadMesh(std::shared_ptr<ShaderProgram> inputShader, const std::string& filename, std::vector<std::shared_ptr<aiMaterial>>* outputMaterials) { #ifndef ASSET_PATH static_assert(false, "ASSET_PATH is not defined. Check to make sure your projects are setup correctly"); #endif Assimp::Importer importer; importer.SetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_LINE | aiPrimitiveType_POINT); const std::string completeFilename = std::string(STRINGIFY(ASSET_PATH)) + "/" + filename; const aiScene* scene = importer.ReadFile(completeFilename.c_str(), aiProcess_CalcTangentSpace | aiProcess_Triangulate | aiProcess_JoinIdenticalVertices | aiProcess_FixInfacingNormals | aiProcess_SortByPType); if (!scene) { std::cerr << "ERROR: Assimp failed -- " << importer.GetErrorString() << std::endl; return {}; } std::vector<std::shared_ptr<aiMaterial>> sceneMaterials; if (outputMaterials) { sceneMaterials.resize(scene->mNumMaterials); for (unsigned int m = 0; m < scene->mNumMaterials; ++m) { aiMaterial* material = scene->mMaterials[m]; std::shared_ptr<aiMaterial> dstMaterial = std::make_shared<aiMaterial>(); aiMaterial::CopyPropertyList(dstMaterial.get(), material); sceneMaterials[m] = dstMaterial; } } std::vector<std::shared_ptr<RenderingObject>> loadedMeshes; for (decltype(scene->mNumMeshes) i = 0; i < scene->mNumMeshes; ++i) { const aiMesh* mesh = scene->mMeshes[i]; if (!mesh->HasPositions()) { std::cerr << "WARNING: A mesh in " << filename << " does not have positions. Skipping." << std::endl; continue; } auto totalVertices = mesh->mNumVertices; std::unique_ptr<RenderingObject::PositionArray> positions = make_unique<RenderingObject::PositionArray>(totalVertices); std::unique_ptr<RenderingObject::NormalArray> normals = mesh->HasNormals() ? make_unique<RenderingObject::NormalArray>(totalVertices) : nullptr; std::unique_ptr<RenderingObject::UVArray> uv = mesh->HasTextureCoords(0) ? make_unique<RenderingObject::UVArray>(totalVertices) : nullptr; std::unique_ptr<RenderingObject::ColorArray> colors = mesh->HasVertexColors(0) ? make_unique<RenderingObject::ColorArray>(totalVertices) : nullptr; std::unique_ptr<RenderingObject::IndexArray> indices = mesh->HasFaces() ? make_unique<RenderingObject::IndexArray>(mesh->mNumFaces * 3) : nullptr; for (decltype(totalVertices) v = 0; v < totalVertices; ++v) { positions->at(v) = glm::vec4(mesh->mVertices[v].x, mesh->mVertices[v].y, mesh->mVertices[v].z, 1.f); if (normals) { normals->at(v) = glm::vec3(mesh->mNormals[v].x, mesh->mNormals[v].y, mesh->mNormals[v].z); } if (uv) { uv->at(v) = glm::vec2(mesh->mTextureCoords[0][v].x, mesh->mTextureCoords[0][v].y); } if (colors) { colors->at(v) = glm::vec4(mesh->mColors[0][v].r, mesh->mColors[0][v].g, mesh->mColors[0][v].b, mesh->mColors[0][v].a); } } for (decltype(mesh->mNumFaces) f = 0; f < mesh->mNumFaces && indices; ++f) { const aiFace& face = mesh->mFaces[f]; if (face.mNumIndices != 3) { std::cerr << "WARNING: Input mesh may not be triangulated. Skipping face with: " << face.mNumIndices << " vertices." << std::endl; continue; } for (int j = 0; j < 3; ++j) { indices->at(f * 3 + j) = face.mIndices[j]; } } std::shared_ptr<RenderingObject> createdMesh = std::make_shared<RenderingObject>(inputShader, std::move(positions), std::move(indices), std::move(normals), std::move(uv), std::move(colors)); loadedMeshes.push_back(std::move(createdMesh)); if (outputMaterials) { outputMaterials->push_back(sceneMaterials[mesh->mMaterialIndex]); } } return loadedMeshes; }
S3DModel* CAssParser::Load(const std::string& modelFilePath) { LOG_SL(LOG_SECTION_MODEL, L_INFO, "Loading model: %s", modelFilePath.c_str()); const std::string& modelPath = FileSystem::GetDirectory(modelFilePath); const std::string& modelName = FileSystem::GetBasename(modelFilePath); // Load the lua metafile. This contains properties unique to Spring models and must return a table std::string metaFileName = modelFilePath + ".lua"; if (!CFileHandler::FileExists(metaFileName, SPRING_VFS_ZIP)) { // Try again without the model file extension metaFileName = modelPath + '/' + modelName + ".lua"; } if (!CFileHandler::FileExists(metaFileName, SPRING_VFS_ZIP)) { LOG_SL(LOG_SECTION_MODEL, L_INFO, "No meta-file '%s'. Using defaults.", metaFileName.c_str()); } LuaParser metaFileParser(metaFileName, SPRING_VFS_MOD_BASE, SPRING_VFS_ZIP); if (!metaFileParser.Execute()) { LOG_SL(LOG_SECTION_MODEL, L_INFO, "'%s': %s. Using defaults.", metaFileName.c_str(), metaFileParser.GetErrorLog().c_str()); } // Get the (root-level) model table const LuaTable& modelTable = metaFileParser.GetRoot(); if (!modelTable.IsValid()) { LOG_SL(LOG_SECTION_MODEL, L_INFO, "No valid model metadata in '%s' or no meta-file", metaFileName.c_str()); } // Create a model importer instance Assimp::Importer importer; // Give the importer an IO class that handles Spring's VFS importer.SetIOHandler(new AssVFSSystem()); // Speed-up processing by skipping things we don't need importer.SetPropertyInteger(AI_CONFIG_PP_RVC_FLAGS, ASS_IMPORTER_OPTIONS); #ifndef BITMAP_NO_OPENGL { importer.SetPropertyInteger(AI_CONFIG_PP_SLM_VERTEX_LIMIT, maxVertices); importer.SetPropertyInteger(AI_CONFIG_PP_SLM_TRIANGLE_LIMIT, maxIndices / 3); } #endif // Read the model file to build a scene object LOG_SL(LOG_SECTION_MODEL, L_INFO, "Importing model file: %s", modelFilePath.c_str()); const aiScene* scene = nullptr; { // ASSIMP spams many SIGFPEs atm in normal & tangent generation ScopedDisableFpuExceptions fe; scene = importer.ReadFile(modelFilePath, ASS_POSTPROCESS_OPTIONS); } if (scene != nullptr) { LOG_SL(LOG_SECTION_MODEL, L_INFO, "Processing scene for model: %s (%d meshes / %d materials / %d textures)", modelFilePath.c_str(), scene->mNumMeshes, scene->mNumMaterials, scene->mNumTextures); } else { throw content_error("[AssimpParser] Model Import: " + std::string(importer.GetErrorString())); } ModelPieceMap pieceMap; ParentNameMap parentMap; S3DModel* model = new S3DModel(); model->name = modelFilePath; model->type = MODELTYPE_ASS; // Load textures FindTextures(model, scene, modelTable, modelPath, modelName); LOG_SL(LOG_SECTION_MODEL, L_INFO, "Loading textures. Tex1: '%s' Tex2: '%s'", model->texs[0].c_str(), model->texs[1].c_str()); texturehandlerS3O->PreloadTexture(model, modelTable.GetBool("fliptextures", true), modelTable.GetBool("invertteamcolor", true)); // Load all pieces in the model LOG_SL(LOG_SECTION_MODEL, L_INFO, "Loading pieces from root node '%s'", scene->mRootNode->mName.data); LoadPiece(model, scene->mRootNode, scene, modelTable, pieceMap, parentMap); // Update piece hierarchy based on metadata BuildPieceHierarchy(model, pieceMap, parentMap); CalculateModelProperties(model, modelTable); // Verbose logging of model properties LOG_SL(LOG_SECTION_MODEL, L_DEBUG, "model->name: %s", model->name.c_str()); LOG_SL(LOG_SECTION_MODEL, L_DEBUG, "model->numobjects: %d", model->numPieces); LOG_SL(LOG_SECTION_MODEL, L_DEBUG, "model->radius: %f", model->radius); LOG_SL(LOG_SECTION_MODEL, L_DEBUG, "model->height: %f", model->height); LOG_SL(LOG_SECTION_MODEL, L_DEBUG, "model->mins: (%f,%f,%f)", model->mins[0], model->mins[1], model->mins[2]); LOG_SL(LOG_SECTION_MODEL, L_DEBUG, "model->maxs: (%f,%f,%f)", model->maxs[0], model->maxs[1], model->maxs[2]); LOG_SL(LOG_SECTION_MODEL, L_INFO, "Model %s Imported.", model->name.c_str()); return model; }
std::shared_ptr<GeometryData::GenericObject> AssimpWrapper::LoadModel(std::string sFilename) { Assimp::Importer importer; importer.SetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_POINT | aiPrimitiveType_LINE ); const aiScene* scene = importer.ReadFile(sFilename, aiProcess_CalcTangentSpace | aiProcess_ValidateDataStructure | aiProcess_Triangulate | aiProcess_PreTransformVertices | //aiProcess_JoinIdenticalVertices | aiProcess_GenSmoothNormals | aiProcess_ImproveCacheLocality | aiProcess_FindInvalidData | //aiProcess_MakeLeftHanded | //aiProcess_OptimizeMeshes | //aiProcess_OptimizeGraph | aiProcess_GenUVCoords | aiProcess_TransformUVCoords | aiProcess_FlipUVs | aiProcess_FindDegenerates | aiProcess_SortByPType ); // If the import failed, report it if( !scene) Logger::error() << importer.GetErrorString() << Logger::endl; else Logger::debug() << "Loaded file " << sFilename << " with assimp" << Logger::endl; //bool bModelHasMeshes = scene->HasMeshes(); //bool bModelHasMaterials = scene->HasMaterials(); //bool bModelHasTextures = scene->HasTextures(); unsigned int nNumMeshes = scene->mNumMeshes; assert (nNumMeshes > 0); GeometryData::GenericObject *pObject = new GeometryData::GenericObject(nNumMeshes); std::shared_ptr<GeometryData::GenericObject> spObject(pObject); for (unsigned int i=0; i < nNumMeshes; i++) { aiMesh *pMesh = scene->mMeshes[i]; GeometryData::GenericMesh *pGenericMesh = std::shared_ptr<GeometryData::GenericMesh>(pObject->GetMesh(i)).get(); unsigned int nMaterialIndex = pMesh->mMaterialIndex; aiMaterial *pUsedMaterial = scene->mMaterials[nMaterialIndex]; // now fetch the material properties /* aiColor3D acDiffuse (0.f,0.f,0.f); bool bGotColorDiffuse = (AI_SUCCESS == pUsedMaterial->Get(AI_MATKEY_COLOR_DIFFUSE, acDiffuse)); float pfDiffuseColor[3] = { acDiffuse.r, acDiffuse.g, acDiffuse.b }; if (bGotColorDiffuse) { // pGenericMesh->AddAttributeValues(GeometryData::GenericData::DATA_MATERIAL_COLOR_DIFFUSE, 3, pfDiffuseColor); unsigned int nNumVertices = pMesh->mNumVertices; std::vector<float> vfFakedColor; //get vertices for (unsigned int ii=0; ii < nNumVertices; ii++) { vfFakedColor.push_back(pfDiffuseColor[0]); vfFakedColor.push_back(pfDiffuseColor[1]); vfFakedColor.push_back(pfDiffuseColor[2]); } pGenericMesh->AddAttributeValues(GeometryData::GenericData::DATA_COLORS, 3 * nNumVertices, &vfFakedColor[0]); } aiColor3D acSpecular (0.f, 0.f, 0.f); bool bGotSpecularColor = (AI_SUCCESS == pUsedMaterial->Get(AI_MATKEY_COLOR_SPECULAR, acSpecular)); float pfSpecularColor[3] = { acSpecular.r, acSpecular.g, acSpecular.b }; if (bGotSpecularColor) pGenericMesh->AddAttributeValues(GeometryData::GenericData::DATA_MATERIAL_COLOR_SPECULAR, 3, pfSpecularColor); float fShininess = 0.0f; bool bGotShininess = (AI_SUCCESS == pUsedMaterial->Get(AI_MATKEY_SHININESS, fShininess)); if (bGotShininess) pGenericMesh->AddAttributeValues(GeometryData::GenericData::DATA_MATERIAL_SHININESS, 1, &fShininess); float fShininessStrength = 0.0f; bool bGotShininessStrength = (AI_SUCCESS == pUsedMaterial->Get(AI_MATKEY_SHININESS_STRENGTH, fShininessStrength)); if (bGotShininessStrength) pGenericMesh->AddAttributeValues(GeometryData::GenericData::DATA_MATERIAL_SHININESS_STRENGTH, 1, &fShininessStrength);*/ assert (pMesh->HasPositions()); if (pMesh->HasPositions()) { // get vertices unsigned int nNumVertices = pMesh->mNumVertices; std::vector<float> vfVertices; //get vertices for (unsigned int ii=0; ii < nNumVertices; ii++) { vfVertices.push_back(pMesh->mVertices[ii].x); vfVertices.push_back(pMesh->mVertices[ii].y); vfVertices.push_back(pMesh->mVertices[ii].z); } pGenericMesh->AddAttributeValues(GeometryData::GenericData::DATA_VERTICES, 3 * nNumVertices, &vfVertices[0]); } if (pMesh->HasNormals()) { unsigned int nNumVertices = pMesh->mNumVertices; std::vector<float> vfNormals; //get vertices for (unsigned int ii=0; ii < nNumVertices; ii++) { vfNormals.push_back(pMesh->mNormals[ii].x); vfNormals.push_back(pMesh->mNormals[ii].y); vfNormals.push_back(pMesh->mNormals[ii].z); } pGenericMesh->AddAttributeValues(GeometryData::GenericData::DATA_NORMALS, 3 * nNumVertices, &vfNormals[0]); } if (pMesh->HasTangentsAndBitangents()) { unsigned int nNumVertices = pMesh->mNumVertices; std::vector<float> vfTangents; std::vector<float> vfBitangents; //get vertices for (unsigned int ii=0; ii < nNumVertices; ii++) { vfTangents.push_back(pMesh->mTangents[ii].x); vfTangents.push_back(pMesh->mTangents[ii].y); vfTangents.push_back(pMesh->mTangents[ii].z); vfBitangents.push_back(pMesh->mBitangents[ii].x); vfBitangents.push_back(pMesh->mBitangents[ii].y); vfBitangents.push_back(pMesh->mBitangents[ii].z); } pGenericMesh->AddAttributeValues(GeometryData::GenericData::DATA_TANGENTS, 3 * nNumVertices, &vfTangents[0]); pGenericMesh->AddAttributeValues(GeometryData::GenericData::DATA_BITANGENTS, 3 * nNumVertices, &vfBitangents[0]); } if (pMesh->GetNumColorChannels() > 1) Logger::error() << "The model " << sFilename << " has more than one color channel. Only 1 color channel is imported by AssimpWrapper" << Logger::endl; if (pMesh->HasVertexColors(0)) { unsigned int nNumVertices = pMesh->mNumVertices; std::vector<float> vfColors; //get vertices for (unsigned int ii=0; ii < nNumVertices; ii++) { vfColors.push_back(pMesh->mColors[0][ii].r); vfColors.push_back(pMesh->mColors[0][ii].g); vfColors.push_back(pMesh->mColors[0][ii].b); } pGenericMesh->AddAttributeValues(GeometryData::GenericData::DATA_COLORS, 3 * nNumVertices, &vfColors[0]); } // get indices unsigned int nNumFaces = pMesh->mNumFaces; std::vector<unsigned int> vIndicesVector; for (unsigned int ii=0; ii < nNumFaces; ii++) { assert (pMesh->mFaces[ii].mNumIndices == 3); vIndicesVector.push_back(pMesh->mFaces[ii].mIndices[0]); vIndicesVector.push_back(pMesh->mFaces[ii].mIndices[1]); vIndicesVector.push_back(pMesh->mFaces[ii].mIndices[2]); } pGenericMesh->AddIndices(nNumFaces * 3, &vIndicesVector[0]); if (sFilename == "models/pool_sphere.dae") { pGenericMesh->SetTexturePath(GeometryData::TextureNames::CUBEMAP, "abc"); } // get textures for (int iTextureType= (int) aiTextureType_DIFFUSE; iTextureType <= (int) aiTextureType_UNKNOWN; iTextureType++) { int iCount = pUsedMaterial->GetTextureCount((aiTextureType) iTextureType); if (iCount >= 1) { aiString sTexturePath; if (iCount > 1) Logger::debug() << "Model \"" << sFilename << "\" contains more than one texture per type. This is not supported yet." << Logger::endl; pUsedMaterial->GetTexture((aiTextureType) iTextureType, 0, &sTexturePath); GeometryData::TextureType tTextureType; bool bIgnoreTexture = false; // if the texture type is supported by GeometryData, set the corresponding type and path switch (iTextureType) { case aiTextureType_DIFFUSE: Logger::debug() << "diffuse " << std::string(sTexturePath.data) << Logger::endl; tTextureType = GeometryData::TextureNames::ALBEDO; break; case aiTextureType_NORMALS: Logger::debug() << "normals " << std::string(sTexturePath.data) << Logger::endl; tTextureType = GeometryData::TextureNames::NORMAL; break; case aiTextureType_SPECULAR: Logger::debug() << "specular " << std::string(sTexturePath.data) << Logger::endl; tTextureType = GeometryData::TextureNames::SPECULAR; break; case aiTextureType_HEIGHT: Logger::debug() << "height " << std::string(sTexturePath.data) << Logger::endl; tTextureType = GeometryData::TextureNames::NORMAL; break; case aiTextureType_DISPLACEMENT: Logger::debug() << "displacement " << std::string(sTexturePath.data) << Logger::endl; tTextureType = GeometryData::TextureNames::DISPLACE; break; default: bIgnoreTexture = true; } if (!bIgnoreTexture) { std::vector<float> vTextureCoords; if (pMesh->GetNumUVChannels() > 1) Logger::error() << "Model \"" << sFilename << "\" contains more than one uv channel. That's not supported by the importer yet." << Logger::endl; if (pMesh->GetNumUVChannels() >= 1) { for (unsigned int nVertex=0; nVertex < pMesh->mNumVertices; nVertex++) { vTextureCoords.push_back(pMesh->mTextureCoords[0][nVertex].x); vTextureCoords.push_back(pMesh->mTextureCoords[0][nVertex].y); } pGenericMesh->SetTextureCoords(tTextureType, vTextureCoords.size(), &vTextureCoords[0]); } pGenericMesh->SetTexturePath(tTextureType, std::string(sTexturePath.data)); } else { Logger::debug() << "Model \"" << sFilename << "\" contains some types of textures which are not supported by the importer yet" << Logger::endl; } } } } return spObject; }
bool Model::LoadAssimp(const char *filename) { Assimp::Importer importer; // remove unused data importer.SetPropertyInteger(AI_CONFIG_PP_RVC_FLAGS, aiComponent_COLORS | aiComponent_LIGHTS | aiComponent_CAMERAS); // max triangles and vertices per mesh, splits above this threshold importer.SetPropertyInteger(AI_CONFIG_PP_SLM_TRIANGLE_LIMIT, INT_MAX); importer.SetPropertyInteger(AI_CONFIG_PP_SLM_VERTEX_LIMIT, 0xfffe); // avoid the primitive restart index // remove points and lines importer.SetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_POINT | aiPrimitiveType_LINE); const aiScene *scene = importer.ReadFile(filename, aiProcess_CalcTangentSpace | aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_RemoveComponent | aiProcess_GenSmoothNormals | aiProcess_SplitLargeMeshes | aiProcess_ValidateDataStructure | //aiProcess_ImproveCacheLocality | // handled by optimizePostTransform() aiProcess_RemoveRedundantMaterials | aiProcess_SortByPType | aiProcess_FindInvalidData | aiProcess_GenUVCoords | aiProcess_TransformUVCoords | aiProcess_OptimizeMeshes | aiProcess_OptimizeGraph); if (scene == nullptr) return false; if (scene->HasTextures()) { // embedded textures... } if (scene->HasAnimations()) { // todo } m_Header.materialCount = scene->mNumMaterials; m_pMaterial = new Material [m_Header.materialCount]; memset(m_pMaterial, 0, sizeof(Material) * m_Header.materialCount); for (unsigned int materialIndex = 0; materialIndex < scene->mNumMaterials; materialIndex++) { const aiMaterial *srcMat = scene->mMaterials[materialIndex]; Material *dstMat = m_pMaterial + materialIndex; aiColor3D diffuse(1.0f, 1.0f, 1.0f); aiColor3D specular(1.0f, 1.0f, 1.0f); aiColor3D ambient(1.0f, 1.0f, 1.0f); aiColor3D emissive(0.0f, 0.0f, 0.0f); aiColor3D transparent(1.0f, 1.0f, 1.0f); float opacity = 1.0f; float shininess = 0.0f; float specularStrength = 1.0f; aiString texDiffusePath; aiString texSpecularPath; aiString texEmissivePath; aiString texNormalPath; aiString texLightmapPath; aiString texReflectionPath; srcMat->Get(AI_MATKEY_COLOR_DIFFUSE, diffuse); srcMat->Get(AI_MATKEY_COLOR_SPECULAR, specular); srcMat->Get(AI_MATKEY_COLOR_AMBIENT, ambient); srcMat->Get(AI_MATKEY_COLOR_EMISSIVE, emissive); srcMat->Get(AI_MATKEY_COLOR_TRANSPARENT, transparent); srcMat->Get(AI_MATKEY_OPACITY, opacity); srcMat->Get(AI_MATKEY_SHININESS, shininess); srcMat->Get(AI_MATKEY_SHININESS_STRENGTH, specularStrength); srcMat->Get(AI_MATKEY_TEXTURE(aiTextureType_DIFFUSE, 0), texDiffusePath); srcMat->Get(AI_MATKEY_TEXTURE(aiTextureType_SPECULAR, 0), texSpecularPath); srcMat->Get(AI_MATKEY_TEXTURE(aiTextureType_EMISSIVE, 0), texEmissivePath); srcMat->Get(AI_MATKEY_TEXTURE(aiTextureType_NORMALS, 0), texNormalPath); srcMat->Get(AI_MATKEY_TEXTURE(aiTextureType_LIGHTMAP, 0), texLightmapPath); srcMat->Get(AI_MATKEY_TEXTURE(aiTextureType_REFLECTION, 0), texReflectionPath); dstMat->diffuse = Vector3(diffuse.r, diffuse.g, diffuse.b); dstMat->specular = Vector3(specular.r, specular.g, specular.b); dstMat->ambient = Vector3(ambient.r, ambient.g, ambient.b); dstMat->emissive = Vector3(emissive.r, emissive.g, emissive.b); dstMat->transparent = Vector3(transparent.r, transparent.g, transparent.b); dstMat->opacity = opacity; dstMat->shininess = shininess; dstMat->specularStrength = specularStrength; char *pRem = nullptr; strncpy_s(dstMat->texDiffusePath, "models/", Material::maxTexPath - 1); strncat_s(dstMat->texDiffusePath, texDiffusePath.C_Str(), Material::maxTexPath - 1); pRem = strrchr(dstMat->texDiffusePath, '.'); while (pRem != nullptr && *pRem != 0) *(pRem++) = 0; // remove extension strncpy_s(dstMat->texSpecularPath, "models/", Material::maxTexPath - 1); strncat_s(dstMat->texSpecularPath, texSpecularPath.C_Str(), Material::maxTexPath - 1); pRem = strrchr(dstMat->texSpecularPath, '.'); while (pRem != nullptr && *pRem != 0) *(pRem++) = 0; // remove extension strncpy_s(dstMat->texEmissivePath, "models/", Material::maxTexPath - 1); strncat_s(dstMat->texEmissivePath, texEmissivePath.C_Str(), Material::maxTexPath - 1); pRem = strrchr(dstMat->texEmissivePath, '.'); while (pRem != nullptr && *pRem != 0) *(pRem++) = 0; // remove extension strncpy_s(dstMat->texNormalPath, "models/", Material::maxTexPath - 1); strncat_s(dstMat->texNormalPath, texNormalPath.C_Str(), Material::maxTexPath - 1); pRem = strrchr(dstMat->texNormalPath, '.'); while (pRem != nullptr && *pRem != 0) *(pRem++) = 0; // remove extension strncpy_s(dstMat->texLightmapPath, "models/", Material::maxTexPath - 1); strncat_s(dstMat->texLightmapPath, texLightmapPath.C_Str(), Material::maxTexPath - 1); pRem = strrchr(dstMat->texLightmapPath, '.'); while (pRem != nullptr && *pRem != 0) *(pRem++) = 0; // remove extension strncpy_s(dstMat->texReflectionPath, "models/", Material::maxTexPath - 1); strncat_s(dstMat->texReflectionPath, texReflectionPath.C_Str(), Material::maxTexPath - 1); pRem = strrchr(dstMat->texReflectionPath, '.'); while (pRem != nullptr && *pRem != 0) *(pRem++) = 0; // remove extension aiString matName; srcMat->Get(AI_MATKEY_NAME, matName); strncpy_s(dstMat->name, matName.C_Str(), Material::maxMaterialName - 1); } m_Header.meshCount = scene->mNumMeshes; m_pMesh = new Mesh [m_Header.meshCount]; memset(m_pMesh, 0, sizeof(Mesh) * m_Header.meshCount); // first pass, count everything for (unsigned int meshIndex = 0; meshIndex < scene->mNumMeshes; meshIndex++) { const aiMesh *srcMesh = scene->mMeshes[meshIndex]; Mesh *dstMesh = m_pMesh + meshIndex; assert(srcMesh->mPrimitiveTypes == aiPrimitiveType_TRIANGLE); dstMesh->materialIndex = srcMesh->mMaterialIndex; // just store everything as float. Can quantize in Model::optimize() dstMesh->attribsEnabled |= attrib_mask_position; dstMesh->attrib[attrib_position].offset = dstMesh->vertexStride; dstMesh->attrib[attrib_position].normalized = 0; dstMesh->attrib[attrib_position].components = 3; dstMesh->attrib[attrib_position].format = attrib_format_float; dstMesh->vertexStride += sizeof(float) * 3; dstMesh->attribsEnabled |= attrib_mask_texcoord0; dstMesh->attrib[attrib_texcoord0].offset = dstMesh->vertexStride; dstMesh->attrib[attrib_texcoord0].normalized = 0; dstMesh->attrib[attrib_texcoord0].components = 2; dstMesh->attrib[attrib_texcoord0].format = attrib_format_float; dstMesh->vertexStride += sizeof(float) * 2; dstMesh->attribsEnabled |= attrib_mask_normal; dstMesh->attrib[attrib_normal].offset = dstMesh->vertexStride; dstMesh->attrib[attrib_normal].normalized = 0; dstMesh->attrib[attrib_normal].components = 3; dstMesh->attrib[attrib_normal].format = attrib_format_float; dstMesh->vertexStride += sizeof(float) * 3; dstMesh->attribsEnabled |= attrib_mask_tangent; dstMesh->attrib[attrib_tangent].offset = dstMesh->vertexStride; dstMesh->attrib[attrib_tangent].normalized = 0; dstMesh->attrib[attrib_tangent].components = 3; dstMesh->attrib[attrib_tangent].format = attrib_format_float; dstMesh->vertexStride += sizeof(float) * 3; dstMesh->attribsEnabled |= attrib_mask_bitangent; dstMesh->attrib[attrib_bitangent].offset = dstMesh->vertexStride; dstMesh->attrib[attrib_bitangent].normalized = 0; dstMesh->attrib[attrib_bitangent].components = 3; dstMesh->attrib[attrib_bitangent].format = attrib_format_float; dstMesh->vertexStride += sizeof(float) * 3; // depth-only dstMesh->attribsEnabledDepth |= attrib_mask_position; dstMesh->attribDepth[attrib_position].offset = dstMesh->vertexStrideDepth; dstMesh->attribDepth[attrib_position].normalized = 0; dstMesh->attribDepth[attrib_position].components = 3; dstMesh->attribDepth[attrib_position].format = attrib_format_float; dstMesh->vertexStrideDepth += sizeof(float) * 3; // color rendering dstMesh->vertexDataByteOffset = m_Header.vertexDataByteSize; dstMesh->vertexCount = srcMesh->mNumVertices; dstMesh->indexDataByteOffset = m_Header.indexDataByteSize; dstMesh->indexCount = srcMesh->mNumFaces * 3; m_Header.vertexDataByteSize += dstMesh->vertexStride * dstMesh->vertexCount; m_Header.indexDataByteSize += sizeof(uint16_t) * dstMesh->indexCount; // depth-only rendering dstMesh->vertexDataByteOffsetDepth = m_Header.vertexDataByteSizeDepth; dstMesh->vertexCountDepth = srcMesh->mNumVertices; m_Header.vertexDataByteSizeDepth += dstMesh->vertexStrideDepth * dstMesh->vertexCountDepth; } // allocate storage m_pVertexData = new unsigned char [m_Header.vertexDataByteSize]; m_pIndexData = new unsigned char [m_Header.indexDataByteSize]; m_pVertexDataDepth = new unsigned char [m_Header.vertexDataByteSizeDepth]; m_pIndexDataDepth = new unsigned char [m_Header.indexDataByteSize]; // second pass, fill in vertex and index data for (unsigned int meshIndex = 0; meshIndex < scene->mNumMeshes; meshIndex++) { const aiMesh *srcMesh = scene->mMeshes[meshIndex]; Mesh *dstMesh = m_pMesh + meshIndex; float *dstPos = (float*)(m_pVertexData + dstMesh->vertexDataByteOffset + dstMesh->attrib[attrib_position].offset); float *dstTexcoord0 = (float*)(m_pVertexData + dstMesh->vertexDataByteOffset + dstMesh->attrib[attrib_texcoord0].offset); float *dstNormal = (float*)(m_pVertexData + dstMesh->vertexDataByteOffset + dstMesh->attrib[attrib_normal].offset); float *dstTangent = (float*)(m_pVertexData + dstMesh->vertexDataByteOffset + dstMesh->attrib[attrib_tangent].offset); float *dstBitangent = (float*)(m_pVertexData + dstMesh->vertexDataByteOffset + dstMesh->attrib[attrib_bitangent].offset); float *dstPosDepth = (float*)(m_pVertexDataDepth + dstMesh->vertexDataByteOffsetDepth + dstMesh->attribDepth[attrib_position].offset); for (unsigned int v = 0; v < dstMesh->vertexCount; v++) { if (srcMesh->mVertices) { dstPos[0] = srcMesh->mVertices[v].x; dstPos[1] = srcMesh->mVertices[v].y; dstPos[2] = srcMesh->mVertices[v].z; dstPosDepth[0] = srcMesh->mVertices[v].x; dstPosDepth[1] = srcMesh->mVertices[v].y; dstPosDepth[2] = srcMesh->mVertices[v].z; } else { // no position? That's kind of bad. assert(0); } dstPos = (float*)((unsigned char*)dstPos + dstMesh->vertexStride); dstPosDepth = (float*)((unsigned char*)dstPosDepth + dstMesh->vertexStrideDepth); if (srcMesh->mTextureCoords[0]) { dstTexcoord0[0] = srcMesh->mTextureCoords[0][v].x; dstTexcoord0[1] = srcMesh->mTextureCoords[0][v].y; } else { dstTexcoord0[0] = 0.0f; dstTexcoord0[1] = 0.0f; } dstTexcoord0 = (float*)((unsigned char*)dstTexcoord0 + dstMesh->vertexStride); if (srcMesh->mNormals) { dstNormal[0] = srcMesh->mNormals[v].x; dstNormal[1] = srcMesh->mNormals[v].y; dstNormal[2] = srcMesh->mNormals[v].z; } else { // Assimp should generate normals if they are missing, according to the postprocessing flag specified on load, // so we should never get here. assert(0); } dstNormal = (float*)((unsigned char*)dstNormal + dstMesh->vertexStride); if (srcMesh->mTangents) { dstTangent[0] = srcMesh->mTangents[v].x; dstTangent[1] = srcMesh->mTangents[v].y; dstTangent[2] = srcMesh->mTangents[v].z; } else { // TODO: generate tangents/bitangents if missing dstTangent[0] = 1.0f; dstTangent[1] = 0.0f; dstTangent[2] = 0.0f; } dstTangent = (float*)((unsigned char*)dstTangent + dstMesh->vertexStride); if (srcMesh->mBitangents) { dstBitangent[0] = srcMesh->mBitangents[v].x; dstBitangent[1] = srcMesh->mBitangents[v].y; dstBitangent[2] = srcMesh->mBitangents[v].z; } else { // TODO: generate tangents/bitangents if missing dstBitangent[0] = 0.0f; dstBitangent[1] = 1.0f; dstBitangent[2] = 0.0f; } dstBitangent = (float*)((unsigned char*)dstBitangent + dstMesh->vertexStride); } uint16_t *dstIndex = (uint16_t*)(m_pIndexData + dstMesh->indexDataByteOffset); uint16_t *dstIndexDepth = (uint16_t*)(m_pIndexDataDepth + dstMesh->indexDataByteOffset); for (unsigned int f = 0; f < srcMesh->mNumFaces; f++) { assert(srcMesh->mFaces[f].mNumIndices == 3); *dstIndex++ = srcMesh->mFaces[f].mIndices[0]; *dstIndex++ = srcMesh->mFaces[f].mIndices[1]; *dstIndex++ = srcMesh->mFaces[f].mIndices[2]; *dstIndexDepth++ = srcMesh->mFaces[f].mIndices[0]; *dstIndexDepth++ = srcMesh->mFaces[f].mIndices[1]; *dstIndexDepth++ = srcMesh->mFaces[f].mIndices[2]; } } ComputeAllBoundingBoxes(); return true; }
/** * Load the model. * Return true on success, false on failure. **/ bool AssimpModel::load(Render3D* render, bool meshdata, AssimpLoadFlags flags) { Assimp::Importer importer; importer.SetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_POINT | aiPrimitiveType_LINE); importer.SetPropertyInteger(AI_CONFIG_PP_RVC_FLAGS, aiComponent_COLORS | aiComponent_LIGHTS | aiComponent_CAMERAS); importer.SetPropertyInteger(AI_CONFIG_PP_SLM_VERTEX_LIMIT, 65535); importer.SetPropertyInteger(AI_CONFIG_PP_SLM_TRIANGLE_LIMIT, 65535); unsigned int assflags = aiProcess_CalcTangentSpace | aiProcess_Triangulate | aiProcess_JoinIdenticalVertices | aiProcess_SortByPType | aiProcess_FlipUVs | aiProcess_FindDegenerates | aiProcess_ImproveCacheLocality | aiProcess_RemoveComponent; // For map meshes we flatten geometry if (flags & ALM_FlattenGeometry) { assflags |= aiProcess_PreTransformVertices; assflags |= aiProcess_RemoveRedundantMaterials; assflags |= aiProcess_OptimizeMeshes; assflags |= aiProcess_FindInvalidData; assflags |= aiProcess_SplitLargeMeshes; } // Normally models are re-centered; this isn't always deisred. if (flags & ALF_NoRecenter) { this->recenter = false; } // Read the file from the mod Sint64 len; Uint8 * data = this->mod->loadBinary("models/" + this->name, &len); if (! data) { this->mod->setLoadErr("Failed to load %s; file read failed", this->name.c_str()); return false; } // Check we aren't larger than size_t if (len > MAX_FILE_SIZE) { this->mod->setLoadErr("Failed to load %s; file too large", this->name.c_str()); return false; } // Do the import const struct aiScene* sc = importer.ReadFileFromMemory((const void*) data, (size_t)len, assflags, this->name.c_str()); if (!sc) { this->mod->setLoadErr("Failed to load %s; file read failed; %s", this->name.c_str(), importer.GetErrorString()); return false; } free(data); if (debug_enabled("loadbones")) { cout << endl << this->name << endl; } if (render != NULL && render->is3D()) { this->loadMeshes(true, sc); this->loadMaterials(render, sc); } else { this->loadMeshes(false, sc); } if (meshdata) { this->loadMeshdata(render != NULL, sc); } this->loadNodes(sc); this->loadAnimations(sc); this->calcBoundingBox(sc); this->setBoneNodes(); return true; }
void Mesh::bind() { Assimp::Importer importer; unsigned int flags = aiProcess_Triangulate | aiProcess_JoinIdenticalVertices; if (generateVertexNormals) { importer.SetPropertyInteger(AI_CONFIG_PP_RVC_FLAGS, aiComponent_NORMALS); importer.SetPropertyFloat(AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE, 30.0f); // ignore normals in file and generate per vertex normals flags |= aiProcess_RemoveComponent | aiProcess_GenSmoothNormals; } else { // generate face normals if not already present in the file flags |= aiProcess_GenNormals; } const aiScene* scene = importer.ReadFile(fileName, flags); if (!scene) { return; } std::vector<float> vertices; std::vector<float> normals; std::vector<float> textureCoordinates; min.x = 1e38; min.y = 1e38; min.z = 1e38; max.x = -1e38; max.y = -1e38; max.z = -1e38; const struct aiNode* nd = scene->mRootNode; for (unsigned int n = 0; n < nd->mNumMeshes; ++n) { const struct aiMesh* mesh = scene->mMeshes[nd->mMeshes[n]]; std::cout << "vertices: " << mesh->mNumVertices << ", faces: " << mesh->mNumFaces << std::endl; for (unsigned int t = 0; t < mesh->mNumFaces; ++t) { const struct aiFace* face = &mesh->mFaces[t]; switch (face->mNumIndices) { case 3: break; default: std::cout << "ignored" << std::endl; continue; } for (unsigned int idx= 0;idx < face->mNumIndices;idx++) { float x = mesh->mVertices[face->mIndices[idx]].x; float y = mesh->mVertices[face->mIndices[idx]].y; float z = mesh->mVertices[face->mIndices[idx]].z; float nx = mesh->mNormals[face->mIndices[idx]].x; float ny = mesh->mNormals[face->mIndices[idx]].y; float nz = mesh->mNormals[face->mIndices[idx]].z; if (x < min.x) { min.x = x; } if (y < min.y) { min.y = y; } if (z < min.z) { min.z = z; } if (x > max.x) { max.x = x; } if (y > max.y) { max.y = y; } if (z > max.z) { max.z = z; } vertices.push_back(x); vertices.push_back(y); vertices.push_back(z); vertices.push_back(1.0f); normals.push_back(nx); normals.push_back(ny); normals.push_back(nz); normals.push_back(0.0f); textureCoordinates.push_back(x / 8); textureCoordinates.push_back(z / 8); } } } std::cout << "bounding box: min = " << min.x << ", " << min.y << ", " << min.z << " - max = " << max.x << ", " << max.y << ", " << max.z << std::endl; // vertices.clear(); // normals.clear(); // quad(vertices, normals, 0, 1, 2, 3); // quad(vertices, normals, 0, 4, 5, 1); // quad(vertices, normals, 1, 5, 6, 2); // quad(vertices, normals, 2, 6, 7, 3); // quad(vertices, normals, 3, 7, 4, 0); // quad(vertices, normals, 4, 7, 6, 5); count = vertices.size(); glGenBuffers(1, &vertexBuffer); glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), &vertices[0], GL_STATIC_DRAW); glGenBuffers(1, &normalBuffer); glBindBuffer(GL_ARRAY_BUFFER, normalBuffer); glBufferData(GL_ARRAY_BUFFER, normals.size() * sizeof(float), &normals[0], GL_STATIC_DRAW); glGenBuffers(1, &textureBuffer); glBindBuffer(GL_ARRAY_BUFFER, textureBuffer); glBufferData(GL_ARRAY_BUFFER, textureCoordinates.size() * sizeof(float), &textureCoordinates[0], GL_STATIC_DRAW); }
bool StaticMesh::loadFromFile(const std::string& infile) { Assimp::Importer imp; // Load scene and grab first mesh only imp.SetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_LINE | aiPrimitiveType_POINT | aiPrimitiveType_POLYGON, nullptr); #ifdef DEBUG std::cout << "Calling Assimp::Importer::ReadFile...\n"; #endif // DEBUG const aiScene *scene = imp.ReadFile(infile, aiProcessPreset_TargetRealtime_Quality & (!aiProcess_SplitLargeMeshes)); #ifdef DEBUG std::cout << "Checking for a mesh...\n"; #endif // DEBUG if(scene == nullptr) return false; else if(!scene->HasMeshes()) return false; imp.ApplyPostProcessing(aiProcess_JoinIdenticalVertices); // Will only load one mesh const aiMesh *mesh = scene->mMeshes[0]; #ifdef DEBUG std::cout << "Checking mesh has what we need...\n"; #endif // DEBUG // Don't process special mesh types if(!mesh->HasPositions() || !mesh->HasFaces()) return false; vertexCount = mesh->mNumVertices; triangleCount = mesh->mNumFaces; // Check what attributes are in the mesh and // calculate total count of floats std::size_t componentCount = components.vertex = 3; if(mesh->HasNormals()) { componentCount += components.normal = 3; #ifdef DEBUG std::cout << "Has normals\n"; #endif // DEBUG } if(mesh->HasTangentsAndBitangents()) { componentCount += (components.tangentBitangent = 3) * 2; #ifdef DEBUG std::cout << "Has tangents and bitangents\n"; #endif // DEBUG } if(mesh->HasTextureCoords(0)) { componentCount += components.texture = 3; #ifdef DEBUG std::cout << "Has texture coordinates\n"; #endif // DEBUG } // Scale size for size of float std::size_t totalSize = componentCount * sizeof(float) * vertexCount; #ifdef DEBUG std::cout << totalSize << std::endl; std::cout << "Allocating and mapping vertex buffer...\n"; #endif // DEBUG // Set size and map buffer glBindBuffer(GL_ARRAY_BUFFER, buffer); glBufferData(GL_ARRAY_BUFFER, totalSize, nullptr, GL_STATIC_DRAW); GLfloat *mapPtr = (GLfloat*) glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); // glNamedBufferDataEXT(buffer, totalSize, nullptr, GL_STATIC_DRAW); // GLfloat *mapPtr = (GLfloat*) glMapNamedBufferEXT(buffer, GL_WRITE_ONLY); if(mapPtr == nullptr) return false; #ifdef DEBUG std::cout << "Worked!\n"; #endif // DEBUG // Load all attributes into mapped buffer for(unsigned int i = 0; i < vertexCount; ++i) { GLfloat *head = mapPtr + i * componentCount; *head++ = mesh->mVertices[i].x; *head++ = mesh->mVertices[i].y; *head++ = mesh->mVertices[i].z; if(mesh->HasNormals()) { *head++ = mesh->mNormals[i].x; *head++ = mesh->mNormals[i].y; *head++ = mesh->mNormals[i].z; } if(mesh->HasTextureCoords(0)) { *head++ = mesh->mTextureCoords[0][i].x; *head++ = mesh->mTextureCoords[0][i].y; *head++ = mesh->mTextureCoords[0][i].z; } if(mesh->HasTangentsAndBitangents()) { *head++ = mesh->mTangents[i].x; *head++ = mesh->mTangents[i].y; *head++ = mesh->mTangents[i].z; *head++ = mesh->mBitangents[i].x; *head++ = mesh->mBitangents[i].y; *head++ = mesh->mBitangents[i].z; } } if(!glUnmapBuffer(GL_ARRAY_BUFFER)) return false; #ifdef DEBUG std::cout << "Allocating and mapping index buffer...\n"; #endif // DEBUG // Same for index buffer glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indices); glBufferData(GL_ELEMENT_ARRAY_BUFFER, triangleCount*3*sizeof(GLshort), nullptr, GL_STATIC_DRAW); GLushort *indexMapPtr = (GLushort*) glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY); // glNamedBufferDataEXT(indices, triangleCount * 3 * sizeof(GLushort), nullptr, GL_STATIC_DRAW); // GLushort *indexMapPtr = (GLushort*) glMapNamedBufferEXT(indices, GL_WRITE_ONLY); if(indexMapPtr == nullptr) return false; #ifdef DEBUG std::cout << "Worked!\n"; #endif // DEBUG for(unsigned int i = 0; i < triangleCount; ++i) { #ifdef DEBUG assert(mesh->mFaces[i].mNumIndices == 3); #endif // DEBUG unsigned int *indexArray = mesh->mFaces[i].mIndices; for(unsigned int j = 0; j < 3; ++j) { #ifdef DEBUG assert(*indexArray <= std::numeric_limits<GLushort>::max()); #endif // DEBUG *indexMapPtr++ = *indexArray++; } } if(!glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER)) return false;; #ifdef DEBUG std::cout << "Loading mesh successful!\n"; std::cout << "Setting up vertex array...\n"; #endif // DEBUG glBindVertexArray(vertexArray); glBindVertexBuffer(0, buffer, 0, (components.vertex + components.normal + components.texture + components.tangentBitangent * 2) * sizeof(GLfloat)); glVertexAttribFormat(0, 3, GL_FLOAT, GL_FALSE, 0); glVertexAttribBinding(0, 0); glEnableVertexAttribArray(0); if(components.normal) { glVertexAttribFormat(1, 3, GL_FLOAT, GL_FALSE, components.vertex * sizeof(float)); glVertexAttribBinding(1, 0); glEnableVertexAttribArray(1); } if(components.texture) { glVertexAttribFormat(2, 3, GL_FLOAT, GL_FALSE, (components.vertex + components.normal) * sizeof(float)); glVertexAttribBinding(2, 0); glEnableVertexAttribArray(2); } if(components.tangentBitangent) { glVertexAttribFormat(3, 3, GL_FLOAT, GL_FALSE, (components.vertex + components.normal + components.texture) * sizeof(float)); glVertexAttribBinding(3, 0); glEnableVertexAttribArray(3); glVertexAttribFormat(4, 3, GL_FLOAT, GL_FALSE, (components.vertex + components.normal + components.texture + components.tangentBitangent) * sizeof(float)); glVertexAttribBinding(4, 0); glEnableVertexAttribArray(4); } glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indices); glBindVertexArray(0); #ifdef DEBUG std::cout << vertexCount << ' ' << triangleCount << std::endl; std::cout << +components.vertex << ' '; std::cout << +components.normal << ' '; std::cout << +components.texture << ' '; std::cout << +components.tangentBitangent << std::endl; #endif // DEBUG return true; }
bool NvSimpleMeshLoader::LoadFile(LPWSTR szFilename) { bool bLoaded = false; (void)bLoaded; // Create a logger instance Assimp::DefaultLogger::create("",Logger::VERBOSE); // Create an instance of the Importer class Assimp::Importer importer; CHAR szFilenameA[MAX_PATH]; WideCharToMultiByte(CP_ACP,0,szFilename,MAX_PATH,szFilenameA,MAX_PATH,NULL,false); mediaPath = szFilenameA; auto index = mediaPath.find_last_of('\\'); if(index != -1) mediaPath = mediaPath.substr(0,index) + "\\"; else mediaPath = ".\\"; // Set some flags for the removal of various data that we don't use importer.SetPropertyInteger("AI_CONFIG_PP_RVC_FLAGS",aiComponent_ANIMATIONS | aiComponent_BONEWEIGHTS | aiComponent_COLORS | aiComponent_LIGHTS | aiComponent_CAMERAS); //importer.SetPropertyInteger("AI_CONFIG_PP_SBP_REMOVE",aiPrimitiveType_POINTS | aiPrimitiveType_LINES ); // load the scene and preprocess it into the form we want const aiScene *scene = importer.ReadFile(szFilenameA, aiProcess_Triangulate| // only higher order primitives will be triangulated aiProcess_GenNormals| // if normals exist, will not be generated aiProcess_CalcTangentSpace| aiProcess_PreTransformVertices| // rolls all node hierarchy(if existant) into the local space of meshes //aiProcess_RemoveRedundantMaterials| //aiProcess_FixInfacingNormals| aiProcess_FindDegenerates| aiProcess_SortByPType| aiProcess_RemoveComponent| // processes the above flags set to remove data we don't want aiProcess_FindInvalidData| aiProcess_GenUVCoords| aiProcess_TransformUVCoords| aiProcess_OptimizeMeshes | aiProcessPreset_TargetRealtime_Quality ); // can't load? if(!scene) return false; if(scene->HasMeshes()) { pMeshes = new NvSimpleRawMesh[scene->mNumMeshes]; NumMeshes = scene->mNumMeshes; D3DXMATRIX mIdentity; D3DXMatrixIdentity(&mIdentity); RecurseAddMeshes(scene,scene->mRootNode,&mIdentity,true); } // cleanup Assimp::DefaultLogger::kill(); return NumMeshes > 0; }
int main(int argc, char** argv) { if ( argc < 2 ) { fprintf(stderr, "Usage: %s filename format\n" " format = minimal|simple\n", argv[0]); return 42; } VertexType format = VERTEXTYPE_SIMPLE; if ( argc >= 3 ) { static const u32 nbFormats = sizeof(formats)/sizeof(formats[0]); u32 i = 0; for ( ; i < nbFormats ; i++ ) { if ( strcmp(formats[i], argv[2]) == 0 ) { format = (VertexType)i; break; } } if ( i == nbFormats ) { fprintf(stderr, "Unknown format %s\n", argv[2]); return 42; } } Assimp::Importer importer; importer.SetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_POINT | aiPrimitiveType_LINE); static u32 removeComponents[VERTEXTYPE_COUNT] = { // VERTEXTYPE_MINIMAL aiComponent_COLORS | aiComponent_TEXCOORDS | aiComponent_NORMALS | aiComponent_TANGENTS_AND_BITANGENTS | aiComponent_BONEWEIGHTS | aiComponent_ANIMATIONS | aiComponent_TEXTURES | aiComponent_LIGHTS | aiComponent_CAMERAS | aiComponent_MATERIALS, // VERTEXTYPE_SIMPLE aiComponent_COLORS | aiComponent_TEXCOORDSn(1) | aiComponent_TEXCOORDSn(2) | aiComponent_TEXCOORDSn(3) | aiComponent_BONEWEIGHTS | aiComponent_ANIMATIONS | aiComponent_TEXTURES | aiComponent_LIGHTS | aiComponent_CAMERAS | aiComponent_MATERIALS, }; importer.SetPropertyInteger(AI_CONFIG_PP_RVC_FLAGS, removeComponents[format]); static u32 preProcess[VERTEXTYPE_COUNT] = { // VERTEXTYPE_MINIMAL aiProcess_Triangulate | aiProcess_RemoveComponent | aiProcess_JoinIdenticalVertices | aiProcess_ImproveCacheLocality | aiProcess_OptimizeMeshes | aiProcess_FindInstances | aiProcess_FindInvalidData | aiProcess_RemoveRedundantMaterials | aiProcess_FindDegenerates | aiProcess_ValidateDataStructure, // VERTEXTYPE_SIMPLE aiProcess_CalcTangentSpace | aiProcess_GenNormals | aiProcess_Triangulate | aiProcess_RemoveComponent | aiProcess_JoinIdenticalVertices | aiProcess_ImproveCacheLocality | aiProcess_OptimizeMeshes | aiProcess_FindInstances | aiProcess_FindInvalidData | aiProcess_RemoveRedundantMaterials | aiProcess_FindDegenerates | aiProcess_ValidateDataStructure, // VERTEXTYPE_SPRITE aiProcess_Triangulate | aiProcess_RemoveComponent | aiProcess_JoinIdenticalVertices | aiProcess_ImproveCacheLocality | aiProcess_OptimizeMeshes | aiProcess_FindInstances | aiProcess_FindInvalidData | aiProcess_RemoveRedundantMaterials | aiProcess_FindDegenerates | aiProcess_ValidateDataStructure, }; const aiScene* scene = importer.ReadFile(argv[1], preProcess[format]); if ( scene == 0 ) { fprintf(stderr, "Couldn't load %s.\n", argv[1]); fprintf(stderr, importer.GetErrorString()); return 42; } RemoveExtension(argv[1]); for ( u32 i = 0 ; i < scene->mNumMeshes ; i++ ) { aiMesh* mesh = scene->mMeshes[i]; static char filename[4096]; if ( mesh->mName.length != 0 ) { sprintf(filename, "%s-%s.mesh", argv[1], mesh->mName.data); } else { sprintf(filename, "%s-%d.mesh", argv[1], i + 1); } printf("Exporting %d:\"%s\" to %s...\n", i + 1, mesh->mName.data, filename); ExportMesh(mesh, filename, format); } return 0; }
S3DModel* CAssParser::Load(const std::string& modelFilePath) { LOG_SL(LOG_SECTION_MODEL, L_INFO, "Loading model: %s", modelFilePath.c_str()); const std::string& modelPath = FileSystem::GetDirectory(modelFilePath); const std::string& modelName = FileSystem::GetBasename(modelFilePath); // Load the lua metafile. This contains properties unique to Spring models and must return a table std::string metaFileName = modelFilePath + ".lua"; if (!CFileHandler::FileExists(metaFileName, SPRING_VFS_ZIP)) { // Try again without the model file extension metaFileName = modelPath + '/' + modelName + ".lua"; } if (!CFileHandler::FileExists(metaFileName, SPRING_VFS_ZIP)) { LOG_SL(LOG_SECTION_MODEL, L_INFO, "No meta-file '%s'. Using defaults.", metaFileName.c_str()); } LuaParser metaFileParser(metaFileName, SPRING_VFS_MOD_BASE, SPRING_VFS_ZIP); if (!metaFileParser.Execute()) { LOG_SL(LOG_SECTION_MODEL, L_ERROR, "'%s': %s. Using defaults.", metaFileName.c_str(), metaFileParser.GetErrorLog().c_str()); } // Get the (root-level) model table const LuaTable& modelTable = metaFileParser.GetRoot(); if (!modelTable.IsValid()) { LOG_SL(LOG_SECTION_MODEL, L_INFO, "No valid model metadata in '%s' or no meta-file", metaFileName.c_str()); } // Create a model importer instance Assimp::Importer importer; // Create a logger for debugging model loading issues Assimp::DefaultLogger::create("", Assimp::Logger::VERBOSE); Assimp::DefaultLogger::get()->attachStream(new AssLogStream(), ASS_LOGGING_OPTIONS); // Give the importer an IO class that handles Spring's VFS importer.SetIOHandler(new AssVFSSystem()); // Speed-up processing by skipping things we don't need importer.SetPropertyInteger(AI_CONFIG_PP_RVC_FLAGS, ASS_IMPORTER_OPTIONS); #ifndef BITMAP_NO_OPENGL { // Optimize VBO-Mesh sizes/ranges GLint maxIndices = 1024; GLint maxVertices = 1024; // FIXME returns non-optimal data, at best compute it ourselves (pre-TL cache size!) glGetIntegerv(GL_MAX_ELEMENTS_INDICES, &maxIndices); glGetIntegerv(GL_MAX_ELEMENTS_VERTICES, &maxVertices); importer.SetPropertyInteger(AI_CONFIG_PP_SLM_VERTEX_LIMIT, maxVertices); importer.SetPropertyInteger(AI_CONFIG_PP_SLM_TRIANGLE_LIMIT, maxIndices / 3); } #endif // Read the model file to build a scene object LOG_SL(LOG_SECTION_MODEL, L_INFO, "Importing model file: %s", modelFilePath.c_str()); const aiScene* scene; { // ASSIMP spams many SIGFPEs atm in normal & tangent generation ScopedDisableFpuExceptions fe; scene = importer.ReadFile(modelFilePath, ASS_POSTPROCESS_OPTIONS); } if (scene != NULL) { LOG_SL(LOG_SECTION_MODEL, L_INFO, "Processing scene for model: %s (%d meshes / %d materials / %d textures)", modelFilePath.c_str(), scene->mNumMeshes, scene->mNumMaterials, scene->mNumTextures); } else { throw content_error("[AssimpParser] Model Import: " + std::string(importer.GetErrorString())); } S3DModel* model = new S3DModel(); model->name = modelFilePath; model->type = MODELTYPE_ASS; // Load textures FindTextures(model, scene, modelTable, modelPath, modelName); LOG_SL(LOG_SECTION_MODEL, L_INFO, "Loading textures. Tex1: '%s' Tex2: '%s'", model->tex1.c_str(), model->tex2.c_str()); texturehandlerS3O->LoadS3OTexture(model); // Load all pieces in the model LOG_SL(LOG_SECTION_MODEL, L_INFO, "Loading pieces from root node '%s'", scene->mRootNode->mName.data); LoadPiece(model, scene->mRootNode, scene, modelTable); // Update piece hierarchy based on metadata BuildPieceHierarchy(model); CalculateModelProperties(model, modelTable); // Verbose logging of model properties LOG_SL(LOG_SECTION_MODEL, L_DEBUG, "model->name: %s", model->name.c_str()); LOG_SL(LOG_SECTION_MODEL, L_DEBUG, "model->numobjects: %d", model->numPieces); LOG_SL(LOG_SECTION_MODEL, L_DEBUG, "model->radius: %f", model->radius); LOG_SL(LOG_SECTION_MODEL, L_DEBUG, "model->height: %f", model->height); LOG_SL(LOG_SECTION_MODEL, L_DEBUG, "model->drawRadius: %f", model->drawRadius); LOG_SL(LOG_SECTION_MODEL, L_DEBUG, "model->mins: (%f,%f,%f)", model->mins[0], model->mins[1], model->mins[2]); LOG_SL(LOG_SECTION_MODEL, L_DEBUG, "model->maxs: (%f,%f,%f)", model->maxs[0], model->maxs[1], model->maxs[2]); LOG_SL(LOG_SECTION_MODEL, L_INFO, "Model %s Imported.", model->name.c_str()); return model; }
//! Loads and returns a skinned model from a file. SkinnedModel* ModelImporter::LoadSkinnedModel(string filename) { // Is the model already loaded? if(mSkinnedModelMap.find(filename) != mSkinnedModelMap.end()) return mSkinnedModelMap[filename]; Assimp::Importer importer; mFilename = filename; SkinnedModel* model = NULL; // Important! Makes sure that if the angle between two face normals is > 80 they are not smoothed together. // Since the angle between a cubes face normals is 90 the lighting looks very bad if we don't specify this. importer.SetPropertyFloat(AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE, 80.0f); importer.SetPropertyInteger(AI_CONFIG_IMPORT_TER_MAKE_UVS, 1); importer.SetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_LINE); // Load scene from the file. const aiScene* scene = importer.ReadFile(filename, aiProcess_CalcTangentSpace | aiProcess_Triangulate | aiProcess_GenSmoothNormals | aiProcess_SplitLargeMeshes | aiProcess_ConvertToLeftHanded | aiProcess_SortByPType); if(scene) { // Create the model that is getting filled out. model = new SkinnedModel(); // Create the animator. SceneAnimator* animator = new SceneAnimator(); animator->Init(scene); model->SetAnimator(animator); // Loop through all meshes. for(int j = 0; j < scene->mNumMeshes; j++) { aiMesh* assimpMesh = scene->mMeshes[j]; // Calculate vertex weight and bone indices. vector<Weights> weights = CalculateWeights(assimpMesh, animator); vector<SkinnedVertex> vertices; vector<UINT> indices; // Add vertices to the vertex list. for(int i = 0; i < assimpMesh->mNumVertices; i++) { aiVector3D v = assimpMesh->mVertices[i]; aiVector3D n = assimpMesh->mNormals[i]; aiVector3D t = aiVector3D(0, 0, 0); if(assimpMesh->HasTextureCoords(0)) t = assimpMesh->mTextureCoords[0][i]; n = n.Normalize(); // Pos, normal and texture coordinates. SkinnedVertex vertex(v.x, v.y, v.z, n.x, n.y, n.z, 0, 0, 1, t.x, t.y); // Bone indices and weights. for(int k = 0; k < weights[i].boneIndices.size(); k++) vertex.BoneIndices[k] = weights[i].boneIndices[k]; vertex.Weights.x = weights[i].weights.size() >= 1 ? weights[i].weights[0] : 0; vertex.Weights.y = weights[i].weights.size() >= 2 ? weights[i].weights[1] : 0; vertex.Weights.z = weights[i].weights.size() >= 3 ? weights[i].weights[2] : 0; vertices.push_back(vertex); } // Add indices to the index list. for(int i = 0; i < assimpMesh->mNumFaces; i++) for(int k = 0; k < assimpMesh->mFaces[i].mNumIndices; k++) indices.push_back(assimpMesh->mFaces[i].mIndices[k]); // Get the path to the texture in the directory. aiString path; aiMaterial* material = scene->mMaterials[assimpMesh->mMaterialIndex]; material->Get(AI_MATKEY_TEXTURE_DIFFUSE(0), path); FindValidPath(&path); // Extract all the ambient, diffuse and specular colors. aiColor4D ambient, diffuse, specular; material->Get(AI_MATKEY_COLOR_AMBIENT, ambient); material->Get(AI_MATKEY_COLOR_DIFFUSE, diffuse); material->Get(AI_MATKEY_COLOR_SPECULAR, specular); // Create the mesh and its primitive. SkinnedMesh* mesh = new SkinnedMesh(); Primitive* primitive = new Primitive(GlobalApp::GetD3DDevice(), vertices, indices); mesh->SetPrimitive(primitive); mesh->SetVertices(vertices); mesh->SetIndices(indices); mPrimtiveFactory->AddPrimitive(path.C_Str(), primitive); // Replace .tga with .bmp [HACK]. string texturePath = path.C_Str(); int tgaPos = texturePath.find_first_of(".tga"); if(tgaPos != string::npos) { texturePath.replace(texturePath.size()-4, 4, ".bmp"); path = texturePath; } // Any texture? if(_stricmp(path.C_Str(), "") != 0) mesh->LoadTexture(path.C_Str()); // Any normal map? aiString nmap; material->Get(AI_MATKEY_TEXTURE_HEIGHT(0), nmap); FindValidPath(&nmap); if(_stricmp(nmap.C_Str(), "") != 0) mesh->SetNormalMap(GlobalApp::GetGraphics()->LoadTexture(nmap.C_Str())); // [NOTE] The material is set to white. mesh->SetMaterial(Material(Colors::White)); //mesh->SetMaterial(Material(diffuse, diffuse, diffuse)); model->SetFilename(filename); // Add the mesh to the model. model->AddMesh(mesh); } // Pre-calculate the bounding box. model->CalculateAABB(); // Add the newly created mesh to the map and return it. mSkinnedModelMap[filename] = model; return mSkinnedModelMap[filename]; } else { char buffer[246]; sprintf(buffer, "Error loading model: %s", filename.c_str()); MessageBox(0, buffer, "Error!", 0); mSkinnedModelMap[filename] = LoadSkinnedModel("models/box.obj"); return mSkinnedModelMap[filename]; } }
//! Loads and returns a static model from a file. StaticModel* ModelImporter::LoadStaticModel(string filename) { // Is the model already loaded? if(mStaticModelMap.find(filename) != mStaticModelMap.end()) return mStaticModelMap[filename]; Assimp::Importer importer; mFilename = filename; StaticModel* model = NULL; // Important! Makes sure that if the angle between two face normals is > 80 they are not smoothed together. // Since the angle between a cubes face normals is 90 the lighting looks very bad if we don't specify this. importer.SetPropertyFloat(AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE, 80.0f); importer.SetPropertyInteger(AI_CONFIG_IMPORT_TER_MAKE_UVS, 1); importer.SetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_LINE); // Load scene from the file. const aiScene* scene = importer.ReadFile(filename, aiProcess_CalcTangentSpace | aiProcess_Triangulate | aiProcess_GenSmoothNormals | aiProcess_SplitLargeMeshes | aiProcess_ConvertToLeftHanded | aiProcess_SortByPType); // Successfully loaded the scene. if(scene) { // Create the model that is getting filled out. model = new StaticModel(); // Loop through all meshes. for(int i = 0; i < scene->mNumMeshes; i++) { aiMesh* assimpMesh = scene->mMeshes[i]; vector<Vertex> vertices; vector<UINT> indices; // Add vertices to the vertex list. for(int i = 0; i < assimpMesh->mNumVertices; i++) { aiVector3D v = assimpMesh->mVertices[i]; aiVector3D n = assimpMesh->mNormals[i]; aiVector3D t = aiVector3D(0, 0, 0); if(assimpMesh->HasTextureCoords(0)) t = assimpMesh->mTextureCoords[0][i]; n = n.Normalize(); Vertex vertex(v.x, v.y, v.z, n.x, n.y, n.z, 0, 0, 0, t.x, t.y); vertices.push_back(vertex); } // Add indices to the index list. for(int i = 0; i < assimpMesh->mNumFaces; i++) for(int j = 0; j < assimpMesh->mFaces[i].mNumIndices; j++) indices.push_back(assimpMesh->mFaces[i].mIndices[j]); // Get the path to the texture in the directory. aiString path; aiMaterial* material = scene->mMaterials[assimpMesh->mMaterialIndex]; material->Get(AI_MATKEY_TEXTURE_DIFFUSE(0), path); FindValidPath(&path); // Extract all the ambient, diffuse and specular colors. aiColor4D ambient, diffuse, specular; material->Get(AI_MATKEY_COLOR_AMBIENT, ambient); material->Get(AI_MATKEY_COLOR_DIFFUSE, diffuse); material->Get(AI_MATKEY_COLOR_SPECULAR, specular); // Create the mesh and its primitive. StaticMesh* mesh = new StaticMesh(); Primitive* primitive = new Primitive(GlobalApp::GetD3DDevice(), vertices, indices); mesh->SetPrimitive(primitive); mesh->SetVertices(vertices); mesh->SetIndices(indices); mPrimtiveFactory->AddPrimitive(path.C_Str(), primitive); // Any texture? if(_stricmp(path.C_Str(), "") != 0) mesh->LoadTexture(path.C_Str()); // Any normal map? aiString nmap; material->Get(AI_MATKEY_TEXTURE_HEIGHT(0), nmap); FindValidPath(&nmap); if(_stricmp(nmap.C_Str(), "") != 0) mesh->SetNormalMap(GlobalApp::GetGraphics()->LoadTexture(nmap.C_Str())); // [NOTE] The material is set to white. mesh->SetMaterial(Material(Colors::White)); // Was before [NOTE] model->SetFilename(filename); // Add the mesh to the model. model->AddMesh(mesh); } // Add to the model map and return it. mStaticModelMap[filename] = model; return mStaticModelMap[filename]; } else { char buffer[246]; sprintf(buffer, "Error loading model: %s", filename.c_str()); MessageBox(0, buffer, "Error!", 0); mStaticModelMap[filename] = LoadStaticModel("models/box.obj"); return mStaticModelMap[filename]; } }
void ManageAnimation::Init(const char *filename, float xRotateCorrection, bool normalize) { this->fRotateXCorrection = xRotateCorrection; // Create an instance of the Importer class Assimp::Importer importer; unsigned int flags = aiProcess_JoinIdenticalVertices|aiProcess_Triangulate|aiProcess_FixInfacingNormals|aiProcess_ValidateDataStructure| aiProcess_GenNormals|aiProcess_LimitBoneWeights; // |aiProcess_OptimizeMeshes if (normalize) flags |= aiProcess_PreTransformVertices; // This flag will remove all bones. importer.SetPropertyBool(AI_CONFIG_PP_PTV_NORMALIZE, true); // TODO: This is only used for aiProcess_PreTransformVertices importer.SetPropertyInteger(AI_CONFIG_PP_LBW_MAX_WEIGHTS, 3); // The shader can only handle three weights for each vertice. const aiScene* scene = importer.ReadFile( filename, flags); if (scene == 0) { ErrorDialog("assimp loading %s: %s\n", filename, importer.GetErrorString()); return; } // Number of vertices and number of indices int vertexSize = 0, indexSize = 0; if (gVerbose) { printf("\nScene: %s (%s)*******************\n", scene->mRootNode->mName.data, filename); printf("%d materials, %d meshes, %d animations, %d textures\n", scene->mNumMaterials, scene->mNumMeshes, scene->mNumAnimations, scene->mNumTextures); } //********************************************************************** // Count how much data is needed. All indices and vetices are saved in // the same buffer. //********************************************************************** fMeshData.reset(new Mesh[scene->mNumMeshes]); fNumMeshes = scene->mNumMeshes; for (unsigned int i=0; i<scene->mNumMeshes; i++) { aiMesh *m = scene->mMeshes[i]; if (m->mNumBones > 0) this->fUsingBones = true; // True if any mesh uses bones aiMaterial *mat = scene->mMaterials[m->mMaterialIndex]; aiColor3D c (0.f,0.f,0.f); mat->Get(AI_MATKEY_COLOR_DIFFUSE, c); fMeshData[i].colour = glm::vec4(c.r, c.g, c.b, 1.0f); indexSize += m->mNumFaces * 3; // Always 3 indices for each face (triangle). vertexSize += m->mNumVertices; if (gVerbose) printf("\tMesh %d ('%s'): %d faces, %d vertices\n", i, m->mName.data, m->mNumFaces, m->mNumVertices); // Find all animation bones in all meshes. They may have been seen in another mesh already. fMeshData[i].bones.resize(m->mNumBones); for (unsigned int j=0; j < m->mNumBones; j++) { aiBone *aib = m->mBones[j]; // Add the bone to the global list of bones if it isn't already there. boneindexIT_t it = fBoneIndex.find(aib->mName.data); unsigned int jointIndex = fBoneIndex.size(); if (it == fBoneIndex.end()) fBoneIndex[aib->mName.data] = jointIndex; else jointIndex = it->second; fMeshData[i].bones[j].jointIndex = jointIndex; CopyaiMat(&aib->mOffsetMatrix, fMeshData[i].bones[j].offset); } } if (gVerbose) printf("Total vertices needed: %d, index count %d\n", vertexSize, indexSize); // Find all mesh transformations and update the bones matrix dependency on parents glm::mat4 meshmatrix[scene->mNumMeshes]; FindMeshTransformations(0, meshmatrix, glm::mat4(1), scene->mRootNode); if (gVerbose) DumpNodeTree(0, scene->mRootNode); // Copy all vertex data into one big buffer and all index data into another buffer VertexDataf vertexData[vertexSize]; unsigned short indexData[indexSize]; int vertexOffset = 0, indexOffset = 0; int previousOffset = 0; // The index offset for the current mesh // Skinning data. Allocate even if not using bones. char numWeights[vertexSize]; memset(numWeights, 0, sizeof numWeights); glm::vec3 weights[vertexSize]; float joints[vertexSize*3]; // Up to 4 joints per vertex, but only three are used. memset(joints, 0, sizeof joints); //********************************************************************** // Traverse the meshes again, generating the vertex data //********************************************************************** for (unsigned int i=0; i<scene->mNumMeshes; i++) { aiMesh *m = scene->mMeshes[i]; if (gVerbose) printf("Mesh %d: %d faces, %d vertices, mtl index %d\n", i, m->mNumFaces, m->mNumVertices, m->mMaterialIndex); #if 1 if (gVerbose) { #if 0 printf("Indices:\n"); for (unsigned int face=0; face < m->mNumFaces; face++) { printf("%d:(", m->mFaces[face].mNumIndices); for (unsigned int ind=0; ind < m->mFaces[face].mNumIndices; ind++) printf("%d,", m->mFaces[face].mIndices[ind]); printf("),"); } printf("\nVertices:\n"); for (unsigned int vert=0; vert < m->mNumVertices; vert++) printf("%6.3f %6.3f %6.3f\n", m->mVertices[vert].x, m->mVertices[vert].y, m->mVertices[vert].z); #endif printf(" Transformation matrix:\n"); PrintMatrix(4, meshmatrix[i]); printf("\n"); } #endif // Copy faces, but only those that are proper triangles. unsigned int numTriangles = 0; // The number of found triangles. for (unsigned int face=0; face < m->mNumFaces; face++) { if (m->mFaces[face].mNumIndices != 3) continue; // Only allow triangles // m->mFaces[face].mIndices is a local index into the current mesh. Value 0 will thus // adress the first vertex in the current mesh. As all vertices are stored in the same buffer, // an offset need to be added to get the correct index of the vertex. indexData[indexOffset++] = m->mFaces[face].mIndices[0] + previousOffset; indexData[indexOffset++] = m->mFaces[face].mIndices[1] + previousOffset; indexData[indexOffset++] = m->mFaces[face].mIndices[2] + previousOffset; numTriangles++; } fMeshData[i].numFaces = numTriangles; // Copy all vertices for (unsigned int v = 0; v < m->mNumVertices; v++) { glm::vec4 v1(m->mVertices[v].x, m->mVertices[v].y, m->mVertices[v].z, 1); glm::vec4 v2 = meshmatrix[i] * v1; vertexData[vertexOffset].SetVertex(glm::vec3(v2)); glm::vec4 n1(m->mNormals[v].x, m->mNormals[v].y, m->mNormals[v].z, 1); glm::vec4 n2 = meshmatrix[i] * glm::normalize(n1); vertexData[vertexOffset].SetNormal(glm::vec3(n2)); if (m->mTextureCoords[0] != 0) { vertexData[vertexOffset].SetTexture(m->mTextureCoords[0][v].x, m->mTextureCoords[0][v].y); } else { vertexData[vertexOffset].SetTexture(0,0); } vertexData[vertexOffset].SetIntensity(255); vertexData[vertexOffset].SetAmbient(100); vertexOffset++; } // Every animation bone in Assimp data structures have a list of weights and vertex index. // We want the weights, 0-3 of them, sorted on vertices instead. // Iterate through all bones used in this mesh, and copy weights to respective vertex data. for (unsigned j=0; j < m->mNumBones; j++) { fMeshData[i].bones[j].offset *= glm::inverse(meshmatrix[i]); aiBone *aib = m->mBones[j]; boneindexIT_t it = fBoneIndex.find(aib->mName.data); if (it == fBoneIndex.end()) ErrorDialog("Mesh %d bone %s not found", i, aib->mName.data); if (gVerbose) { printf(" Offset for mesh %d %s (%d) joint %d:\n", i, aib->mName.data, j, it->second); PrintMatrix(4, fMeshData[i].bones[j].offset); } for (unsigned k=0; k < aib->mNumWeights; k++) { int v = aib->mWeights[k].mVertexId + previousOffset; // Add the local vertex number in the current mesh to the offset to get the global number int w = numWeights[v]++; // Because AI_CONFIG_PP_LBW_MAX_WEIGHTS is maximized to 3, There can't be more than 3 weights for a vertex // unless there is a bug in Assimp. if (w >= 3) ErrorDialog("Too many bone weights on vertice %d, bone %s, model %s", v, aib->mName.data, filename); weights[v][w] = aib->mWeights[k].mWeight; joints[v*3 + w] = it->second; } } previousOffset = vertexOffset; } // Now that the weights are sorted on vertices, it is possible to normalize them. The sum shall be 1. for (int j=0; j < vertexSize; j++) NormalizeWeights(weights[j], numWeights[j]); ASSERT(vertexOffset == vertexSize); ASSERT(indexOffset <= indexSize); // Allocated the vertex data in OpenGL. The buffer object is used with the following layout: // 1. The usual vertex data, and array of type VertexDataf // 2. Skin weights, array of glm::vec3. 3 floats for each vertex. // 3. Bones index, 4 bytes for each vertex (only 3 used) const int AREA1 = vertexSize*sizeof vertexData[0]; const int AREA2 = vertexSize*sizeof weights[0]; const int AREA3 = vertexSize*3*sizeof (float); int bufferSize = AREA1; if (fUsingBones) { // Also need space for weights and bones indices. bufferSize += AREA2 + AREA3; } // Allocate the buffer, random content so far if (!fOpenglBuffer.BindArray(bufferSize, 0)) { ErrorDialog("ManageAnimation::Init: Data size is mismatch with input array\n"); } fOpenglBuffer.ArraySubData(0, AREA1, vertexData); if (this->fUsingBones) { glBufferSubData(GL_ARRAY_BUFFER, AREA1, AREA2, weights); glBufferSubData(GL_ARRAY_BUFFER, AREA1+AREA2, AREA3, joints); } glGenVertexArrays(1, &fVao); glBindVertexArray(fVao); StageOneShader::EnableVertexAttribArray(this->fUsingBones); // Will be remembered in the VAO state // Allocate the index data in OpenGL if (!fIndexBuffer.BindElementsArray(indexSize*sizeof indexData[0], indexData)) { ErrorDialog("ManageAnimation::Init: Data size is mismatch with input array\n"); } StageOneShader::VertexAttribPointer(); if (this->fUsingBones) StageOneShader::VertexAttribPointerSkinWeights(AREA1, AREA1+AREA2); checkError("ManageAnimation::Init"); glBindVertexArray(0); if (gVerbose) { printf("Mesh bones for '%s':\n", filename); for (auto &bone : fBoneIndex) { printf(" %s: joint %d\n", bone.first.c_str(), bone.second); } } // Decode animation information unsigned int numMeshBones = fBoneIndex.size(); if (numMeshBones > 0 && scene->mNumAnimations == 0) ErrorDialog("ManageAnimation::Init %s: Bones but no animations", filename); if (scene->mNumAnimations > 0) { if (numMeshBones == 0) ErrorDialog("ManageAnimation::Init %s: No mesh bones", filename); if (gVerbose) printf("Parsing %d animations\n", scene->mNumAnimations); } //********************************************************************** // Decode the animations // There may be more animated bones than used by meshes. //********************************************************************** fAnimations.reset(new Animation[scene->mNumAnimations]); for (unsigned int i=0; i < scene->mNumAnimations; i++) { aiAnimation *aia = scene->mAnimations[i]; fAnimations[i].name = aia->mName.data; fAnimations[i].keysPerSecond = aia->mTicksPerSecond; fAnimations[i].duration = aia->mDuration; // Set the size of bones to the actual nutmber of bones used by the meshes, not the total number of bones // in the model. fAnimations[i].bones.reset(new AnimationBone[numMeshBones]); // The number of keys has to be the same for all channels. This is a limitation if it is going to be possible to // pre compute all matrices. unsigned numChannels = aia->mNumChannels; if (numChannels == 0) ErrorDialog("ManageAnimation::Init no animation channels for %s, %s", aia->mName.data, filename); if (numChannels != numMeshBones) ErrorDialog("ManageAnimation::Init %s animation %d: Can only handle when all bones are used (%d out of %d)", filename, i, numChannels, numMeshBones); unsigned int numKeys = aia->mChannels[0]->mNumPositionKeys; struct channel { glm::mat4 mat; // Relative transformation matrix to parent channel *parent; aiNodeAnim *node; unsigned joint; #define UNUSEDCHANNEL 0xFFFF }; channel channels[numChannels]; // This is the list of all bones in this animation // Check that there are the same number of keys for all channels, and allocate transformation matrices bool foundOneBone = false; for (unsigned int j=0; j < numChannels; j++) { aiNodeAnim *ain = aia->mChannels[j]; if (ain->mNumPositionKeys != numKeys || ain->mNumRotationKeys != numKeys || ain->mNumScalingKeys != numKeys) ErrorDialog("ManageAnimation::Init %s Bad animation setup: Pos keys %d, rot keys %d, scaling keys %d", filename, ain->mNumPositionKeys, ain->mNumRotationKeys, ain->mNumScalingKeys); channels[j].node = ain; channels[j].parent = 0; boneindexIT_t it = fBoneIndex.find(ain->mNodeName.data); if (it == fBoneIndex.end()) { // Skip this channel (bone animation) channels[j].joint = UNUSEDCHANNEL; // Mark it as not used continue; // ErrorDialog("ManageAnimation::Init: Unknown bone %s in animation %s for %s", ain->mNodeName.data, fAnimations[i].name.c_str(), filename); } foundOneBone = true; unsigned int joint = it->second; fAnimations[i].bones[joint].frameMatrix.reset(new glm::mat4[numKeys]); channels[j].joint = joint; } if (!foundOneBone) ErrorDialog("ManageAnimation::Init %s animation %d no bones used", filename, i); // Find the parent for each animation node for (unsigned j=0; j<numChannels; j++) { aiNode *n = FindNode(scene->mRootNode, channels[j].node->mNodeName.data); if (n == 0 || n->mParent == 0) continue; // No parent n = n->mParent; for (unsigned p=0; p<numChannels; p++) { if (p == j || strcmp(n->mName.data, channels[p].node->mNodeName.data) != 0) continue; // Found it! channels[j].parent = &channels[p]; break; } } fAnimations[i].times.reset(new double[numKeys]); fAnimations[i].numKeys = numKeys; // The first key frame doesn't always start at time 0 double firstKeyTime = channels[0].node->mPositionKeys[0].mTime; for (unsigned k=0; k < numKeys; k++) { fAnimations[i].times[k] = channels[0].node->mPositionKeys[k].mTime - firstKeyTime; // printf("Animation key %d at time %.2f\n", k, fAnimations[i].times[k]); for (unsigned int j=0; j < numChannels; j++) { aiNodeAnim *ain = channels[j].node; glm::mat4 R; CopyaiQuat(R, ain->mRotationKeys[k].mValue); glm::mat4 T = glm::translate(glm::mat4(1), glm::vec3(ain->mPositionKeys[k].mValue.x, ain->mPositionKeys[k].mValue.y, ain->mPositionKeys[k].mValue.z)); glm::mat4 S = glm::scale(glm::mat4(1), glm::vec3(ain->mScalingKeys[k].mValue.x, ain->mScalingKeys[k].mValue.y, ain->mScalingKeys[k].mValue.z)); channels[j].mat = T * R * S; if (gVerbose) { printf(" Key %d animation bone %s relative\n", k, channels[j].node->mNodeName.data); PrintMatrix(10, channels[j].mat); } } // Iterate through each animation bone and compute the transformation matrix using parent matrix. for (unsigned int j=0; j < numChannels; j++) { unsigned joint = channels[j].joint; glm::mat4 mat(1); for (channel *node = &channels[j]; node; node = node->parent) { mat = node->mat * mat; } mat = armature * mat; if (joint != UNUSEDCHANNEL) { fAnimations[i].bones[joint].frameMatrix[k] = mat; if (gVerbose) { printf(" Key %d animation bone %s channel %d absolute\n", k, channels[j].node->mNodeName.data, j); PrintMatrix(10, mat); } } } } } }
bool Model::loadFromFile(const std::string& filename) { Assimp::Importer importer; std::string strModelsResourcesFolder = "Data/Resources/Models/"; std::string strModelFinalFilename = strModelsResourcesFolder + filename; // Component to be removed when importing file importer.SetPropertyInteger(AI_CONFIG_PP_RVC_FLAGS, aiComponent_COLORS // | aiComponent_TANGENTS_AND_BITANGENTS | aiComponent_BONEWEIGHTS | aiComponent_ANIMATIONS | aiComponent_LIGHTS | aiComponent_CAMERAS); importer.SetPropertyInteger(AI_CONFIG_PP_PTV_NORMALIZE, 1); // And have it read the given file with some example postprocessing // Usually - if speed is not the most important aspect for you - you'll // propably to request more postprocessing than we do in this example. const aiScene* scene = importer.ReadFile(strModelFinalFilename, aiProcess_Triangulate | aiProcess_JoinIdenticalVertices | aiProcess_RemoveComponent | aiProcess_GenNormals | aiProcess_CalcTangentSpace | aiProcess_PreTransformVertices //| aiProcess_GenUVCoords //| aiProcess_MakeLeftHanded ); // const aiScene* scene = importer.ReadFile( filename, // aiProcessPreset_TargetRealtime_MaxQuality // ); // If the import failed, report it if( !scene) { std::cerr << importer.GetErrorString() << '\n'; return false; } m_nodes.clear(); aiNode* rootNode = scene->mRootNode; handleNode(rootNode, aiMatrix4x4()); m_meshes.clear(); m_meshes.resize(scene->mNumMeshes); m_materials.clear(); //m_materials.resize(scene->mNumMaterials); //std::string dir = filename; //size_t lastSeparator = dir.find_last_of('/'); //if (lastSeparator != std::string::npos) { // dir.erase(lastSeparator, dir.size()); //} //else { // dir = std::string("."); //} for (unsigned int i = 0; i < scene->mNumMeshes; ++i) { m_meshes[i].loadFromAssimpMesh(scene->mMeshes[i]); } //for (unsigned int i = 0; i < scene->mNumMaterials; ++i) { // m_materials[i].loadFromAssimpMaterial(scene->mMaterials[i], dir); //} // const aiMesh* mesh = scene->mMeshes[0]; // std::cerr << "Mesh HasFaces ? " << mesh->HasFaces() << '\n'; // std::cerr << "Mesh HasNormals ? " << mesh->HasNormals() << '\n'; // std::cerr << "Mesh HasPositions ? " << mesh->HasPositions() << '\n'; // std::cerr << "Mesh HasTangentsAndBitangents ? " << mesh->HasTangentsAndBitangents() << '\n'; // std::cerr << "Mesh HasBones ? " << mesh->HasBones() << '\n'; // std::cerr << "Mesh HasTextureCoords ? " << mesh->HasTextureCoords(0) << '\n'; // std::cerr << "mesh->mTextureCoords[0] != NULL ? " << (mesh->mTextureCoords[0] != NULL) << '\n'; // std::cerr << "Mesh GetNumUVChannels ? " << mesh->GetNumUVChannels() << '\n'; // std::cerr << "Mesh GetNumColorChannels ? " << mesh->GetNumColorChannels() << '\n'; // std::cerr << "Mesh mNumFaces ? " << mesh->mNumFaces << '\n'; // std::cerr << "Mesh mNumVertices ? " << mesh->mNumVertices << '\n'; // std::cerr << "Mesh mPrimitiveTypes ? " << mesh->mPrimitiveTypes << " and should be " << aiPrimitiveType_TRIANGLE << '\n'; return true; }
S3DModel* CAssParser::Load(const std::string& modelFilePath) { LOG_S(LOG_SECTION_MODEL, "Loading model: %s", modelFilePath.c_str() ); const std::string modelPath = FileSystem::GetDirectory(modelFilePath); const std::string modelName = FileSystem::GetBasename(modelFilePath); //! LOAD METADATA //! Load the lua metafile. This contains properties unique to Spring models and must return a table std::string metaFileName = modelFilePath + ".lua"; if (!CFileHandler::FileExists(metaFileName, SPRING_VFS_ZIP)) { //! Try again without the model file extension metaFileName = modelPath + '/' + modelName + ".lua"; } LuaParser metaFileParser(metaFileName, SPRING_VFS_MOD_BASE, SPRING_VFS_ZIP); if (!CFileHandler::FileExists(metaFileName, SPRING_VFS_ZIP)) { LOG_S(LOG_SECTION_MODEL, "No meta-file '%s'. Using defaults.", metaFileName.c_str()); } else if (!metaFileParser.Execute()) { LOG_SL(LOG_SECTION_MODEL, L_ERROR, "'%s': %s. Using defaults.", metaFileName.c_str(), metaFileParser.GetErrorLog().c_str()); } //! Get the (root-level) model table const LuaTable& metaTable = metaFileParser.GetRoot(); if (metaTable.IsValid()) { LOG_S(LOG_SECTION_MODEL, "Found valid model metadata in '%s'", metaFileName.c_str()); } //! LOAD MODEL DATA //! Create a model importer instance Assimp::Importer importer; //! Create a logger for debugging model loading issues Assimp::DefaultLogger::create("",Assimp::Logger::VERBOSE); const unsigned int severity = Assimp::Logger::Debugging|Assimp::Logger::Info|Assimp::Logger::Err|Assimp::Logger::Warn; Assimp::DefaultLogger::get()->attachStream( new AssLogStream(), severity ); //! Give the importer an IO class that handles Spring's VFS importer.SetIOHandler( new AssVFSSystem() ); //! Speed-up processing by skipping things we don't need importer.SetPropertyInteger(AI_CONFIG_PP_RVC_FLAGS, aiComponent_CAMERAS|aiComponent_LIGHTS|aiComponent_TEXTURES|aiComponent_ANIMATIONS); #ifndef BITMAP_NO_OPENGL //! Optimize VBO-Mesh sizes/ranges GLint maxIndices = 1024; GLint maxVertices = 1024; glGetIntegerv(GL_MAX_ELEMENTS_INDICES, &maxIndices); glGetIntegerv(GL_MAX_ELEMENTS_VERTICES, &maxVertices); //FIXME returns not optimal data, at best compute it ourself! (pre-TL cache size!) importer.SetPropertyInteger(AI_CONFIG_PP_SLM_VERTEX_LIMIT, maxVertices); importer.SetPropertyInteger(AI_CONFIG_PP_SLM_TRIANGLE_LIMIT, maxIndices/3); #endif //! Read the model file to build a scene object LOG_S(LOG_SECTION_MODEL, "Importing model file: %s", modelFilePath.c_str() ); const aiScene* scene = importer.ReadFile( modelFilePath, ASS_POSTPROCESS_OPTIONS ); if (scene != NULL) { LOG_S(LOG_SECTION_MODEL, "Processing scene for model: %s (%d meshes / %d materials / %d textures)", modelFilePath.c_str(), scene->mNumMeshes, scene->mNumMaterials, scene->mNumTextures ); } else { LOG_SL(LOG_SECTION_MODEL, L_ERROR, "Model Import: %s", importer.GetErrorString()); } SAssModel* model = new SAssModel; model->name = modelFilePath; model->type = MODELTYPE_ASS; model->scene = scene; //model->meta = &metaTable; //! Gather per mesh info CalculatePerMeshMinMax(model); //! Assign textures //! The S3O texture handler uses two textures. //! The first contains diffuse color (RGB) and teamcolor (A) //! The second contains glow (R), reflectivity (G) and 1-bit Alpha (A). if (metaTable.KeyExists("tex1")) { model->tex1 = metaTable.GetString("tex1", "default.png"); } else { //! Search for a texture std::vector<std::string> files = CFileHandler::FindFiles("unittextures/", modelName + ".*"); for(std::vector<std::string>::iterator fi = files.begin(); fi != files.end(); ++fi) { model->tex1 = FileSystem::GetFilename(*fi); break; //! there can be only one! } } if (metaTable.KeyExists("tex2")) { model->tex2 = metaTable.GetString("tex2", ""); } else { //! Search for a texture std::vector<std::string> files = CFileHandler::FindFiles("unittextures/", modelName + "2.*"); for(std::vector<std::string>::iterator fi = files.begin(); fi != files.end(); ++fi) { model->tex2 = FileSystem::GetFilename(*fi); break; //! there can be only one! } } model->flipTexY = metaTable.GetBool("fliptextures", true); //! Flip texture upside down model->invertTexAlpha = metaTable.GetBool("invertteamcolor", true); //! Reverse teamcolor levels //! Load textures LOG_S(LOG_SECTION_MODEL, "Loading textures. Tex1: '%s' Tex2: '%s'", model->tex1.c_str(), model->tex2.c_str()); texturehandlerS3O->LoadS3OTexture(model); //! Load all pieces in the model LOG_S(LOG_SECTION_MODEL, "Loading pieces from root node '%s'", scene->mRootNode->mName.data); LoadPiece(model, scene->mRootNode, metaTable); //! Update piece hierarchy based on metadata BuildPieceHierarchy( model ); //! Simplified dimensions used for rough calculations model->radius = metaTable.GetFloat("radius", model->radius); model->height = metaTable.GetFloat("height", model->height); model->relMidPos = metaTable.GetFloat3("midpos", model->relMidPos); model->mins = metaTable.GetFloat3("mins", model->mins); model->maxs = metaTable.GetFloat3("maxs", model->maxs); //! Calculate model dimensions if not set if (!metaTable.KeyExists("mins") || !metaTable.KeyExists("maxs")) CalculateMinMax( model->rootPiece ); if (model->radius < 0.0001f) CalculateRadius( model ); if (model->height < 0.0001f) CalculateHeight( model ); //! Verbose logging of model properties LOG_SL(LOG_SECTION_MODEL, L_DEBUG, "model->name: %s", model->name.c_str()); LOG_SL(LOG_SECTION_MODEL, L_DEBUG, "model->numobjects: %d", model->numPieces); LOG_SL(LOG_SECTION_MODEL, L_DEBUG, "model->radius: %f", model->radius); LOG_SL(LOG_SECTION_MODEL, L_DEBUG, "model->height: %f", model->height); LOG_SL(LOG_SECTION_MODEL, L_DEBUG, "model->mins: (%f,%f,%f)", model->mins[0], model->mins[1], model->mins[2]); LOG_SL(LOG_SECTION_MODEL, L_DEBUG, "model->maxs: (%f,%f,%f)", model->maxs[0], model->maxs[1], model->maxs[2]); LOG_S(LOG_SECTION_MODEL, "Model %s Imported.", model->name.c_str()); return model; }