feat: load obj
This commit is contained in:
		
							
								
								
									
										171
									
								
								src/mesh.cpp
									
									
									
									
									
								
							
							
						
						
									
										171
									
								
								src/mesh.cpp
									
									
									
									
									
								
							| @@ -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 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user