bool Mesh::Load(const GLfloat* positions, const GLfloat* colors, const GLfloat* normals, const GLfloat* texCoords, const GLuint* indices, int numVertices, int numIndices, GLenum drawMode, StorageType storageType ) { if (!positions || numVertices <= 0) { return false; } mDrawMode = drawMode; mStorageType = storageType; mNumVertices = numVertices; mNumIndices = (numIndices <= 0 || indices == nullptr) ? numVertices : numIndices; mHasColors = (colors != nullptr); mHasNormals = (normals != nullptr); mHasTexCoord = (texCoords != nullptr); mVertexSize = 3 + 3*mHasColors + 3*mHasNormals + 2*mHasTexCoord; mVertices = new GLfloat [mVertexSize * mNumVertices]; mIndices = new GLuint [mNumIndices]; if (mStorageType == kTightlyPacked) { // Initialize vertices buffer array. for (int i = 0; i < mNumVertices; i++) { float* position = PositionAt(i); float* texCoord = TexCoordAt(i); float* normal = NormalAt(i); float* color = ColorAt(i); memcpy(position, positions + 3*i, sizeof(GLfloat)*3); if (HasColors()) { memcpy(color, colors + 3*i, sizeof(GLfloat)*3); } if (HasNormals()) { memcpy(normal, normals + 3*i, sizeof(GLfloat)*3); } if (HasTexCoord()) { memcpy(texCoord, texCoords + 2*i, sizeof(GLfloat)*2); } } } else { GLfloat* dest = mVertices; memcpy(dest, positions, 3*sizeof(GLfloat)*mNumVertices); dest += 3*mNumVertices; if (HasColors()) { memcpy(dest, colors, 3*sizeof(GLfloat)*mNumVertices); dest += 3*mNumVertices; } if (HasNormals()) { memcpy(dest, normals, 3*sizeof(GLfloat)*mNumVertices); dest += 3*mNumVertices; } if (HasTexCoord()) { memcpy(dest, texCoords, 2*sizeof(GLfloat)*mNumVertices); dest += 2*mNumVertices; } } // Initialize element array (indices array). if (indices) // Element array provided. { memcpy(mIndices, indices, sizeof(GLuint)*mNumIndices); } else // Element array wasn't provided -- build it up. { for (int i = 0; i < mNumIndices; i++) { mIndices[i] = i; } } mInitialized = true; Mesh::Upload(); return true; }
void Mesh::Upload() { if (mProgramHandle == 0) { std::cerr << "ERROR Program Handle must be set before creating buffer objects.\n"; return; } if (!mInitialized) { std::cerr << "ERROR The mesh must be initialized before creating buffer objects.\n"; return; } // Specify how the arguments will be passed to shaders. GLuint locTexCoordAttrib = glGetAttribLocation(mProgramHandle, "in_tex_coord"); GLuint locPositionAttrib = glGetAttribLocation(mProgramHandle, "in_position"); GLuint locNormalAttrib = glGetAttribLocation(mProgramHandle, "in_normal"); GLuint locColorAttrib = glGetAttribLocation(mProgramHandle, "in_color"); // Generate Buffers. glGenVertexArrays(1, &mVao); glGenBuffers(1, &mVbo); glGenBuffers(1, &mEab); // Specify VAO. glBindVertexArray(mVao); // Upload indices to GPU. glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mEab); glBufferData(GL_ELEMENT_ARRAY_BUFFER, mNumIndices * sizeof(GLuint), mIndices, GL_STATIC_DRAW); // Enable/Disable each vertex attribute. glEnableVertexAttribArray(locPositionAttrib); if (HasColors()) { glEnableVertexAttribArray(locColorAttrib); } else { glDisableVertexAttribArray(locColorAttrib); } if (HasNormals()) { glEnableVertexAttribArray(locNormalAttrib); } else { glDisableVertexAttribArray(locNormalAttrib); } if (HasTexCoord()) { glEnableVertexAttribArray(locTexCoordAttrib); } else { glDisableVertexAttribArray(locTexCoordAttrib); } // Upload vertices to GPU. glBindBuffer(GL_ARRAY_BUFFER, mVbo); glBufferData(GL_ARRAY_BUFFER, mVertexSize * mNumVertices * sizeof(GLfloat), mVertices, GL_STATIC_DRAW); if (mStorageType == kTightlyPacked) { GLfloat stride = sizeof(GLfloat) * mVertexSize; glVertexAttribPointer(locPositionAttrib, 3, GL_FLOAT, GL_FALSE, stride, 0); glVertexAttribPointer(locColorAttrib, 3, GL_FLOAT, GL_FALSE, stride, (void*)(sizeof(GLfloat) * 3)); glVertexAttribPointer(locNormalAttrib, 3, GL_FLOAT, GL_FALSE, stride, (void*)(sizeof(GLfloat) * (3 + 3*mHasColors))); glVertexAttribPointer(locTexCoordAttrib, 2, GL_FLOAT, GL_FALSE, stride, (void*)(sizeof(GLfloat) * (3 + 3*mHasColors + 3*mHasNormals)) ); } else // Sub buffered storage type. { glVertexAttribPointer(locPositionAttrib, 3, GL_FLOAT, GL_FALSE, 0, 0); glVertexAttribPointer(locColorAttrib, 3, GL_FLOAT, GL_FALSE, 0, (void*)(sizeof(GLfloat) * 3*mNumVertices)); glVertexAttribPointer(locNormalAttrib, 3, GL_FLOAT, GL_FALSE, 0, (void*)(sizeof(GLfloat) * (3 + 3*mHasColors)*mNumVertices)); glVertexAttribPointer(locTexCoordAttrib, 2, GL_FLOAT, GL_FALSE, 0, (void*)(sizeof(GLfloat) * (3 + 3*mHasColors + 3*mHasNormals)*mNumVertices)); } }
// Assuming the metallic-roughness material model of models loaded with GLTF. std::pair<ID, std::vector<std::pair<Texture::Type, std::string>>> MeshManager::load_mesh(const std::string& directory, const std::string& file) { MeshInformation mesh_info; mesh_info.loaded_from_filepath = directory + file; Assimp::Importer importer; auto scene = importer.ReadFile(mesh_info.loaded_from_filepath.c_str(), aiProcess_Triangulate | aiProcess_JoinIdenticalVertices); if (scene == nullptr || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE) { Log::error(std::string(importer.GetErrorString())); return {0, {}}; } std::vector<std::pair<Texture::Type, std::string>> texture_info; if (scene->HasMaterials()) { Log::info("Number of materials: " + std::to_string(scene->mNumMaterials)); for (size_t i = 0; i < scene->mNumMaterials; i++) { auto material = scene->mMaterials[i]; aiString material_name; material->Get(AI_MATKEY_NAME, material_name); Log::info("Material name: " + std::string(material_name.C_Str())); aiString diffuse_filepath; if (material->GetTexture(aiTextureType_DIFFUSE, 0, &diffuse_filepath) == AI_SUCCESS) { Log::info("Diffuse texture name: " + std::string(directory.c_str()) + std::string(diffuse_filepath.data)); std::string texture_filepath(diffuse_filepath.data); texture_filepath.insert(0, directory); texture_info.push_back({Texture::Type::Diffuse, texture_filepath}); } aiString specular_filepath; if (material->GetTexture(aiTextureType_SPECULAR, 0, &specular_filepath) == AI_SUCCESS) { Log::info("Specular texture name: " + std::string(directory.c_str()) + std::string(specular_filepath.data)); } aiString ambient_filepath; if (material->GetTexture(aiTextureType_AMBIENT, 0, &ambient_filepath) == AI_SUCCESS) { Log::info("Ambient occlusion texture name: " + std::string(directory.c_str()) + std::string(ambient_filepath.data)); } aiString shininess_filepath; if (material->GetTexture(aiTextureType_SHININESS, 0, &shininess_filepath) == AI_SUCCESS) { Log::info("Shininess texture name: " + std::string(directory.c_str()) + std::string(shininess_filepath.data)); } aiString emissive_filepath; if (material->GetTexture(aiTextureType_EMISSIVE, 0, &emissive_filepath) == AI_SUCCESS) { Log::info("Emissive texture name: " + std::string(directory.c_str()) + std::string(emissive_filepath.data)); std::string texture_filepath(emissive_filepath.data); texture_filepath.insert(0, directory); texture_info.push_back({Texture::Type::Emissive, texture_filepath}); // TODO: Fetch emissive factor as well } aiString displacement_filepath; if (material->GetTexture(aiTextureType_DISPLACEMENT, 0, &displacement_filepath) == AI_SUCCESS) { Log::info("Displacement texture name: " + std::string(directory.c_str()) + std::string(displacement_filepath.data)); } aiString height_filepath; if (material->GetTexture(aiTextureType_HEIGHT, 0, &height_filepath) == AI_SUCCESS) { Log::info("Bumpmap texture name: " + std::string(directory.c_str()) + std::string(height_filepath.data)); } // Lightmap is usually the ambient occlusion map ... aiString lightmap_filepath; if (material->GetTexture(aiTextureType_LIGHTMAP, 0, &lightmap_filepath) == AI_SUCCESS) { Log::info("Lightmap texture name: " + std::string(directory.c_str()) + std::string(lightmap_filepath.data)); std::string texture_filepath(lightmap_filepath.data); texture_filepath.insert(0, directory); texture_info.push_back({Texture::Type::AmbientOcclusion, texture_filepath}); } aiString normals_filepath; if (material->GetTexture(aiTextureType_NORMALS, 0, &normals_filepath) == AI_SUCCESS) { Log::info("Normals texture name: " + std::string(directory.c_str()) + std::string(normals_filepath.data)); } aiString reflection_filepath; if (material->GetTexture(aiTextureType_REFLECTION, 0, &reflection_filepath) == AI_SUCCESS) { Log::info("Reflection texture name: " + std::string(directory.c_str()) + std::string(reflection_filepath.data)); } aiString opacity_filepath; if (material->GetTexture(aiTextureType_OPACITY, 0, &opacity_filepath) == AI_SUCCESS) { Log::info("Opacity texture name: " + std::string(directory.c_str()) + std::string(opacity_filepath.data)); } // NOTE: Roughness metallic textures are not detected so here we are assuming this is the unknown texture of the material. aiString unknown_filepath; if (material->GetTexture(aiTextureType_UNKNOWN, 0, &unknown_filepath) == AI_SUCCESS) { Log::info("Unknown texture name: " + std::string(directory.c_str()) + std::string(unknown_filepath.data)); std::string texture_filepath(normals_filepath.data); texture_filepath.insert(0, directory); texture_info.push_back({Texture::Type::MetallicRoughness, texture_filepath}); } } } if (scene->HasMeshes()) { // FIXME: Assumes the mesh is a single mesh and not a hierarchy Log::info("Scene: # meshes " + std::to_string(scene->mNumMeshes)); for (size_t i = 0; i < scene->mNumMeshes; i++) { auto mesh = scene->mMeshes[i]; Log::info("Loading mesh with name: " + std::string(mesh->mName.data)); for (size_t j = 0; j < mesh->mNumVertices; j++) { Vertex vertex; auto pos = mesh->mVertices[j]; vertex.position = {pos.x, pos.y, pos.z}; if (mesh->HasTextureCoords(0)) { auto tex_coord = mesh->mTextureCoords[0][j]; vertex.tex_coord = {tex_coord.x, -tex_coord.y}; // glTF (& .obj) has a flipped texture coordinate system compared to OpenGL } if (mesh->HasNormals()) { auto normal = mesh->mNormals[j]; vertex.normal = {normal.x, normal.y, normal.z}; } mesh_info.mesh.vertices.push_back(vertex); } for (size_t j = 0; j < mesh->mNumFaces; j++) { auto face = &mesh->mFaces[j]; if (face->mNumIndices != 3) { Log::warn("Not 3 vertices per face in model."); return {0, {}}; } for (size_t k = 0; k < 3; k++) { auto index = face->mIndices[k]; mesh_info.mesh.indices.push_back(index); } } } } // FIXME: Mesh id is worthless since it does not change or anything ... loaded_meshes.push_back(mesh_info.mesh); return {loaded_meshes.size() - 1, texture_info}; }
// Reloads the geometry. To keep some data constant, just specify nullptr. void Mesh::Reload(const GLfloat* positions, const GLfloat* colors, const GLfloat* normals, const GLfloat* texCoords ) { if (!mInitialized) { std::cerr << "ERROR The mesh must be initialized before creating buffer objects.\n"; return; } if (mStorageType == kTightlyPacked) { for (int i = 0; i < mNumVertices; i++) { float* position = PositionAt(i); float* texCoord = TexCoordAt(i); float* normal = NormalAt(i); float* color = ColorAt(i); if (positions) { memcpy(position, positions + 3*i, sizeof(GLfloat)*3); } if (colors && HasColors()) { memcpy(color, colors + 3*i, sizeof(GLfloat)*3); } if (normals && HasNormals()) { memcpy(normal, normals + 3*i, sizeof(GLfloat)*3); } if (texCoords && HasTexCoord()) { memcpy(texCoord, texCoords + 2*i, sizeof(GLfloat)*2); } } } else // Sub-buffered. { GLfloat* dest = mVertices; if (positions) { memcpy(dest, positions, 3*sizeof(GLfloat)*mNumVertices); dest += 3*mNumVertices; } if (colors && HasColors()) { memcpy(dest, colors, 3*sizeof(GLfloat)*mNumVertices); dest += 3*mNumVertices; } if (normals && HasNormals()) { memcpy(dest, normals, 3*sizeof(GLfloat)*mNumVertices); dest += 3*mNumVertices; } if (texCoords && HasTexCoord()) { memcpy(dest, texCoords, 2*sizeof(GLfloat)*mNumVertices); dest += 2*mNumVertices; } } Mesh::Upload(); }
MeshPtr AssimpMeshIO::load(const std::string &mesh_name, const std::string &file_name, unsigned int mesh_index, const BufferManagerPtr &buffer_manager, const VaoManagerPtr &vao_manager) { auto scene = importer->ReadFile(file_name, aiProcessPreset_TargetRealtime_MaxQuality | aiProcess_ConvertToLeftHanded); if (scene == nullptr) { throw clan::Exception(clan::string_format("Unable to locate (%1) for (%2)", file_name, mesh_name)); } if (scene->mNumMeshes <= mesh_index) throw clan::Exception(clan::string_format("The mesh index (%1) was out of bounds for mesh %2.", mesh_index, mesh_name)); auto scene_mesh = scene->mMeshes[mesh_index]; unsigned int stride = 0; std::vector<Core::VertexAttribute> interleaved_spec; Core::VaoLayout vao_layout; Core::RenderCommand render_command; // vectors to store bounds position and size in. glm::vec3 mesh_bounds_pos = glm::vec3(0); glm::vec3 mesh_bounds_size = glm::vec3(0); if (scene_mesh->HasPositions() && scene_mesh->mNumVertices > 0) { mesh_bounds_pos = glm::vec3(scene_mesh->mVertices[0].x, scene_mesh->mVertices[0].y, scene_mesh->mVertices[0].z); mesh_bounds_size = mesh_bounds_pos; } // // Set up vertices // // Set up the interleaved vertex attributes if (scene_mesh->HasPositions()) { stride += sizeof(glm::vec3); interleaved_spec.push_back(Core::VaoArg<glm::vec3>(Core::ShaderConstants::Position)); } if (scene_mesh->HasNormals()) { stride += sizeof(glm::vec3); interleaved_spec.push_back(Core::VaoArg<glm::vec3>(Core::ShaderConstants::Normal)); } if (scene_mesh->HasTextureCoords(0)) { stride += sizeof(glm::vec2); interleaved_spec.push_back(Core::VaoArg<glm::vec2>(Core::ShaderConstants::TexCoord)); } if (scene_mesh->HasTangentsAndBitangents()) { stride += 2*sizeof(glm::vec3); interleaved_spec.push_back(Core::VaoArg<glm::vec3>(Core::ShaderConstants::Tangent)); interleaved_spec.push_back(Core::VaoArg<glm::vec3>(Core::ShaderConstants::Bitangent)); } std::vector<float> vertices; vertices.reserve(scene_mesh->mNumVertices * stride / sizeof(float)); // This is how many floats we have for (unsigned int i = 0; i < scene_mesh->mNumVertices; i++) { // This may be made into a lambda for BuffeOperations::unsafe_upload if (scene_mesh->HasPositions()) { auto position = scene_mesh->mVertices[i]; vertices.push_back(position.x); vertices.push_back(position.y); vertices.push_back(position.z); // check for new min-max component in vertex, and update bounds. check_if_new_min_or_max_vertex(glm::vec3(position.x, position.y, position.z), mesh_bounds_pos, mesh_bounds_size); } if (scene_mesh->HasNormals()) { auto normal = scene_mesh->mNormals[i]; vertices.push_back(normal.x); vertices.push_back(normal.y); vertices.push_back(normal.z); } if (scene_mesh->HasTextureCoords(0)) { auto texcoord = scene_mesh->mTextureCoords[0][i]; vertices.push_back(texcoord.x); vertices.push_back(texcoord.y); } if (scene_mesh->HasTangentsAndBitangents()) { auto tangent = scene_mesh->mTangents[i]; vertices.push_back(tangent.x); vertices.push_back(tangent.y); vertices.push_back(tangent.z); auto bitangent = scene_mesh->mBitangents[i]; vertices.push_back(bitangent.x); vertices.push_back(bitangent.y); vertices.push_back(bitangent.z); } } auto vertex_allocation = buffer_manager->allocate(vertices.size()*stride, stride); vertex_allocation.upload(vertices); vao_layout .for_buffer(vertex_allocation) .use_as(GL_ARRAY_BUFFER) .bind_interleaved(0, 0, interleaved_spec); render_command.set_draw_mode(GL_TRIANGLES); render_command.set_vertices(vertex_allocation, scene_mesh->mNumVertices, stride); // // Set up indices // if (scene_mesh->HasFaces()) { std::vector<unsigned int> indices; for (unsigned int i = 0; i < scene_mesh->mNumFaces; i++) { auto face = scene_mesh->mFaces[i]; for (unsigned int j = 0; j < face.mNumIndices; j++) { indices.push_back(face.mIndices[j]); } } auto index_allocation = buffer_manager->allocate_and_upload(indices); vao_layout .for_buffer(index_allocation) .use_as(GL_ELEMENT_ARRAY_BUFFER); render_command.set_indices(index_allocation, indices); } // Ensure that mesh_bounds_size is the distance from mesh_bounds_pos to upper-right-back-corner of the bounds mesh_bounds_size = glm::vec3( mesh_bounds_size.x - mesh_bounds_pos.x, mesh_bounds_size.y - mesh_bounds_pos.y, mesh_bounds_size.z - mesh_bounds_pos.z ); return std::make_shared<Mesh>(render_command, vao_layout, vao_manager, mesh_name, mesh_bounds_pos, mesh_bounds_size); }