//------------------------------------------- bool ofxAssimpModelLoader::loadModel(ofBuffer & buffer, bool optimize, const char * extension){ normalizeFactor = ofGetWidth() / 2.0; // only ever give us triangles. aiSetImportPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_LINE | aiPrimitiveType_POINT ); aiSetImportPropertyInteger(AI_CONFIG_PP_PTV_NORMALIZE, true); // aiProcess_FlipUVs is for VAR code. Not needed otherwise. Not sure why. unsigned int flags = aiProcessPreset_TargetRealtime_MaxQuality | aiProcess_Triangulate | aiProcess_FlipUVs; if(optimize) flags |= aiProcess_ImproveCacheLocality | aiProcess_OptimizeGraph | aiProcess_OptimizeMeshes | aiProcess_JoinIdenticalVertices | aiProcess_RemoveRedundantMaterials; if(scene){ clear(); } scene = aiImportFileFromMemory(buffer.getBinaryBuffer(), buffer.size(), flags, extension); if(scene){ calculateDimensions(); loadGLResources(); if(getAnimationCount()) ofLog(OF_LOG_VERBOSE, "scene has animations"); else { ofLog(OF_LOG_VERBOSE, "no animations"); } return true; }else{ ofLog(OF_LOG_ERROR,string("ofxAssimpModelLoader: ") + aiGetErrorString()); return false; } }
int main(int argc, const char **argv) { if(argc == 1) { const char *params[] = {"-p", "win32", "../../../Raw/Level/level.dae", "./"}; parseArgs(4, params); } else parseArgs(argc, argv); printf("Outputting in %s format\n", g_Endianness == LittleEndian ? "LittleEndian" : "BigEndian"); aiSetImportPropertyInteger( AI_CONFIG_PP_RVC_FLAGS, aiComponent_TANGENTS_AND_BITANGENTS | aiComponent_COLORS ); // Remove degenerates; if we don't do this, tangents & bitangents // can't be calculated for meshes with degenerate tris aiSetImportPropertyInteger( AI_CONFIG_PP_FD_REMOVE, true ); const aiScene* scene = aiImportFile( filename.c_str(), aiProcessPreset_TargetRealtime_MaxQuality | aiProcess_GenNormals | aiProcess_RemoveComponent ); if(!scene) printf("Failed to load %s\n", filename.c_str()); else { makeDirectories(); cJSON *root = cJSON_CreateObject(); cJSON *list = cJSON_CreateArray(); cJSON_AddItemToObject(root, "scene", list); aiMatrix4x4 identity; printf("Loaded %s\n", filename.c_str()); processSceneRecursive(scene, list, identity, scene->mRootNode); printf("Done\n"); outputFinalStep(); writeJsonToFile(root, outdir + "/scene.scene"); } aiReleaseImport(scene); getchar(); return 0; }
//------------------------------------------ void ofxAssimpModelLoader::loadModel(string modelName){ // if we have a model loaded, unload the f****r. if(scene != NULL) { aiReleaseImport(scene); scene = NULL; deleteGLResources(); } // Load our new path. string filepath = ofToDataPath(modelName); ofLog(OF_LOG_VERBOSE, "loading model %s", filepath.c_str()); // only ever give us triangles. aiSetImportPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_LINE | aiPrimitiveType_POINT ); aiSetImportPropertyInteger(AI_CONFIG_PP_PTV_NORMALIZE, true); // aiProcess_FlipUVs is for VAR code. Not needed otherwise. Not sure why. scene = (aiScene*) aiImportFile(filepath.c_str(), aiProcessPreset_TargetRealtime_MaxQuality | aiProcess_OptimizeGraph | aiProcess_Triangulate | aiProcess_FlipUVs | 0 ); if(scene){ ofLog(OF_LOG_VERBOSE, "initted scene with %i meshes & %i animations", scene->mNumMeshes, scene->mNumAnimations); getBoundingBoxWithMinVector(&scene_min, &scene_max); scene_center.x = (scene_min.x + scene_max.x) / 2.0f; scene_center.y = (scene_min.y + scene_max.y) / 2.0f; scene_center.z = (scene_min.z + scene_max.z) / 2.0f; // optional normalized scaling normalizedScale = scene_max.x-scene_min.x; normalizedScale = aisgl_max(scene_max.y - scene_min.y,normalizedScale); normalizedScale = aisgl_max(scene_max.z - scene_min.z,normalizedScale); normalizedScale = 1.f / normalizedScale; normalizedScale *= ofGetWidth() / 2.0; glPushAttrib(GL_ALL_ATTRIB_BITS); glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS); loadGLResources(); glPopClientAttrib(); glPopAttrib(); if(getAnimationCount()) ofLog(OF_LOG_VERBOSE, "scene has animations"); else { ofLog(OF_LOG_VERBOSE, "no animations"); } } }
//------------------------------------------ bool ofxAssimpModelLoader::loadModel(string modelName, unsigned extraFlags /* = 0 */){ // if we have a model loaded, unload the f****r. if(scene != NULL){ clear(); } // Load our new path. filepath = modelName; string filepath = ofToDataPath(modelName); //theo added - so we can have models and their textures in sub folders modelFolder = ofFilePath::getEnclosingDirectory(filepath); ofLog(OF_LOG_VERBOSE, "loading model %s", filepath.c_str()); ofLog(OF_LOG_VERBOSE, "loading from folder %s", modelFolder.c_str()); // only ever give us triangles. aiSetImportPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_LINE | aiPrimitiveType_POINT ); aiSetImportPropertyInteger(AI_CONFIG_PP_PTV_NORMALIZE, true); // aiProcess_FlipUVs is for VAR code. Not needed otherwise. Not sure why. unsigned int flags = aiProcessPreset_TargetRealtime_MaxQuality | aiProcess_Triangulate | aiProcess_FlipUVs; flags |= extraFlags; /* if(optimize) flags |= aiProcess_ImproveCacheLocality | aiProcess_OptimizeGraph | aiProcess_OptimizeMeshes | aiProcess_JoinIdenticalVertices | aiProcess_RemoveRedundantMaterials; */ scene = aiImportFile(filepath.c_str(), flags); if(scene){ calculateDimensions(); loadGLResources(); if(getAnimationCount()) ofLog(OF_LOG_VERBOSE, "scene has animations"); else { ofLog(OF_LOG_VERBOSE, "no animations"); } collectNodeTransforms(scene, scene->mRootNode); collectBoneTransforms(); return true; }else{ ofLog(OF_LOG_ERROR,string("ofxAssimpModelLoader: ") + aiGetErrorString()); return false; } }
unsigned int ofxAssimpModelLoader::initImportProperties(bool optimize) { store.reset(aiCreatePropertyStore(), aiReleasePropertyStore); // only ever give us triangles. aiSetImportPropertyInteger(store.get(), AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_LINE | aiPrimitiveType_POINT ); aiSetImportPropertyInteger(store.get(), AI_CONFIG_PP_PTV_NORMALIZE, true); // aiProcess_FlipUVs is for VAR code. Not needed otherwise. Not sure why. unsigned int flags = aiProcessPreset_TargetRealtime_MaxQuality | aiProcess_Triangulate | aiProcess_FlipUVs; if(optimize) flags |= aiProcess_ImproveCacheLocality | aiProcess_OptimizeGraph | aiProcess_OptimizeMeshes | aiProcess_JoinIdenticalVertices | aiProcess_RemoveRedundantMaterials; return flags; }
const aiScene* MeshShape::loadMesh(const std::string& _fileName) { aiPropertyStore* propertyStore = aiCreatePropertyStore(); // remove points and lines aiSetImportPropertyInteger(propertyStore, AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_POINT | aiPrimitiveType_LINE); const aiScene* scene = aiImportFileExWithProperties(_fileName.c_str(), aiProcess_GenNormals | aiProcess_Triangulate | aiProcess_JoinIdenticalVertices | aiProcess_SortByPType | aiProcess_OptimizeMeshes, NULL, propertyStore); aiReleasePropertyStore(propertyStore); // Assimp rotates collada files such that the up-axis (specified in the // collada file) aligns with assimp's y-axis. Here we are reverting this // rotation. We are only catching files with the .dae file ending here. We // might miss files with an .xml file ending, which would need to be looked // into to figure out whether they are collada files. if (_fileName.length() >= 4 && _fileName.substr(_fileName.length() - 4, 4) == ".dae") { scene->mRootNode->mTransformation = aiMatrix4x4(); } scene = aiApplyPostProcessing(scene, aiProcess_PreTransformVertices); return scene; }
//------------------------------------------- bool ofxAssimpModelLoader::loadModel(ofBuffer & buffer, bool optimize, const char * extension){ normalizeFactor = ofGetWidth() / 2.0; if(scene != NULL){ clear(); } // only ever give us triangles. aiSetImportPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_LINE | aiPrimitiveType_POINT ); aiSetImportPropertyInteger(AI_CONFIG_PP_PTV_NORMALIZE, true); // aiProcess_FlipUVs is for VAR code. Not needed otherwise. Not sure why. unsigned int flags = aiProcessPreset_TargetRealtime_MaxQuality | aiProcess_Triangulate | aiProcess_FlipUVs; if(optimize) flags |= aiProcess_ImproveCacheLocality | aiProcess_OptimizeGraph | aiProcess_OptimizeMeshes | aiProcess_JoinIdenticalVertices | aiProcess_RemoveRedundantMaterials; scene = aiImportFileFromMemory(buffer.getBinaryBuffer(), buffer.size(), flags, extension); if(scene){ calculateDimensions(); loadGLResources(); update(); if(getAnimationCount()) ofLogVerbose("ofxAssimpModelLoader") << "loadModel(): scene has " << getAnimationCount() << "animations"; else { ofLogVerbose("ofxAssimpModelLoader") << "loadMode(): no animations"; } ofAddListener(ofEvents().exit,this,&ofxAssimpModelLoader::onAppExit); return true; }else{ ofLogError("ofxAssimpModelLoader") << "loadModel(): " + (string) aiGetErrorString(); clear(); return false; } }
const aiScene* ShapeMesh::loadMesh(const string& fileName) { aiPropertyStore* propertyStore = aiCreatePropertyStore(); aiSetImportPropertyInteger(propertyStore, AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_POINT | aiPrimitiveType_LINE ); // remove points and lines const aiScene* scene = aiImportFileExWithProperties(fileName.c_str(), aiProcess_GenNormals | aiProcess_Triangulate | aiProcess_JoinIdenticalVertices | aiProcess_SortByPType | aiProcess_PreTransformVertices | aiProcess_OptimizeMeshes, NULL, propertyStore); aiReleasePropertyStore(propertyStore); return scene; }
void NCAssimpModel::setup(string pathtomodel){ string filepath = ofToDataPath(pathtomodel); bool optimize =false; // only ever give us triangles. aiSetImportPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_LINE | aiPrimitiveType_POINT ); aiSetImportPropertyInteger(AI_CONFIG_PP_PTV_NORMALIZE, true); // aiProcess_FlipUVs is for VAR code. Not needed otherwise. Not sure why. unsigned int flags = aiProcessPreset_TargetRealtime_MaxQuality | aiProcess_OptimizeGraph | aiProcess_Triangulate | aiProcess_FlipUVs | aiProcess_CalcTangentSpace | aiProcess_JoinIdenticalVertices | aiProcess_SortByPType; if(optimize) flags |= aiProcess_ImproveCacheLocality | aiProcess_OptimizeGraph | aiProcess_OptimizeMeshes | aiProcess_JoinIdenticalVertices | aiProcess_RemoveRedundantMaterials; if (scene) { for(int i = 0; i < 100; i++) { jointlabels[i] = ""; } bones.clear(); delete scene; } scene = aiImportFile(filepath.c_str(), flags); if(scene){ meshes.clear(); initBones(); for (int i=0;i<scene->mNumMeshes;i++) { NCAIMesh mesh; mesh.setup(scene->mMeshes[i], scene); meshes.push_back(mesh); } } }
bool opMeshBaker::LoadAndBake(const char* input_filename, const char* output_filename) { aiSetImportPropertyFloat("AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE", 80.0f); aiSetImportPropertyInteger("AI_CONFIG_PP_RVC_FLAGS", aiComponent_NORMALS); // throw away normals (generate them) /* generate a mesh from the input file: Calculate tangents and bitangents Triangulate Weld vertices together Sort by primitive type Generate smooth normals Fix infacing normals Generate UV coords optimize the mesh */ const aiScene* scene = aiImportFile(input_filename, aiProcess_CalcTangentSpace | aiProcess_Triangulate | aiProcess_JoinIdenticalVertices | aiProcess_SortByPType | aiProcess_GenSmoothNormals | aiProcess_FixInfacingNormals | aiProcess_GenUVCoords | aiProcess_ConvertToLeftHanded | aiProcess_OptimizeMeshes); if(scene == NULL) { palPrintf("Could not open understand input file: %s\n", input_filename); return false; } palPrintf("Baking %s to %s\n", input_filename, output_filename); // we only support 1:1 mapping between input file meshes and output file meshes palAssert(scene->mNumMeshes == 1); aiMesh* mesh = scene->mMeshes[0]; // we expect faces palAssert(mesh->HasFaces() == true); // we expect vertex positions palAssert(mesh->HasPositions() == true); //printf("Has vertex positions.\n"); // we expect normals palAssert(mesh->HasNormals() == true); //printf("Has vertex normal.\n"); // we only support triangular meshes palAssert(mesh->mPrimitiveTypes == aiPrimitiveType_TRIANGLE); palFile baked; int r; r = baked.OpenForWritingTruncate(output_filename); if (r != 0) { palPrintf("Could not open output file: %s\n", output_filename); return false; } palArray<opBakedMeshArrayHeader> mesh_arrays; mesh_arrays.SetAllocator(g_DefaultHeapAllocator); palArray<void*> mesh_arrays_data; mesh_arrays_data.SetAllocator(g_DefaultHeapAllocator); palArray<opBakedMeshAttributeHeader> mesh_attributes; mesh_attributes.SetAllocator(g_DefaultHeapAllocator); opBakedMeshArrayHeader mesh_array; // index array mesh_array.array_type = OP_BAKED_MESH_INDEX_ARRAY; // pack index data tightly uint32_t num_indices = mesh->mNumFaces * 3; void* index_array_data = NULL; if (num_indices < 0xffff) { mesh_array.index_type = kOpRenderDeviceFormatUintR16; mesh_array.length_in_bytes = num_indices * sizeof(uint16_t); uint16_t* indices = (uint16_t*)g_DefaultHeapAllocator->Allocate(mesh_array.length_in_bytes); palAssert(indices); index_array_data = indices; for (unsigned int i = 0; i < mesh->mNumFaces; i++) { *indices = mesh->mFaces[i].mIndices[0]; indices++; *indices = mesh->mFaces[i].mIndices[1]; indices++; *indices = mesh->mFaces[i].mIndices[2]; indices++; } } else { mesh_array.index_type = kOpRenderDeviceFormatUintR32; mesh_array.length_in_bytes = num_indices * sizeof(uint32_t); uint32_t* indices = (uint32_t*)g_DefaultHeapAllocator->Allocate(mesh_array.length_in_bytes); palAssert(indices); index_array_data = indices; for (unsigned int i = 0; i < mesh->mNumFaces; i++) { *indices = mesh->mFaces[i].mIndices[0]; indices++; *indices = mesh->mFaces[i].mIndices[1]; indices++; *indices = mesh->mFaces[i].mIndices[2]; indices++; } } printf("Indexed triangle mesh: %d triangles (%d bytes in index data)\n", num_indices/3, mesh_array.length_in_bytes); mesh_arrays_data.push_back(index_array_data); mesh_arrays.push_back(mesh_array); // pack into single interleaved array { uint32_t offset = 0; opBakedMeshAttributeHeader mesh_component; // position mesh_component.attribute_type = OP_BAKED_MESH_ATTRIBUTE_VERTICES; mesh_component.element_type = kOpRenderDeviceFormatFloat32x3; mesh_component.offset_in_bytes = 0; mesh_component.array_index = 1; mesh_attributes.push_back(mesh_component); offset += 3 * sizeof(float); // normals mesh_component.attribute_type = OP_BAKED_MESH_ATTRIBUTE_NORMALS; mesh_component.element_type = kOpRenderDeviceFormatFloat32x3; mesh_component.offset_in_bytes = offset; mesh_component.array_index = 1; mesh_attributes.push_back(mesh_component); offset += 3 * sizeof(float); if (mesh->HasTangentsAndBitangents()) { // tangents mesh_component.attribute_type = OP_BAKED_MESH_ATTRIBUTE_TANGENTS; mesh_component.element_type = kOpRenderDeviceFormatFloat32x3; mesh_component.offset_in_bytes = offset; mesh_component.array_index = 1; mesh_attributes.push_back(mesh_component); offset += 3 * sizeof(float); // bitangents mesh_component.attribute_type = OP_BAKED_MESH_ATTRIBUTE_BITANGENTS; mesh_component.element_type = kOpRenderDeviceFormatFloat32x3; mesh_component.offset_in_bytes = offset; mesh_component.array_index = 1; mesh_attributes.push_back(mesh_component); offset += 3 * sizeof(float); printf("Has vertex tangents and bitangents.\n"); } // texture coordinates 0..3 for (unsigned int i = 0; i < mesh->GetNumUVChannels(); i++) { mesh_component.attribute_type = (opBakedMeshAttributeType)(OP_BAKED_MESH_ATTRIBUTE_TEXCOORDS0 + i); mesh_component.element_type = TextureElementType(mesh->mNumUVComponents[i]); mesh_component.offset_in_bytes = offset; mesh_component.array_index = 1; printf("Has UV channel %d (%d)\n", i, mesh->mNumUVComponents[i]); mesh_attributes.push_back(mesh_component); offset += mesh->mNumUVComponents[i] * sizeof(float); } // fill in the stride between vertices for (int i = 0; i < mesh_attributes.GetSize(); i++) { mesh_attributes[i].stride_in_bytes = offset; } mesh_array.array_type = OP_BAKED_MESH_VERTEX_ARRAY; mesh_array.length_in_bytes = mesh->mNumVertices * offset; printf("%d vertices (%d bytes in vertex data)\n", mesh->mNumVertices, mesh_array.length_in_bytes); void* vertex_array_data = g_DefaultHeapAllocator->Allocate(mesh_array.length_in_bytes); float* vertex_array_data_f = (float*)vertex_array_data; for (unsigned int i = 0; i < mesh->mNumVertices; i++) { // positions *vertex_array_data_f = mesh->mVertices[i].x; vertex_array_data_f++; *vertex_array_data_f = mesh->mVertices[i].y; vertex_array_data_f++; *vertex_array_data_f = mesh->mVertices[i].z; vertex_array_data_f++; // normals *vertex_array_data_f = mesh->mNormals[i].x; vertex_array_data_f++; *vertex_array_data_f = mesh->mNormals[i].y; vertex_array_data_f++; *vertex_array_data_f = mesh->mNormals[i].z; vertex_array_data_f++; if (mesh->HasTangentsAndBitangents()) { // tangents *vertex_array_data_f = mesh->mTangents[i].x; vertex_array_data_f++; *vertex_array_data_f = mesh->mTangents[i].y; vertex_array_data_f++; *vertex_array_data_f = mesh->mTangents[i].z; vertex_array_data_f++; // bitangents *vertex_array_data_f = mesh->mBitangents[i].x; vertex_array_data_f++; *vertex_array_data_f = mesh->mBitangents[i].y; vertex_array_data_f++; *vertex_array_data_f = mesh->mBitangents[i].z; vertex_array_data_f++; } // texture channels for (unsigned int tc = 0; tc < mesh->GetNumUVChannels(); tc++) { // texture coordinates for (unsigned int st = 0; st < mesh->mNumUVComponents[tc]; st++) { //printf("tex[%d] = %f\n", st, mesh->mTextureCoords[tc][i][st]); *vertex_array_data_f = mesh->mTextureCoords[tc][i][st]; vertex_array_data_f++; } } } // push data back into arrays mesh_arrays_data.push_back(vertex_array_data); mesh_arrays.push_back(mesh_array); } // write data to disk opBakedMeshHeader header; header.magic = OP_BAKED_MESH_FILE_MAGIC; header.attribute_count = mesh_attributes.GetSize(); header.attribute_offset = sizeof(header); header.array_count = mesh_arrays.GetSize(); header.array_offset = sizeof(header) + sizeof(opBakedMeshAttributeHeader) * mesh_attributes.GetSize(); baked.Write(&header, sizeof(header)); for (int i = 0; i < mesh_attributes.GetSize();i++) { opBakedMeshAttributeHeader& component_attribute = mesh_attributes[i]; baked.Write(&component_attribute, sizeof(component_attribute)); } uint32_t array_offset = sizeof(header); array_offset += sizeof(opBakedMeshAttributeHeader) * mesh_attributes.GetSize(); array_offset += sizeof(opBakedMeshArrayHeader) * mesh_arrays.GetSize(); for (int i = 0; i < mesh_arrays.GetSize(); i++) { opBakedMeshArrayHeader& array_header = mesh_arrays[i]; array_header.offset_in_bytes = array_offset; baked.Write(&array_header, sizeof(array_header)); array_offset += array_header.length_in_bytes; } for (int i = 0; i < mesh_arrays.GetSize(); i++) { baked.Write(mesh_arrays_data[i], mesh_arrays[i].length_in_bytes); g_DefaultHeapAllocator->Deallocate(mesh_arrays_data[i]); } baked.Close(); aiReleaseImport(scene); return true; }
const aiScene* MeshShape::loadMesh( const std::string& _uri, const common::ResourceRetrieverPtr& _retriever) { // Remove points and lines from the import. aiPropertyStore* propertyStore = aiCreatePropertyStore(); aiSetImportPropertyInteger(propertyStore, AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_POINT | aiPrimitiveType_LINE ); // Wrap ResourceRetriever in an IOSystem from Assimp's C++ API. Then wrap // the IOSystem in an aiFileIO from Assimp's C API. Yes, this API is // completely ridiculous... AssimpInputResourceRetrieverAdaptor systemIO(_retriever); aiFileIO fileIO = createFileIO(&systemIO); // Import the file. const aiScene* scene = aiImportFileExWithProperties( _uri.c_str(), aiProcess_GenNormals | aiProcess_Triangulate | aiProcess_JoinIdenticalVertices | aiProcess_SortByPType | aiProcess_OptimizeMeshes, &fileIO, propertyStore ); // If succeeded, store the importer in the scene to keep it alive. This is // necessary because the importer owns the memory that it allocates. if(!scene) { dtwarn << "[MeshShape::loadMesh] Failed loading mesh '" << _uri << "'.\n"; return nullptr; } // Assimp rotates collada files such that the up-axis (specified in the // collada file) aligns with assimp's y-axis. Here we are reverting this // rotation. We are only catching files with the .dae file ending here. We // might miss files with an .xml file ending, which would need to be looked // into to figure out whether they are collada files. std::string extension; const size_t extensionIndex = _uri.find_last_of('.'); if(extensionIndex != std::string::npos) extension = _uri.substr(extensionIndex); std::transform(std::begin(extension), std::end(extension), std::begin(extension), ::tolower); if(extension == ".dae" || extension == ".zae") scene->mRootNode->mTransformation = aiMatrix4x4(); // Finally, pre-transform the vertices. We can't do this as part of the // import process, because we may have changed mTransformation above. scene = aiApplyPostProcessing(scene, aiProcess_PreTransformVertices); if(!scene) dtwarn << "[MeshShape::loadMesh] Failed pre-transforming vertices.\n"; return scene; }