feat: load obj

This commit is contained in:
2025-10-13 19:47:21 +02:00
parent 95275f9183
commit d084ce8799
4 changed files with 229 additions and 84 deletions

View File

@@ -1,15 +1,184 @@
#include "mesh.h"
#include <filesystem>
#include <string>
#include <unordered_map>
#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"
void Mesh::add_buffer(const std::shared_ptr<Buffer>& buffer,
const Material& material)
const Material& material)
{
buffers_.push_back(buffer);
materials_.push_back(material);
}
std::shared_ptr<Mesh> Mesh::load(const char* filename,
const std::shared_ptr<Shader>& shader)
{
Logger::info(sstr("Loading mesh from: ", filename));
// Load the OBJ file
tinyobj::attrib_t attrib;
std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials;
std::string warn_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())) {
Logger::error(
sstr("Failed to load OBJ file: ", filename, " - ",
warn_err));
return nullptr;
}
if (!warn_err.empty()) {
Logger::warn(sstr("TinyObjLoader warning: ", warn_err));
}
// Create the mesh
auto mesh = std::make_shared<Mesh>();
// Cache loaded textures to avoid loading the same texture multiple
// times
std::unordered_map<std::string, std::shared_ptr<Texture>> texture_cache;
// Process each shape
for (const auto& shape : shapes) {
Logger::info(sstr("Processing shape: ", shape.name));
// Group faces by material
std::unordered_map<int, std::vector<Vertex>> material_vertices;
std::unordered_map<int, std::vector<uint16_t>> 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<size_t>(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<uint16_t>(
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<Buffer>(vertices, indices);
// Create material
std::shared_ptr<Texture> texture = nullptr;
// Load texture if material has one
if (material_id >= 0
&& material_id
< static_cast<int>(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);
}
}
Logger::info(sstr("Mesh loaded successfully with ", mesh->num_buffers(),
" buffers"));
return mesh;
}
void Mesh::draw()
{
// Draw each buffer with its material