#include "mesh.h" #include #include #include #include "../lib/tiny_obj_loader.h" #include "buffer.h" #include "logger.h" #include "material.h" #include "shader.h" #include "state.h" #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 if (idx.vertex_index >= 0) { 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 vertex.tex_coord = glm::vec2(0.0f, 0.0f); if (idx.texcoord_index >= 0) { vertex.tex_coord.x = attrib.texcoords[2 * idx.texcoord_index + 0]; vertex.tex_coord.y = attrib.texcoords[2 * idx.texcoord_index + 1]; } return vertex; } void process_shape(const tinyobj::shape_t& shape, const tinyobj::attrib_t& attrib, std::vector& out_vertices, std::vector& out_indices) { // Process all indices - create one vertex per index for (size_t i = 0; i < shape.mesh.indices.size(); ++i) { const tinyobj::index_t& idx = shape.mesh.indices[i]; Vertex vertex = create_vertex(attrib, idx); out_vertices.push_back(vertex); out_indices.push_back( static_cast(out_vertices.size() - 1)); } } 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_buffer_for_shape(const tinyobj::shape_t& shape, const std::vector& vertices, const std::vector& indices, LoadContext& context, std::shared_ptr& mesh) { auto buffer = std::make_shared(vertices, indices); // Use first material ID from the shape int material_id = -1; if (!shape.mesh.material_ids.empty() && shape.mesh.material_ids[0] >= 0) { material_id = shape.mesh.material_ids[0]; } 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) { buffers_.push_back(buffer); materials_.push_back(material); } std::shared_ptr Mesh::load(const char* filename, const std::shared_ptr& shader) { Logger::info(sstr("Loading mesh from: ", filename)); // Load OBJ file tinyobj::attrib_t attrib; std::vector shapes; std::vector materials; std::string err; 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, &err, filename, base_dir.c_str())) { Logger::error( sstr("Failed to load OBJ file: ", filename, " - ", err)); return nullptr; } if (!err.empty()) { Logger::warn(sstr("TinyObjLoader warning: ", err)); } // 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)); std::vector vertices; std::vector indices; process_shape(shape, attrib, vertices, indices); create_buffer_for_shape(shape, vertices, indices, context, mesh); } Logger::info(sstr("Mesh loaded successfully with ", mesh->num_buffers(), " buffers")); return mesh; } void Mesh::draw() { // Draw each buffer with its material for (size_t i = 0; i < buffers_.size(); ++i) { materials_[i].prepare(); buffers_[i]->draw(*materials_[i].shader()); } }