195 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			195 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #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"
 | |
| 
 | |
| // Helper structures and functions for OBJ loading
 | |
| namespace {
 | |
| 
 | |
| struct LoadContext {
 | |
| 	std::string						  base_dir;
 | |
| 	std::unordered_map<std::string, std::shared_ptr<Texture>> texture_cache;
 | |
| 	const std::vector<tinyobj::material_t>*			  materials;
 | |
| 	const std::shared_ptr<Shader>&				  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<Vertex>&	    out_vertices,
 | |
| 		   std::vector<uint16_t>&   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<uint16_t>(out_vertices.size() - 1));
 | |
| 	}
 | |
| }
 | |
| 
 | |
| std::shared_ptr<Texture> load_material_texture(int	    material_id,
 | |
| 					       LoadContext& context)
 | |
| {
 | |
| 	if (material_id < 0
 | |
| 	    || material_id >= static_cast<int>(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<Vertex>&	   vertices,
 | |
| 			     const std::vector<uint16_t>& indices,
 | |
| 			     LoadContext&		   context,
 | |
| 			     std::shared_ptr<Mesh>&	   mesh)
 | |
| {
 | |
| 	auto buffer = std::make_shared<Buffer>(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>& buffer,
 | |
| 		      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 OBJ file
 | |
| 	tinyobj::attrib_t		 attrib;
 | |
| 	std::vector<tinyobj::shape_t>	 shapes;
 | |
| 	std::vector<tinyobj::material_t> 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<Mesh>();
 | |
| 	for (const auto& shape : shapes) {
 | |
| 		Logger::info(sstr("Processing shape: ", shape.name));
 | |
| 
 | |
| 		std::vector<Vertex>   vertices;
 | |
| 		std::vector<uint16_t> 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());
 | |
| 	}
 | |
| }
 |