feat: load obj
This commit is contained in:
		
							
								
								
									
										135
									
								
								src/engine.cpp
									
									
									
									
									
								
							
							
						
						
									
										135
									
								
								src/engine.cpp
									
									
									
									
									
								
							| @@ -111,7 +111,7 @@ void Engine::run() | ||||
| 		const double now	= glfwGetTime(); | ||||
| 		const double delta_time = now - last_update_time_; | ||||
|  | ||||
| 		process_input(); | ||||
| 		process_input(delta_time); | ||||
| 		update(delta_time); | ||||
|  | ||||
| 		if (now - last_frame_time_ >= fps_limit) { | ||||
| @@ -156,78 +156,27 @@ void Engine::setup() | ||||
| 	world_->add_entity(camera_); | ||||
| 	Logger::info("Camera created and added to world"); | ||||
|  | ||||
| 	// Load textures | ||||
| 	auto top_texture   = Texture::load("data/textures/top.png"); | ||||
| 	auto front_texture = Texture::load("data/textures/front.png"); | ||||
| 	// Load the box_stack model | ||||
| 	auto box_stack_mesh = Mesh::load("data/models/box_stack.obj"); | ||||
| 	if (box_stack_mesh) { | ||||
| 		auto box_stack_model = std::make_shared<Model>(box_stack_mesh); | ||||
| 		box_stack_model->set_position(glm::vec3(-2.0f, 0.0f, 0.0f)); | ||||
| 		models_.push_back(box_stack_model); | ||||
| 		world_->add_entity(box_stack_model); | ||||
| 		Logger::info("Box stack model loaded and added to world"); | ||||
| 	} | ||||
|  | ||||
| 	// Create cube mesh with two buffers | ||||
| 	mesh_ = std::make_shared<Mesh>(); | ||||
|  | ||||
| 	// Buffer 1: Top and bottom faces | ||||
| 	std::vector<Vertex> top_bottom_vertices = { | ||||
| 	    // Top face (y = 0.5) | ||||
| 	    {{-0.5f, 0.5f, -0.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}}, | ||||
| 	    {{0.5f, 0.5f, -0.5f}, {1.0f, 1.0f, 1.0f}, {1.0f, 0.0f}}, | ||||
| 	    {{0.5f, 0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}, {1.0f, 1.0f}}, | ||||
| 	    {{-0.5f, 0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}}, | ||||
| 	    // Bottom face (y = -0.5) | ||||
| 	    {{-0.5f, -0.5f, -0.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}}, | ||||
| 	    {{0.5f, -0.5f, -0.5f}, {1.0f, 1.0f, 1.0f}, {1.0f, 0.0f}}, | ||||
| 	    {{0.5f, -0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}, {1.0f, 1.0f}}, | ||||
| 	    {{-0.5f, -0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}}}; | ||||
| 	std::vector<uint16_t> top_bottom_indices = {// Top face | ||||
| 						    0, 1, 2, 0, 2, 3, | ||||
| 						    // Bottom face | ||||
| 						    4, 6, 5, 4, 7, 6}; | ||||
|  | ||||
| 	auto top_bottom_buffer = | ||||
| 	    std::make_shared<Buffer>(top_bottom_vertices, top_bottom_indices); | ||||
| 	Material top_material(top_texture); | ||||
| 	mesh_->add_buffer(top_bottom_buffer, top_material); | ||||
|  | ||||
| 	// Buffer 2: Front, back, left, right faces | ||||
| 	std::vector<Vertex> side_vertices = { | ||||
| 	    // Front face (z = 0.5) | ||||
| 	    {{-0.5f, -0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}}, | ||||
| 	    {{0.5f, -0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}, {1.0f, 0.0f}}, | ||||
| 	    {{0.5f, 0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}, {1.0f, 1.0f}}, | ||||
| 	    {{-0.5f, 0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}}, | ||||
| 	    // Back face (z = -0.5) | ||||
| 	    {{-0.5f, -0.5f, -0.5f}, {1.0f, 1.0f, 1.0f}, {1.0f, 0.0f}}, | ||||
| 	    {{-0.5f, 0.5f, -0.5f}, {1.0f, 1.0f, 1.0f}, {1.0f, 1.0f}}, | ||||
| 	    {{0.5f, 0.5f, -0.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}}, | ||||
| 	    {{0.5f, -0.5f, -0.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}}, | ||||
| 	    // Left face (x = -0.5) | ||||
| 	    {{-0.5f, -0.5f, -0.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}}, | ||||
| 	    {{-0.5f, -0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}, {1.0f, 0.0f}}, | ||||
| 	    {{-0.5f, 0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}, {1.0f, 1.0f}}, | ||||
| 	    {{-0.5f, 0.5f, -0.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}}, | ||||
| 	    // Right face (x = 0.5) | ||||
| 	    {{0.5f, -0.5f, -0.5f}, {1.0f, 1.0f, 1.0f}, {1.0f, 0.0f}}, | ||||
| 	    {{0.5f, 0.5f, -0.5f}, {1.0f, 1.0f, 1.0f}, {1.0f, 1.0f}}, | ||||
| 	    {{0.5f, 0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}}, | ||||
| 	    {{0.5f, -0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}}}; | ||||
| 	std::vector<uint16_t> side_indices = {// Front face | ||||
| 					      0, 1, 2, 0, 2, 3, | ||||
| 					      // Back face | ||||
| 					      4, 5, 6, 4, 6, 7, | ||||
| 					      // Left face | ||||
| 					      8, 9, 10, 8, 10, 11, | ||||
| 					      // Right face | ||||
| 					      12, 13, 14, 12, 14, 15}; | ||||
|  | ||||
| 	auto side_buffer = | ||||
| 	    std::make_shared<Buffer>(side_vertices, side_indices); | ||||
| 	Material side_material(front_texture); | ||||
| 	mesh_->add_buffer(side_buffer, side_material); | ||||
|  | ||||
| 	Logger::info("Cube mesh created with two buffers"); | ||||
|  | ||||
| 	// Create model at origin | ||||
| 	auto model = std::make_shared<Model>(mesh_); | ||||
| 	model->set_position(glm::vec3(0.0f, 0.0f, 0.0f)); | ||||
| 	models_.push_back(model); | ||||
| 	world_->add_entity(model); | ||||
| 	// Load the gunslinger model | ||||
| 	auto gunslinger_mesh = Mesh::load("data/models/gunslinger.obj"); | ||||
| 	if (gunslinger_mesh) { | ||||
| 		auto gunslinger_model = | ||||
| 		    std::make_shared<Model>(gunslinger_mesh); | ||||
| 		gunslinger_model->set_position(glm::vec3(2.0f, 0.0f, 0.0f)); | ||||
| 		gunslinger_model->set_scale(glm::vec3(0.5f, 0.5f, 0.5f)); | ||||
| 		models_.push_back(gunslinger_model); | ||||
| 		world_->add_entity(gunslinger_model); | ||||
| 		Logger::info("Gunslinger model loaded and added to world"); | ||||
| 	} | ||||
|  | ||||
| 	Logger::info("Scene setup complete"); | ||||
| } | ||||
| @@ -238,22 +187,44 @@ void Engine::start() | ||||
| 	// Can be used for initialization that needs the scene to be ready | ||||
| } | ||||
|  | ||||
| void Engine::process_input() | ||||
| void Engine::process_input(const double delta_time) | ||||
| { | ||||
| 	glfwPollEvents(); | ||||
|  | ||||
| 	if (!camera_) { | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	constexpr float camera_speed = 7.5f; | ||||
|  | ||||
| 	glm::vec3 forward = glm::vec3(0.0f, 0.0f, -1.0f); | ||||
| 	glm::vec3 right	  = glm::vec3(1.0f, 0.0f, 0.0f); | ||||
|  | ||||
| 	glm::mat4 rotation = glm::mat4(1.0f); | ||||
| 	rotation	   = glm::rotate(rotation, camera_->rotation().y, | ||||
| 					 glm::vec3(0.0f, 1.0f, 0.0f)); | ||||
| 	rotation	   = glm::rotate(rotation, camera_->rotation().x, | ||||
| 					 glm::vec3(1.0f, 0.0f, 0.0f)); | ||||
| 	forward		   = glm::vec3(rotation * glm::vec4(forward, 0.0f)); | ||||
| 	right		   = glm::vec3(rotation * glm::vec4(right, 0.0f)); | ||||
|  | ||||
| 	const float movement = camera_speed * static_cast<float>(delta_time); | ||||
| 	if (glfwGetKey(window_, GLFW_KEY_UP) == GLFW_PRESS) { | ||||
| 		camera_->set_position(camera_->position() + forward * movement); | ||||
| 	} | ||||
| 	if (glfwGetKey(window_, GLFW_KEY_DOWN) == GLFW_PRESS) { | ||||
| 		camera_->set_position(camera_->position() - forward * movement); | ||||
| 	} | ||||
| 	if (glfwGetKey(window_, GLFW_KEY_LEFT) == GLFW_PRESS) { | ||||
| 		camera_->set_position(camera_->position() - right * movement); | ||||
| 	} | ||||
| 	if (glfwGetKey(window_, GLFW_KEY_RIGHT) == GLFW_PRESS) { | ||||
| 		camera_->set_position(camera_->position() + right * movement); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void Engine::update(const double delta_time) | ||||
| { | ||||
| 	// Update angle for rotation | ||||
| 	angle_ += 32.0 * delta_time; | ||||
|  | ||||
| 	// Update rotation for all models | ||||
| 	for (auto& model : models_) { | ||||
| 		model->set_rotation(glm::vec3( | ||||
| 		    0.0f, glm::radians(static_cast<float>(angle_)), 0.0f)); | ||||
| 	} | ||||
|  | ||||
| 	// Update world | ||||
| 	world_->update(static_cast<float>(delta_time)); | ||||
| } | ||||
|   | ||||
| @@ -25,7 +25,7 @@ private: | ||||
| 	// Lifecycle methods | ||||
| 	void setup(); | ||||
| 	void start(); | ||||
| 	void process_input(); | ||||
| 	void process_input(const double delta_time); | ||||
| 	void update(const double delta_time); | ||||
| 	void render(); | ||||
|  | ||||
|   | ||||
							
								
								
									
										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 | ||||
|   | ||||
| @@ -7,12 +7,17 @@ | ||||
| #include "material.h" | ||||
|  | ||||
| class Buffer; | ||||
| class Shader; | ||||
|  | ||||
| class Mesh { | ||||
| public: | ||||
| 	Mesh()	= default; | ||||
| 	~Mesh() = default; | ||||
|  | ||||
| 	static std::shared_ptr<Mesh> load( | ||||
| 	    const char*			     filename, | ||||
| 	    const std::shared_ptr<Shader>& shader = nullptr); | ||||
|  | ||||
| 	void add_buffer(const std::shared_ptr<Buffer>& buffer, | ||||
| 			const Material&		       material); | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user