TEST_F(utObjImportExport, homogeneous_coordinates_divide_by_zero_Test) { static const std::string ObjModel = "v -0.500000 0.000000 0.400000 0.\n" "v -0.500000 0.000000 -0.800000 1.00000\n" "v 0.500000 1.000000 -0.800000 0.5000\n" "f 1 2 3\nB"; Assimp::Importer myimporter; const aiScene *scene = myimporter.ReadFileFromMemory(ObjModel.c_str(), ObjModel.size(), aiProcess_ValidateDataStructure); EXPECT_EQ(nullptr, scene); }
bool KeOpenSceneFromMemory( void* ptr, uint32_t size ) { /* Import this mesh from disk */ scene = importer.ReadFileFromMemory( ptr, size, aiProcessPreset_TargetRealtime_MaxQuality ); if( !scene ) { DISPDBG( KE_ERROR, "Error reading mesh file!\nReason: " << importer.GetErrorString() ); return false; } return true; }
CParaXModel* FBXParser::ParseParaXModel(const char* buffer, int nSize) { CParaXModel* pMesh = NULL; Assimp::Importer importer; Reset(); SetAnimSplitterFilename(); // aiProcess_MakeLeftHanded | const aiScene* pFbxScene = importer.ReadFileFromMemory(buffer, nSize, aiProcess_Triangulate | aiProcess_GenSmoothNormals | aiProcess_FlipUVs, "fbx"); if (pFbxScene) { ParaXHeaderDef m_xheader; m_xheader.IsAnimated = pFbxScene->HasAnimations() ? 1 : 0; pMesh = new CParaXModel(m_xheader); if (pFbxScene->HasMaterials()) { int materials_num = pFbxScene->mNumMaterials; for (int i = 0; i < materials_num; i++) { ProcessFBXMaterial(pFbxScene, i, pMesh); } } // get root node //m_nRootNodeIndex = CreateGetBoneIndex(pFbxScene->mRootNode->mName.C_Str()); // must be called before ProcessFBXBoneNodes if (pFbxScene->HasAnimations()) { int animations_num = pFbxScene->mNumAnimations; for (int i = 0; i < animations_num; i++) { ProcessFBXAnimation(pFbxScene, i, pMesh); } } // building the parent-child relationship of the bones, and add meshes if any; ProcessFBXBoneNodes(pFbxScene, pFbxScene->mRootNode, -1, pMesh); // MakeAxisY_UP(); FillParaXModelData(pMesh, pFbxScene); PostProcessParaXModelData(pMesh); #ifdef _DEBUG //PrintDebug(pFbxScene); #endif } else { OUTPUT_LOG("Error parsing '%s': '%s'\n", m_sFilename.c_str(), importer.GetErrorString()); } return pMesh; }
TEST_F( utObjImportExport, issue1453_segfault ) { static const std::string ObjModel = "v 0.0 0.0 0.0\n" "v 0.0 0.0 1.0\n" "v 0.0 1.0 0.0\n" "v 0.0 1.0 1.0\n" "v 1.0 0.0 0.0\n" "v 1.0 0.0 1.0\n" "v 1.0 1.0 0.0\n" "v 1.0 1.0 1.0\nB"; Assimp::Importer myimporter; const aiScene *scene = myimporter.ReadFileFromMemory( ObjModel.c_str(), ObjModel.size(), aiProcess_ValidateDataStructure ); EXPECT_EQ( nullptr, scene ); }
Mesh* createMeshFromResource(const std::string& resource, const Eigen::Vector3d &scale) { resource_retriever::Retriever retriever; resource_retriever::MemoryResource res; try { res = retriever.get(resource); } catch (resource_retriever::Exception& e) { ROS_ERROR("%s", e.what()); return NULL; } if (res.size == 0) { ROS_WARN("Retrieved empty mesh for resource '%s'", resource.c_str()); return NULL; } // Create an instance of the Importer class Assimp::Importer importer; // try to get a file extension std::string hint; std::size_t pos = resource.find_last_of("."); if (pos != std::string::npos) { hint = resource.substr(pos + 1); std::transform(hint.begin(), hint.end(), hint.begin(), ::tolower); if (hint.find("stl") != std::string::npos) hint = "stl"; } // And have it read the given file with some postprocessing const aiScene* scene = importer.ReadFileFromMemory(reinterpret_cast<void*>(res.data.get()), res.size, aiProcess_Triangulate | aiProcess_JoinIdenticalVertices | aiProcess_SortByPType | aiProcess_OptimizeGraph | aiProcess_OptimizeMeshes, hint.c_str()); if (!scene) { ROS_WARN_STREAM("Assimp reports no scene in " << resource); return NULL; } return createMeshFromAsset(scene, scale, resource); }
void _loadModel( const char * xPath ) { std::string path( xPath ); Assimp::Importer importer; #if 0 const aiScene* scene = importer.ReadFile(path , aiProcess_Triangulate | aiProcess_FlipUVs | aiProcess_CalcTangentSpace | aiProcess_GenNormals | aiProcess_SplitLargeMeshes | aiProcess_OptimizeMeshes ); #else size_t out; char * buffer = fileToBuffer( xPath, &out ); const aiScene* scene = importer.ReadFileFromMemory( buffer , out , aiProcess_Triangulate | aiProcess_FlipUVs | aiProcess_CalcTangentSpace | aiProcess_GenNormals | aiProcess_SplitLargeMeshes | aiProcess_OptimizeMeshes , "" ); free ( buffer ); #endif // Check for errors if(!scene || scene->mFlags == AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) // if is Not Zero { std::cerr << "ERROR::ASSIMP:: " << importer.GetErrorString() << std::endl; return; } // Retrieve the directory path of the filepath mDirectory = path.substr(0, path.find_last_of('/')); // Process ASSIMP's root node recursively _processNode(scene->mRootNode, scene); }
/** * Load a scene from a supported 3D file format * * @param filename Name of the file (or asset) to load * @param flags (Optional) Set of ASSIMP processing flags * * @return Returns true if the scene has been loaded */ bool LoadMesh(const std::string& filename, int flags = defaultFlags) { #if defined(__ANDROID__) // Meshes are stored inside the apk on Android (compressed) // So they need to be loaded via the asset manager AAsset* asset = AAssetManager_open(assetManager, filename.c_str(), AASSET_MODE_STREAMING); assert(asset); size_t size = AAsset_getLength(asset); assert(size > 0); void *meshData = malloc(size); AAsset_read(asset, meshData, size); AAsset_close(asset); pScene = Importer.ReadFileFromMemory(meshData, size, flags); free(meshData); #else pScene = Importer.ReadFile(filename.c_str(), flags); #endif if (pScene) { m_Entries.clear(); m_Entries.resize(pScene->mNumMeshes); // Read in all meshes in the scene for (auto i = 0; i < m_Entries.size(); i++) { m_Entries[i].vertexBase = numVertices; numVertices += pScene->mMeshes[i]->mNumVertices; const aiMesh* paiMesh = pScene->mMeshes[i]; InitMesh(&m_Entries[i], paiMesh, pScene); } return true; } else { printf("Error parsing '%s': '%s'\n", filename.c_str(), Importer.GetErrorString()); #if defined(__ANDROID__) LOGE("Error parsing '%s': '%s'", filename.c_str(), Importer.GetErrorString()); #endif return false; } }
// ------------------------------------------------------------------------------------------------ const aiScene* aiImportFileFromMemory( const char* pBuffer, unsigned int pLength, unsigned int pFlags, const char* pHint) { ai_assert(NULL != pBuffer && 0 != pLength); const aiScene* scene = NULL; ASSIMP_BEGIN_EXCEPTION_REGION(); // create an Importer for this file Assimp::Importer* imp = new Assimp::Importer(); #ifdef AI_C_THREADSAFE boost::mutex::scoped_lock lock(gMutex); #endif // copy the global property lists to the Importer instance imp->pimpl->mIntProperties = gIntProperties; imp->pimpl->mFloatProperties = gFloatProperties; imp->pimpl->mStringProperties = gStringProperties; #ifdef AI_C_THREADSAFE lock.unlock(); #endif // and have it read the file from the memory buffer scene = imp->ReadFileFromMemory( pBuffer, pLength, pFlags,pHint); // if succeeded, place it in the collection of active processes if ( scene) { #ifdef AI_C_THREADSAFE lock.lock(); #endif gActiveImports[scene] = imp; } else { // if failed, extract error code and destroy the import gLastErrorString = imp->GetErrorString(); delete imp; } // return imported data. If the import failed the pointer is NULL anyways ASSIMP_END_EXCEPTION_REGION(const aiScene*); return scene; }
// ------------------------------------------------------------------------------------------------ const aiScene* aiImportFileFromMemoryWithProperties( const char* pBuffer, unsigned int pLength, unsigned int pFlags, const char* pHint, const aiPropertyStore* props) { ai_assert( NULL != pBuffer ); ai_assert( 0 != pLength ); const aiScene* scene = NULL; ASSIMP_BEGIN_EXCEPTION_REGION(); // create an Importer for this file Assimp::Importer* imp = new Assimp::Importer(); // copy properties if(props) { const PropertyMap* pp = reinterpret_cast<const PropertyMap*>(props); ImporterPimpl* pimpl = imp->Pimpl(); pimpl->mIntProperties = pp->ints; pimpl->mFloatProperties = pp->floats; pimpl->mStringProperties = pp->strings; pimpl->mMatrixProperties = pp->matrices; } // and have it read the file from the memory buffer scene = imp->ReadFileFromMemory( pBuffer, pLength, pFlags,pHint); // if succeeded, store the importer in the scene and keep it alive if( scene) { ScenePrivateData* priv = const_cast<ScenePrivateData*>( ScenePriv(scene) ); priv->mOrigImporter = imp; } else { // if failed, extract error code and destroy the import gLastErrorString = imp->GetErrorString(); delete imp; } // return imported data. If the import failed the pointer is NULL anyways ASSIMP_END_EXCEPTION_REGION(const aiScene*); return scene; }
XFile::Scene* ParaEngine::FBXParser::ParseFBXFile(const char* buffer, int nSize) { Assimp::Importer importer; Reset(); const aiScene* pFbxScene = importer.ReadFileFromMemory(buffer, nSize, aiProcess_Triangulate | aiProcess_GenSmoothNormals, "fbx"); if (pFbxScene) { if (pFbxScene->HasMeshes()) { m_pScene = new Scene; int numMeshes = pFbxScene->mNumMeshes; for (int i = 0; i < numMeshes; i++) { Mesh* mesh = new Mesh; aiMesh* test = pFbxScene->mMeshes[i]; int numVertices = (pFbxScene->mMeshes[i])->mNumVertices; ProcessStaticFBXMesh(pFbxScene->mMeshes[i], mesh); m_pScene->mGlobalMeshes.push_back(mesh); if (i == 0) { Vector3 vMin, vMax; ParaComputeBoundingBox((Vector3*)(&mesh->mPositions[0]), mesh->mPositions.size(), sizeof(Vector3), &vMin, &vMax); m_pScene->m_header.minExtent = vMin; m_pScene->m_header.maxExtent = vMax; } } // OUTPUT_LOG("Successful parsing '%s' numMeshes:%d numMaterials:%d\n", m_sFilename.c_str(), numMeshes, pFbxScene->mNumMaterials); } if (pFbxScene->HasMaterials()) { int materials_num = pFbxScene->mNumMaterials; for (int i = 0; i < materials_num; i++) { ProcessStaticFBXMaterial(pFbxScene, i); } } } else { OUTPUT_LOG("Error parsing '%s': '%s'\n", m_sFilename.c_str(), importer.GetErrorString()); } return m_pScene; }
TEST_F(utObjImportExport, homogeneous_coordinates_Test) { static const std::string ObjModel = "v -0.500000 0.000000 0.400000 0.50000\n" "v -0.500000 0.000000 -0.800000 1.00000\n" "v 0.500000 1.000000 -0.800000 0.5000\n" "f 1 2 3\nB"; Assimp::Importer myimporter; const aiScene *scene = myimporter.ReadFileFromMemory(ObjModel.c_str(), ObjModel.size(), aiProcess_ValidateDataStructure); EXPECT_NE(nullptr, scene); EXPECT_EQ(scene->mNumMeshes, 1U); const aiMesh *mesh = scene->mMeshes[0]; EXPECT_EQ(mesh->mNumVertices, 3U); EXPECT_EQ(mesh->mNumFaces, 1U); const aiFace face = mesh->mFaces[0]; EXPECT_EQ(face.mNumIndices, 3U); const aiVector3D vertice = mesh->mVertices[0]; EXPECT_EQ(vertice.x, -1.0f); EXPECT_EQ(vertice.y, 0.0f); EXPECT_EQ(vertice.z, 0.8f); }
TEST_F(utObjImportExport, relative_indices_Test) { static const std::string ObjModel = "v -0.500000 0.000000 0.400000\n" "v -0.500000 0.000000 -0.800000\n" "v -0.500000 1.000000 -0.800000\n" "v -0.500000 1.000000 0.400000\n" "f -4 -3 -2 -1\nB"; Assimp::Importer myimporter; const aiScene *scene = myimporter.ReadFileFromMemory(ObjModel.c_str(), ObjModel.size(), aiProcess_ValidateDataStructure); EXPECT_NE(nullptr, scene); EXPECT_EQ(scene->mNumMeshes, 1U); const aiMesh *mesh = scene->mMeshes[0]; EXPECT_EQ(mesh->mNumVertices, 4U); EXPECT_EQ(mesh->mNumFaces, 1U); const aiFace face = mesh->mFaces[0]; EXPECT_EQ(face.mNumIndices, 4U); for (unsigned int i = 0; i < face.mNumIndices; ++i) { EXPECT_EQ(face.mIndices[i], i); } }
Mesh* createMeshFromBinary(const char *buffer, std::size_t size, const Eigen::Vector3d &scale, const std::string &assimp_hint) { if (!buffer || size < 1) { logWarn("Cannot construct mesh from empty binary buffer"); return NULL; } // try to get a file extension std::string hint; std::size_t pos = assimp_hint.find_last_of("."); if (pos != std::string::npos) { hint = assimp_hint.substr(pos + 1); std::transform(hint.begin(), hint.end(), hint.begin(), ::tolower); if (hint.find("stl") != std::string::npos) hint = "stl"; } // Create an instance of the Importer class Assimp::Importer importer; // And have it read the given file with some postprocessing const aiScene* scene = importer.ReadFileFromMemory(reinterpret_cast<const void*>(buffer), size, aiProcess_Triangulate | aiProcess_JoinIdenticalVertices | aiProcess_SortByPType | aiProcess_OptimizeGraph | aiProcess_OptimizeMeshes, assimp_hint.c_str()); if (scene) return createMeshFromAsset(scene, scale, assimp_hint); else return NULL; }
std::shared_ptr<Scene> Scene::loadColladaFromMemory( const char *buf, int length, const string& fileName) { MeasureDuration importDuration; Assimp::Importer importer; const aiScene *scene(importer.ReadFileFromMemory( buf, length, aiProcess_Triangulate | aiProcess_JoinIdenticalVertices | aiProcess_SortByPType)); if (!scene) { ALOGE("assimp failed to load %s: %s", fileName.c_str(), importer.GetErrorString()); return nullptr; } if (scene->mFlags == AI_SCENE_FLAGS_INCOMPLETE) { ALOGE("%s: incomplete scene data loaded", fileName.c_str()); return nullptr; } if (scene->mRootNode == NULL) { ALOGE("%s: root node null", fileName.c_str()); return nullptr; } shared_ptr<Scene> s(new Scene); long long importTime = importDuration.getMilliSeconds(); DUMP(Log::F_MODEL, "assimp load %s: %u meshes, %u materials, %u animations, %u textures, %u lights, %u cameras", fileName.c_str(), scene->mNumMeshes, scene->mNumMaterials, scene->mNumAnimations, scene->mNumTextures, scene->mNumLights, scene->mNumCameras); MeasureDuration cvtDuration; for (int i=0; i<scene->mNumCameras; i++) { shared_ptr<Camera> camera(AIAdapter::typeCast(scene->mCameras[i])); s->mCameras.push_back(camera); } for (int i=0; i<scene->mNumLights; i++) { shared_ptr<Light> light(AIAdapter::typeCast(scene->mLights[i])); s->mLights.push_back(light); } for (int i=0; i<scene->mNumTextures; i++) { shared_ptr<Texture> texture(AIAdapter::typeCast(scene->mTextures[i])); s->mTextures.push_back(texture); } for (int i=0; i<scene->mNumAnimations; i++) { shared_ptr<Animation> animation(AIAdapter::typeCast(scene->mAnimations[i])); s->mAnimations.push_back(animation); } for (int i=0; i<scene->mNumMaterials; i++) { shared_ptr<Material> material(AIAdapter::typeCast(scene->mMaterials[i])); s->mMaterials.push_back(material); } for (int i=0; i<scene->mNumMeshes; i++) { shared_ptr<Mesh> mesh(AIAdapter::typeCast(scene->mMeshes[i])); s->mMeshes.push_back(mesh); DUMP(Log::F_MODEL, "%s: " "vtx# %d, indices# %d, face#: %d, bone# %d, " "color channel#: %d, tex coord channel#: %d, " "normal: %s, {bi}tagent: %s", mesh->getName().c_str(), mesh->getNumVertices(), mesh->getNumIndices(), mesh->getNumFaces(), mesh->getNumBones(), mesh->getNumColorChannels(), mesh->getNumTextureCoordChannels(), mesh->hasVertexNormals() ? "true" : "false", mesh->hasVertexTangentsAndBitangents() ? "true" : "false"); } AIAdapter::buildSceneGraph(s, scene->mRootNode); AIAdapter::postProcess(s); long long cvtTime = cvtDuration.getMicroSeconds(); DUMP(Log::F_MODEL, "after conversion %s: %u meshes, %u materials, %u animations, %u textures, %u lights, %u cameras", fileName.c_str(), s->getNumMeshes(), s->getNumMaterials(), s->getNumAnimations(), s->getNumTextures(), s->getNumLights(), s->getNumCameras()); DUMP(Log::F_MODEL, "model load: %lld ms, conversion: %lld us", importTime, cvtTime); return s; }
"f 1 2 3\nB"; Assimp::Importer myimporter; const aiScene *scene = myimporter.ReadFileFromMemory(ObjModel.c_str(), ObjModel.size(), aiProcess_ValidateDataStructure); EXPECT_EQ(nullptr, scene); } TEST_F(utObjImportExport, 0based_array_Test) { static const std::string ObjModel = "v -0.500000 0.000000 0.400000\n" "v -0.500000 0.000000 -0.800000\n" "v -0.500000 1.000000 -0.800000\n" "f 0 1 2\nB"; Assimp::Importer myimporter; const aiScene *scene = myimporter.ReadFileFromMemory(ObjModel.c_str(), ObjModel.size(), 0); EXPECT_EQ(nullptr, scene); } TEST_F( utObjImportExport, mtllib_after_g ) { ::Assimp::Importer importer; const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/OBJ/cube_mtllib_after_g.obj", aiProcess_ValidateDataStructure ); ASSERT_NE( nullptr, scene ); EXPECT_EQ(scene->mNumMeshes, 1U); const aiMesh *mesh = scene->mMeshes[0]; const aiMaterial* mat = scene->mMaterials[mesh->mMaterialIndex]; aiString name; ASSERT_EQ(aiReturn_SUCCESS, mat->Get(AI_MATKEY_NAME, name)); EXPECT_STREQ("MyMaterial", name.C_Str()); }
/** * 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; }
ptr<ImportedScene> AssimpSceneImporter::Import(ptr<File> file) { BEGIN_TRY(); Assimp::Importer importer; const aiScene* aScene = importer.ReadFileFromMemory(file->GetData(), file->GetSize(), // calculate tangents and binormals aiProcess_CalcTangentSpace | // this flag is required for index buffer aiProcess_JoinIdenticalVertices | // only 3-vertex faces aiProcess_Triangulate | // optimize order of vertices aiProcess_ImproveCacheLocality | // sort by primitive type aiProcess_SortByPType | // get right Y uv coord aiProcess_FlipUVs | // CW order aiProcess_FlipWindingOrder ); if(!aScene) THROW(String("Assimp failed: ") + importer.GetErrorString()); ptr<ImportedScene> scene = NEW(ImportedScene()); ImportedScene::Models& models = scene->GetModels(); // assimp calls models "meshes" // so assimp's "mesh" is really a mesh + material, i.e. model models.resize(aScene->mNumMeshes); for(size_t i = 0; i < models.size(); ++i) { const aiMesh* aMesh = aScene->mMeshes[i]; // convert vertices size_t verticesCount = aMesh->mNumVertices; const aiVector3D* positions = aMesh->mVertices; const aiVector3D* tangents = aMesh->mTangents; const aiVector3D* bitangents = aMesh->mBitangents; const aiVector3D* normals = aMesh->mNormals; const aiVector3D* textureCoords = aMesh->mTextureCoords[0]; struct Vertex { vec3 position; vec3 tangent; vec3 binormal; vec3 normal; vec2 texcoord; }; auto c3 = [](const aiVector3D& v) { return vec3(v.x, v.y, v.z); }; auto c2 = [](const aiVector3D& v) { return vec2(v.x, v.y); }; ptr<MemoryFile> verticesFile = NEW(MemoryFile(verticesCount * sizeof(Vertex))); Vertex* vertices = (Vertex*)verticesFile->GetData(); for(size_t i = 0; i < verticesCount; ++i) { Vertex& vertex = vertices[i]; vertex.position = c3(positions[i]); vertex.tangent = c3(tangents[i]); vertex.binormal = c3(bitangents[i]); vertex.normal = c3(normals[i]); vertex.texcoord = c2(textureCoords[i]); } // convert indices size_t facesCount = aMesh->mNumFaces; const aiFace* faces = aMesh->mFaces; size_t indicesCount = facesCount * 3; ptr<MemoryFile> indicesFile; // if we have to use big indices if(indicesCount > 0x10000) { indicesFile = NEW(MemoryFile(facesCount * 3 * sizeof(unsigned int))); unsigned int* indices = (unsigned int*)indicesFile->GetData(); for(size_t i = 0; i < facesCount; ++i) { const aiFace& face = faces[i]; for(size_t j = 0; j < 3; ++j) indices[i * 3 + j] = face.mIndices[j]; } } // else we can use small indices else { indicesFile = NEW(MemoryFile(facesCount * 3 * sizeof(unsigned short))); unsigned short* indices = (unsigned short*)indicesFile->GetData(); for(size_t i = 0; i < facesCount; ++i) { const aiFace& face = faces[i]; for(size_t j = 0; j < 3; ++j) indices[i * 3 + j] = (unsigned short)face.mIndices[j]; } } models.push_back(NEW(Model(NEW(RawMesh(verticesFile, nullptr, indicesFile)), nullptr))); } return scene; END_TRY("Can't import scene with assimp"); }
// Load a model from file using the ASSIMP model loader and generate all resources required to render the model void loadModel(std::string filename) { // Load the model from file using ASSIMP const aiScene* scene; Assimp::Importer Importer; // Flags for loading the mesh static const int assimpFlags = aiProcess_FlipWindingOrder | aiProcess_Triangulate | aiProcess_PreTransformVertices; #if defined(__ANDROID__) // Meshes are stored inside the apk on Android (compressed) // So they need to be loaded via the asset manager AAsset* asset = AAssetManager_open(androidApp->activity->assetManager, filename.c_str(), AASSET_MODE_STREAMING); assert(asset); size_t size = AAsset_getLength(asset); assert(size > 0); void *meshData = malloc(size); AAsset_read(asset, meshData, size); AAsset_close(asset); scene = Importer.ReadFileFromMemory(meshData, size, assimpFlags); free(meshData); #else scene = Importer.ReadFile(filename.c_str(), assimpFlags); #endif // Generate vertex buffer from ASSIMP scene data float scale = 1.0f; std::vector<Vertex> vertexBuffer; // Iterate through all meshes in the file and extract the vertex components for (uint32_t m = 0; m < scene->mNumMeshes; m++) { for (uint32_t v = 0; v < scene->mMeshes[m]->mNumVertices; v++) { Vertex vertex; // Use glm make_* functions to convert ASSIMP vectors to glm vectors vertex.pos = glm::make_vec3(&scene->mMeshes[m]->mVertices[v].x) * scale; vertex.normal = glm::make_vec3(&scene->mMeshes[m]->mNormals[v].x); // Texture coordinates and colors may have multiple channels, we only use the first [0] one vertex.uv = glm::make_vec2(&scene->mMeshes[m]->mTextureCoords[0][v].x); // Mesh may not have vertex colors vertex.color = (scene->mMeshes[m]->HasVertexColors(0)) ? glm::make_vec3(&scene->mMeshes[m]->mColors[0][v].r) : glm::vec3(1.0f); // Vulkan uses a right-handed NDC (contrary to OpenGL), so simply flip Y-Axis vertex.pos.y *= -1.0f; vertexBuffer.push_back(vertex); } } size_t vertexBufferSize = vertexBuffer.size() * sizeof(Vertex); // Generate index buffer from ASSIMP scene data std::vector<uint32_t> indexBuffer; for (uint32_t m = 0; m < scene->mNumMeshes; m++) { uint32_t indexBase = static_cast<uint32_t>(indexBuffer.size()); for (uint32_t f = 0; f < scene->mMeshes[m]->mNumFaces; f++) { // We assume that all faces are triangulated for (uint32_t i = 0; i < 3; i++) { indexBuffer.push_back(scene->mMeshes[m]->mFaces[f].mIndices[i] + indexBase); } } } size_t indexBufferSize = indexBuffer.size() * sizeof(uint32_t); model.indices.count = static_cast<uint32_t>(indexBuffer.size()); // Static mesh should always be device local bool useStaging = true; if (useStaging) { struct { VkBuffer buffer; VkDeviceMemory memory; } vertexStaging, indexStaging; // Create staging buffers // Vertex data VK_CHECK_RESULT(vulkanDevice->createBuffer( VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, vertexBufferSize, &vertexStaging.buffer, &vertexStaging.memory, vertexBuffer.data())); // Index data VK_CHECK_RESULT(vulkanDevice->createBuffer( VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, indexBufferSize, &indexStaging.buffer, &indexStaging.memory, indexBuffer.data())); // Create device local buffers // Vertex buffer VK_CHECK_RESULT(vulkanDevice->createBuffer( VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, vertexBufferSize, &model.vertices.buffer, &model.vertices.memory)); // Index buffer VK_CHECK_RESULT(vulkanDevice->createBuffer( VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, indexBufferSize, &model.indices.buffer, &model.indices.memory)); // Copy from staging buffers VkCommandBuffer copyCmd = VulkanExampleBase::createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); VkBufferCopy copyRegion = {}; copyRegion.size = vertexBufferSize; vkCmdCopyBuffer( copyCmd, vertexStaging.buffer, model.vertices.buffer, 1, ©Region); copyRegion.size = indexBufferSize; vkCmdCopyBuffer( copyCmd, indexStaging.buffer, model.indices.buffer, 1, ©Region); VulkanExampleBase::flushCommandBuffer(copyCmd, queue, true); vkDestroyBuffer(device, vertexStaging.buffer, nullptr); vkFreeMemory(device, vertexStaging.memory, nullptr); vkDestroyBuffer(device, indexStaging.buffer, nullptr); vkFreeMemory(device, indexStaging.memory, nullptr); } else { // Vertex buffer VK_CHECK_RESULT(vulkanDevice->createBuffer( VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, vertexBufferSize, &model.vertices.buffer, &model.vertices.memory, vertexBuffer.data())); // Index buffer VK_CHECK_RESULT(vulkanDevice->createBuffer( VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, indexBufferSize, &model.indices.buffer, &model.indices.memory, indexBuffer.data())); } }
ModelData ModelLoader::Load(const std::vector<std::uint8_t>& fileData, const char* type) { Assimp::Importer importer; const aiScene* scene = importer.ReadFileFromMemory( fileData.data(), fileData.size(), aiProcess_Triangulate | aiProcess_FlipUVs | aiProcess_GenNormals | aiProcess_CalcTangentSpace | aiProcess_SortByPType | aiProcess_JoinIdenticalVertices | aiProcess_ImproveCacheLocality, type); if (!scene || scene->mFlags == AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) return ModelData(); // Used for creating the boundingBox glm::vec3 minPoint, maxPoint; auto processMesh = [&minPoint, &maxPoint](aiMesh* mesh, const aiScene* scene) -> MeshData { (void) scene; MeshData mData; for (std::uint32_t i = 0; i < mesh->mNumVertices; ++i) { VertexData vertexData; // Vertices const aiVector3D& vert = mesh->mVertices[i]; vertexData.vx = vert.x; vertexData.vy = vert.y; vertexData.vz = vert.z; // Normals const aiVector3D& norm = mesh->mNormals[i]; vertexData.nx = norm.x; vertexData.ny = norm.y; vertexData.nz = norm.z; if(mesh->HasTangentsAndBitangents()) { // Tangents const aiVector3D& tan = mesh->mTangents[i]; vertexData.tnx = tan.x; vertexData.tny = tan.y; vertexData.tnz = tan.z; } // TODO: think if we need the 'else' part //else {} // Texture coordinates if (mesh->mTextureCoords[0]) { const aiVector3D& texCoord = mesh->mTextureCoords[0][i]; vertexData.tx = texCoord.x; vertexData.ty = texCoord.y; } else { vertexData.tx = 0; vertexData.ty = 0; } // AABB minPoint if (vert.x < minPoint.x) minPoint.x = vert.x; if (vert.y < minPoint.y) minPoint.y = vert.y; if (vert.z < minPoint.z) minPoint.z = vert.z; // AABB maxPoint if (vert.x > maxPoint.x) maxPoint.x = vert.x; if (vert.y > maxPoint.y) maxPoint.y = vert.y; if (vert.z > maxPoint.z) maxPoint.z = vert.z; // Material index mData.meshIndex = mesh->mMaterialIndex; mData.data.push_back(vertexData); } // Indices for (std::uint32_t i = 0; i < mesh->mNumFaces; ++i) { aiFace face = mesh->mFaces[i]; for (std::uint32_t j = 0; j < face.mNumIndices; ++j) mData.indices.push_back(face.mIndices[j]); } // Material if (scene->HasMaterials()) { aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex]; if (material) { auto texTypes = {aiTextureType_DIFFUSE, aiTextureType_SPECULAR, aiTextureType_NORMALS, aiTextureType_NONE}; for (auto type : texTypes) { std::uint32_t texCount = material->GetTextureCount(type); for (std::uint32_t i = 0; i < texCount; ++i) { aiString str; material->GetTexture(type, i, &str); // TODO } } } } return mData; }; std::function<ModelData(aiNode*, const aiScene*)> processNode = [&processMesh, &processNode](aiNode* node, const aiScene* scene) -> ModelData { ModelData model; // Process all the node's meshes for (std::uint32_t i = 0; i < node->mNumMeshes; ++i) { aiMesh* mesh = scene->mMeshes[node->mMeshes[i]]; model.meshes.push_back(processMesh(mesh, scene)); } // Same for its children for (std::uint32_t i = 0; i < node->mNumChildren; ++i) { ModelData childModel = processNode(node->mChildren[i], scene); for (auto& mesh : childModel.meshes) model.meshes.push_back(std::move(mesh)); } return model; }; ModelData model = processNode(scene->mRootNode, scene); model.boundingBox = AABB(minPoint, maxPoint); return model; }
TEST_F( utPLYImportExport, parseErrorTest ) { Assimp::Importer importer; const aiScene *scene = importer.ReadFileFromMemory( test_file, strlen( test_file ), 0 ); EXPECT_NE( nullptr, scene ); }
TEST_F( utPLYImportExport, parseErrorTest ) { Assimp::Importer importer; //Could not use aiProcess_ValidateDataStructure since it's missing faces. const aiScene *scene = importer.ReadFileFromMemory( test_file, strlen( test_file ), 0); EXPECT_NE( nullptr, scene ); }