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 now = glfwGetTime();
|
||||||
const double delta_time = now - last_update_time_;
|
const double delta_time = now - last_update_time_;
|
||||||
|
|
||||||
process_input();
|
process_input(delta_time);
|
||||||
update(delta_time);
|
update(delta_time);
|
||||||
|
|
||||||
if (now - last_frame_time_ >= fps_limit) {
|
if (now - last_frame_time_ >= fps_limit) {
|
||||||
@@ -156,78 +156,27 @@ void Engine::setup()
|
|||||||
world_->add_entity(camera_);
|
world_->add_entity(camera_);
|
||||||
Logger::info("Camera created and added to world");
|
Logger::info("Camera created and added to world");
|
||||||
|
|
||||||
// Load textures
|
// Load the box_stack model
|
||||||
auto top_texture = Texture::load("data/textures/top.png");
|
auto box_stack_mesh = Mesh::load("data/models/box_stack.obj");
|
||||||
auto front_texture = Texture::load("data/textures/front.png");
|
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
|
// Load the gunslinger model
|
||||||
mesh_ = std::make_shared<Mesh>();
|
auto gunslinger_mesh = Mesh::load("data/models/gunslinger.obj");
|
||||||
|
if (gunslinger_mesh) {
|
||||||
// Buffer 1: Top and bottom faces
|
auto gunslinger_model =
|
||||||
std::vector<Vertex> top_bottom_vertices = {
|
std::make_shared<Model>(gunslinger_mesh);
|
||||||
// Top face (y = 0.5)
|
gunslinger_model->set_position(glm::vec3(2.0f, 0.0f, 0.0f));
|
||||||
{{-0.5f, 0.5f, -0.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}},
|
gunslinger_model->set_scale(glm::vec3(0.5f, 0.5f, 0.5f));
|
||||||
{{0.5f, 0.5f, -0.5f}, {1.0f, 1.0f, 1.0f}, {1.0f, 0.0f}},
|
models_.push_back(gunslinger_model);
|
||||||
{{0.5f, 0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}, {1.0f, 1.0f}},
|
world_->add_entity(gunslinger_model);
|
||||||
{{-0.5f, 0.5f, 0.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f}},
|
Logger::info("Gunslinger model loaded and added to world");
|
||||||
// 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);
|
|
||||||
|
|
||||||
Logger::info("Scene setup complete");
|
Logger::info("Scene setup complete");
|
||||||
}
|
}
|
||||||
@@ -238,22 +187,44 @@ void Engine::start()
|
|||||||
// Can be used for initialization that needs the scene to be ready
|
// 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();
|
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)
|
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
|
// Update world
|
||||||
world_->update(static_cast<float>(delta_time));
|
world_->update(static_cast<float>(delta_time));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ private:
|
|||||||
// Lifecycle methods
|
// Lifecycle methods
|
||||||
void setup();
|
void setup();
|
||||||
void start();
|
void start();
|
||||||
void process_input();
|
void process_input(const double delta_time);
|
||||||
void update(const double delta_time);
|
void update(const double delta_time);
|
||||||
void render();
|
void render();
|
||||||
|
|
||||||
|
|||||||
171
src/mesh.cpp
171
src/mesh.cpp
@@ -1,15 +1,184 @@
|
|||||||
#include "mesh.h"
|
#include "mesh.h"
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#include "../lib/tiny_obj_loader.h"
|
||||||
|
|
||||||
#include "buffer.h"
|
#include "buffer.h"
|
||||||
|
#include "logger.h"
|
||||||
#include "material.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,
|
void Mesh::add_buffer(const std::shared_ptr<Buffer>& buffer,
|
||||||
const Material& material)
|
const Material& material)
|
||||||
{
|
{
|
||||||
buffers_.push_back(buffer);
|
buffers_.push_back(buffer);
|
||||||
materials_.push_back(material);
|
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()
|
void Mesh::draw()
|
||||||
{
|
{
|
||||||
// Draw each buffer with its material
|
// Draw each buffer with its material
|
||||||
|
|||||||
@@ -7,12 +7,17 @@
|
|||||||
#include "material.h"
|
#include "material.h"
|
||||||
|
|
||||||
class Buffer;
|
class Buffer;
|
||||||
|
class Shader;
|
||||||
|
|
||||||
class Mesh {
|
class Mesh {
|
||||||
public:
|
public:
|
||||||
Mesh() = default;
|
Mesh() = default;
|
||||||
~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,
|
void add_buffer(const std::shared_ptr<Buffer>& buffer,
|
||||||
const Material& material);
|
const Material& material);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user