diff --git a/src/engine.cpp b/src/engine.cpp index 00e0c7e..437aa13 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -142,9 +142,9 @@ void Engine::setup() world_ = std::make_unique(); Logger::info("World created"); - // Create camera at position (0, 1, 3) with -20 degrees X rotation + // Create camera camera_ = std::make_shared(); - camera_->set_position(glm::vec3(0.0f, 1.0f, 3.0f)); + camera_->set_position(glm::vec3(0.0f, 1.0f, 10.0f)); camera_->set_rotation(glm::vec3(glm::radians(-20.0f), 0.0f, 0.0f)); camera_->set_projection( glm::perspective(glm::radians(45.0f), diff --git a/src/mesh.cpp b/src/mesh.cpp index f130b28..0ef345d 100644 --- a/src/mesh.cpp +++ b/src/mesh.cpp @@ -14,6 +14,125 @@ #include "texture.h" #include "vertex.h" +// Helper structures and functions for OBJ loading +namespace { + +struct LoadContext { + std::string base_dir; + std::unordered_map> texture_cache; + const std::vector* materials; + const std::shared_ptr& shader; +}; + +Vertex create_vertex(const tinyobj::attrib_t& attrib, + const tinyobj::index_t& idx) +{ + Vertex vertex; + + // Position + vertex.position.x = attrib.vertices[3 * idx.vertex_index + 0]; + vertex.position.y = attrib.vertices[3 * idx.vertex_index + 1]; + vertex.position.z = attrib.vertices[3 * idx.vertex_index + 2]; + + // Color (default to white) + vertex.color = glm::vec3(1.0f, 1.0f, 1.0f); + + // Texture coordinates + if (idx.texcoord_index >= 0) { + vertex.tex_coord.x = + attrib.texcoords[2 * idx.texcoord_index + 0]; + vertex.tex_coord.y = 1.0f + - attrib.texcoords[2 * idx.texcoord_index + 1]; // Flip Y + } + + return vertex; +} + +void group_vertices_by_material( + const tinyobj::shape_t& shape, const tinyobj::attrib_t& attrib, + std::unordered_map>& out_vertices, + std::unordered_map>& out_indices) +{ + size_t index_offset = 0; + for (size_t f = 0; f < shape.mesh.num_face_vertices.size(); ++f) { + int fv = shape.mesh.num_face_vertices[f]; + int material_id = shape.mesh.material_ids[f]; + + for (size_t v = 0; v < static_cast(fv); ++v) { + const tinyobj::index_t& idx = + shape.mesh.indices[index_offset + v]; + + Vertex vertex = create_vertex(attrib, idx); + out_vertices[material_id].push_back(vertex); + out_indices[material_id].push_back( + static_cast( + out_vertices[material_id].size() - 1)); + } + + index_offset += fv; + } +} + +std::shared_ptr load_material_texture(int material_id, + LoadContext& context) +{ + if (material_id < 0 + || material_id >= static_cast(context.materials->size())) { + return nullptr; + } + + const auto& mat = (*context.materials)[material_id]; + std::string texture_name = mat.diffuse_texname; + + // Try ambient texture if diffuse is not available + if (texture_name.empty()) { + texture_name = mat.ambient_texname; + } + + if (texture_name.empty()) { + return nullptr; + } + + // Check cache first + auto it = context.texture_cache.find(texture_name); + if (it != context.texture_cache.end()) { + return it->second; + } + + // Load texture + std::string texture_path = context.base_dir + texture_name; + auto texture = Texture::load(texture_path.c_str()); + + if (texture) { + context.texture_cache[texture_name] = texture; + Logger::info(sstr("Loaded texture: ", texture_path)); + } else { + Logger::warn(sstr("Failed to load texture: ", texture_path)); + } + + return texture; +} + +void create_buffers_for_materials( + const std::unordered_map>& material_vertices, + const std::unordered_map>& material_indices, + LoadContext& context, std::shared_ptr& mesh) +{ + for (const auto& [material_id, vertices] : material_vertices) { + const auto& indices = material_indices.at(material_id); + + auto buffer = std::make_shared(vertices, indices); + auto texture = load_material_texture(material_id, context); + auto material_shader = + context.shader ? context.shader : state::default_shader; + + Material mat(texture, material_shader); + mesh->add_buffer(buffer, mat); + } +} + +} // namespace + void Mesh::add_buffer(const std::shared_ptr& buffer, const Material& material) { @@ -26,152 +145,44 @@ std::shared_ptr Mesh::load(const char* filename, { Logger::info(sstr("Loading mesh from: ", filename)); - // Load the OBJ file + // Load OBJ file tinyobj::attrib_t attrib; std::vector shapes; std::vector materials; - std::string warn_err; + std::string err; - // Get the directory of the OBJ file for loading materials std::filesystem::path obj_path(filename); std::string base_dir = obj_path.parent_path().string(); if (!base_dir.empty()) { base_dir += "/"; } - if (!tinyobj::LoadObj(&attrib, &shapes, &materials, &warn_err, - filename, base_dir.c_str())) { + if (!tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename, + base_dir.c_str())) { Logger::error( - sstr("Failed to load OBJ file: ", filename, " - ", - warn_err)); + sstr("Failed to load OBJ file: ", filename, " - ", err)); return nullptr; } - if (!warn_err.empty()) { - Logger::warn(sstr("TinyObjLoader warning: ", warn_err)); + if (!err.empty()) { + Logger::warn(sstr("TinyObjLoader warning: ", err)); } - // Create the mesh - auto mesh = std::make_shared(); - - // Cache loaded textures to avoid loading the same texture multiple - // times - std::unordered_map> texture_cache; + // Setup load context + LoadContext context{base_dir, {}, &materials, shader}; // Process each shape + auto mesh = std::make_shared(); for (const auto& shape : shapes) { Logger::info(sstr("Processing shape: ", shape.name)); - // Group faces by material std::unordered_map> material_vertices; std::unordered_map> material_indices; - // Process all faces in this shape - size_t index_offset = 0; - for (size_t f = 0; f < shape.mesh.num_face_vertices.size(); - f++) { - int fv = shape.mesh.num_face_vertices[f]; - int material_id = shape.mesh.material_ids[f]; - - // Process each vertex in the face - for (size_t v = 0; v < static_cast(fv); v++) { - tinyobj::index_t idx = - shape.mesh.indices[index_offset + v]; - - Vertex vertex; - - // Position - vertex.position.x = - attrib.vertices[3 * idx.vertex_index + 0]; - vertex.position.y = - attrib.vertices[3 * idx.vertex_index + 1]; - vertex.position.z = - attrib.vertices[3 * idx.vertex_index + 2]; - - // Color (default to white) - vertex.color = glm::vec3(1.0f, 1.0f, 1.0f); - - // Texture coordinates - if (idx.texcoord_index >= 0) { - vertex.tex_coord.x = - attrib.texcoords - [2 * idx.texcoord_index + 0]; - vertex.tex_coord.y = 1.0f - - attrib.texcoords - [2 * idx.texcoord_index - + 1]; // Flip Y - } - - material_vertices[material_id].push_back( - vertex); - material_indices[material_id].push_back( - static_cast( - material_vertices[material_id].size() - - 1)); - } - - index_offset += fv; - } - - // Create buffers for each material - for (const auto& [material_id, vertices] : material_vertices) { - const auto& indices = material_indices[material_id]; - - // Create buffer - auto buffer = - std::make_shared(vertices, indices); - - // Create material - std::shared_ptr texture = nullptr; - - // Load texture if material has one - if (material_id >= 0 - && material_id - < static_cast(materials.size())) { - const auto& mat = materials[material_id]; - std::string texture_name = mat.diffuse_texname; - - // Try ambient texture if diffuse is not - // available - if (texture_name.empty()) { - texture_name = mat.ambient_texname; - } - - if (!texture_name.empty()) { - // Check cache first - auto it = - texture_cache.find(texture_name); - if (it != texture_cache.end()) { - texture = it->second; - } else { - // Load texture - std::string texture_path = - base_dir + texture_name; - texture = Texture::load( - texture_path.c_str()); - if (texture) { - texture_cache - [texture_name] = - texture; - Logger::info(sstr( - "Loaded texture: ", - texture_path)); - } else { - Logger::warn(sstr( - "Failed to load texture: ", - texture_path)); - } - } - } - } - - // Use provided shader or default shader - auto material_shader = - shader ? shader : state::default_shader; - Material mat(texture, material_shader); - - mesh->add_buffer(buffer, mat); - } + group_vertices_by_material(shape, attrib, material_vertices, + material_indices); + create_buffers_for_materials(material_vertices, + material_indices, context, mesh); } Logger::info(sstr("Mesh loaded successfully with ", mesh->num_buffers(), diff --git a/src/mesh.h b/src/mesh.h index aabe4d7..3306934 100644 --- a/src/mesh.h +++ b/src/mesh.h @@ -14,9 +14,9 @@ public: Mesh() = default; ~Mesh() = default; - static std::shared_ptr load( - const char* filename, - const std::shared_ptr& shader = nullptr); + static std::shared_ptr + load(const char* filename, + const std::shared_ptr& shader = nullptr); void add_buffer(const std::shared_ptr& buffer, const Material& material);