.. _program_listing_file_Src_GraphicsEngineVulkan_scene_ObjLoader.cpp: Program Listing for File ObjLoader.cpp ====================================== |exhale_lsh| :ref:`Return to documentation for file ` (``Src/GraphicsEngineVulkan/scene/ObjLoader.cpp``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp #include "ObjLoader.hpp" #define TINYOBJLOADER_IMPLEMENTATION #include #include "util/File.hpp" #include #include using namespace Kataglyphis; ObjLoader::ObjLoader(VulkanDevice *device, VkQueue transfer_queue, VkCommandPool command_pool) { this->device = device; this->transfer_queue = transfer_queue; this->command_pool = command_pool; } std::shared_ptr ObjLoader::loadModel(const std::string &modelFile) { // the model we want to load std::shared_ptr new_model = std::make_shared(device); // first load txtures from model std::vector textureNames = loadTexturesAndMaterials(modelFile); std::vector matToTex(textureNames.size()); // now that we have the names lets create the vulkan side of textures for (size_t i = 0; i < textureNames.size(); i++) { // If material had no texture, set '0' to indicate no texture, texture 0 // will be reserved for a default texture if (!textureNames[i].empty()) { // Otherwise, create texture and set value to index of new texture Texture texture; texture.createFromFile(device, command_pool, textureNames[i]); new_model->addTexture(texture); matToTex[i] = new_model->getTextureCount(); } else { matToTex[i] = 0; } } loadVertices(modelFile); new_model->add_new_mesh(device, transfer_queue, command_pool, vertices, indices, materialIndex, this->materials); return new_model; } std::vector ObjLoader::loadTexturesAndMaterials(const std::string &modelFile) { tinyobj::ObjReaderConfig reader_config; tinyobj::ObjReader reader; if (!reader.ParseFromFile(modelFile, reader_config)) { if (!reader.Error().empty()) { std::cerr << "TinyObjReader: " << reader.Error(); } exit(EXIT_FAILURE); } if (!reader.Warning().empty()) { std::cout << "TinyObjReader: " << reader.Warning(); } auto &tol_materials = reader.GetMaterials(); textures.reserve(tol_materials.size()); int texture_id = 0; // we now iterate over all materials to get diffuse textures for (size_t i = 0; i < tol_materials.size(); i++) { const tinyobj::material_t *mp = &tol_materials[i]; ObjMaterial material{}; material.ambient = glm::vec3(mp->ambient[0], mp->ambient[1], mp->ambient[2]); material.diffuse = glm::vec3(mp->diffuse[0], mp->diffuse[1], mp->diffuse[2]); material.specular = glm::vec3(mp->specular[0], mp->specular[1], mp->specular[2]); material.emission = glm::vec3(mp->emission[0], mp->emission[1], mp->emission[2]); material.transmittance = glm::vec3(mp->transmittance[0], mp->transmittance[1], mp->transmittance[2]); material.dissolve = mp->dissolve; material.ior = mp->ior; material.shininess = mp->shininess; material.illum = mp->illum; if (mp->diffuse_texname.length() > 0) { std::string relative_texture_filename = mp->diffuse_texname; File model_file(modelFile); std::string texture_filename = model_file.getBaseDir() + "/textures/" + relative_texture_filename; textures.push_back(texture_filename); material.textureID = texture_id; texture_id++; } else { material.textureID = 0; textures.push_back(""); } materials.push_back(material); } // for the case no .mtl file is given place some random standard material ... if (tol_materials.empty()) { materials.emplace_back(ObjMaterial()); } return textures; } void ObjLoader::loadVertices(const std::string &fileName) { tinyobj::ObjReaderConfig reader_config; // reader_config.mtl_search_path = ""; // Path to material files tinyobj::ObjReader reader; if (!reader.ParseFromFile(fileName, reader_config)) { if (!reader.Error().empty()) { std::cerr << "TinyObjReader: " << reader.Error(); } exit(EXIT_FAILURE); } if (!reader.Warning().empty()) { std::cout << "TinyObjReader: " << reader.Warning(); } auto &attrib = reader.GetAttrib(); auto &shapes = reader.GetShapes(); auto &materials = reader.GetMaterials(); std::unordered_map vertices_map{}; // Loop over shapes for (size_t s = 0; s < shapes.size(); s++) { // prepare for enlargement vertices.reserve(shapes[s].mesh.indices.size() + vertices.size()); indices.reserve(shapes[s].mesh.indices.size() + indices.size()); // Loop over faces(polygon) size_t index_offset = 0; for (size_t f = 0; f < shapes[s].mesh.num_face_vertices.size(); f++) { size_t fv = size_t(shapes[s].mesh.num_face_vertices[f]); // Loop over vertices in the face. for (size_t v = 0; v < fv; v++) { // access to vertex tinyobj::index_t idx = shapes[s].mesh.indices[index_offset + v]; tinyobj::real_t vx = attrib.vertices[3 * size_t(idx.vertex_index) + 0]; tinyobj::real_t vy = attrib.vertices[3 * size_t(idx.vertex_index) + 1]; tinyobj::real_t vz = attrib.vertices[3 * size_t(idx.vertex_index) + 2]; glm::vec3 pos = { vx, vy, vz }; glm::vec3 normals(0.0f); // Check if `normal_index` is zero or positive. negative = no normal // data if (idx.normal_index >= 0 && !attrib.normals.empty()) { tinyobj::real_t nx = attrib.normals[3 * size_t(idx.normal_index) + 0]; tinyobj::real_t ny = attrib.normals[3 * size_t(idx.normal_index) + 1]; tinyobj::real_t nz = attrib.normals[3 * size_t(idx.normal_index) + 2]; normals = glm::vec3(nx, ny, nz); } glm::vec3 color(-1.f); if (!attrib.colors.empty()) { tinyobj::real_t red = attrib.colors[3 * size_t(idx.vertex_index) + 0]; tinyobj::real_t green = attrib.colors[3 * size_t(idx.vertex_index) + 1]; tinyobj::real_t blue = attrib.colors[3 * size_t(idx.vertex_index) + 2]; color = glm::vec3(red, green, blue); } glm::vec2 tex_coords(0.0f); // Check if `texcoord_index` is zero or positive. negative = no texcoord // data if (idx.texcoord_index >= 0 && !attrib.texcoords.empty()) { tinyobj::real_t tx = attrib.texcoords[2 * size_t(idx.texcoord_index) + 0]; // flip y coordinate !! tinyobj::real_t ty = 1.f - attrib.texcoords[2 * size_t(idx.texcoord_index) + 1]; tex_coords = glm::vec2(tx, ty); } Vertex vert{ pos, normals, color, tex_coords }; if (vertices_map.count(vert) == 0) { vertices_map[vert] = vertices.size(); vertices.push_back(vert); } indices.push_back(vertices_map[vert]); } index_offset += fv; // per-face material; face usually is triangle // matToTex[shapes[s].mesh.material_ids[f]] materialIndex.push_back(shapes[s].mesh.material_ids[f]); } } // precompute normals if no provided if (attrib.normals.empty()) { for (size_t i = 0; i < indices.size(); i += 3) { Vertex &v0 = vertices[indices[i + 0]]; Vertex &v1 = vertices[indices[i + 1]]; Vertex &v2 = vertices[indices[i + 2]]; glm::vec3 n = glm::normalize(glm::cross((v1.pos - v0.pos), (v2.pos - v0.pos))); v0.normal = n; v1.normal = n; v2.normal = n; } } }