Refactor renderer and input handling; add OBJ loader and math utilities
- Updated renderer.h to replace Vec3 and Vec2 structs with Math namespace equivalents. - Introduced Texture struct to manage texture properties. - Modified Triangle struct to use Texture instead of GLuint for texture handling. - Removed deprecated matrix functions and replaced them with Math namespace methods. - Implemented InputManager class for better input handling, including key and mouse state tracking. - Added ObjLoader class to load OBJ files and associated textures, with MTL file parsing. - Created math utilities for fixed-point arithmetic and vector/matrix operations. - Added time management class for frame timing and delta time calculations.
This commit is contained in:
@@ -1,145 +0,0 @@
|
||||
#include "mesh.h"
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <filesystem>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
std::map<std::string, Material> load_mtl_materials(const std::string& mtl_path)
|
||||
{
|
||||
std::map<std::string, Material> materials;
|
||||
std::ifstream file(mtl_path);
|
||||
if (!file.is_open()) {
|
||||
std::cerr << "Warning: could not open MTL file: " << mtl_path << std::endl;
|
||||
return materials;
|
||||
}
|
||||
|
||||
std::string line, current_mtl;
|
||||
while (std::getline(file, line)) {
|
||||
std::istringstream ss(line);
|
||||
std::string keyword;
|
||||
ss >> keyword;
|
||||
|
||||
if (keyword == "newmtl") {
|
||||
ss >> current_mtl;
|
||||
materials[current_mtl] = Material{Color(255, 255, 255), ""}; // Default white, no texture
|
||||
} else if (keyword == "Kd") {
|
||||
float r, g, b;
|
||||
ss >> r >> g >> b;
|
||||
if (materials.count(current_mtl)) {
|
||||
materials[current_mtl].color = Color(
|
||||
static_cast<uint8_t>(r * 255),
|
||||
static_cast<uint8_t>(g * 255),
|
||||
static_cast<uint8_t>(b * 255)
|
||||
);
|
||||
}
|
||||
} else if (keyword == "map_Kd") {
|
||||
std::string texture_path;
|
||||
ss >> texture_path;
|
||||
if (materials.count(current_mtl)) {
|
||||
materials[current_mtl].texture_path = texture_path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return materials;
|
||||
}
|
||||
|
||||
std::vector<Triangle> load_obj(const std::string& obj_path, Renderer* renderer)
|
||||
{
|
||||
std::ifstream file(obj_path);
|
||||
if (!file.is_open()) {
|
||||
throw std::runtime_error("Failed to open OBJ file: " + obj_path);
|
||||
}
|
||||
|
||||
std::vector<Vec3> positions;
|
||||
std::vector<Vec2> texcoords;
|
||||
std::map<std::string, Material> materials;
|
||||
std::map<std::string, GLuint> texture_map;
|
||||
std::vector<Triangle> triangles;
|
||||
|
||||
std::string current_material = "";
|
||||
fs::path base_path = fs::path(obj_path).parent_path();
|
||||
|
||||
std::string line;
|
||||
while (std::getline(file, line)) {
|
||||
std::istringstream ss(line);
|
||||
std::string keyword;
|
||||
ss >> keyword;
|
||||
|
||||
if (keyword == "v") {
|
||||
float x, y, z;
|
||||
ss >> x >> y >> z;
|
||||
positions.emplace_back((int)(x * 4096), (int)(y * 4096), (int)(z * 4096));
|
||||
} else if (keyword == "vt") {
|
||||
float u, v;
|
||||
ss >> u >> v;
|
||||
texcoords.emplace_back(u, 1.0f - v); // flip v
|
||||
} else if (keyword == "f") {
|
||||
std::vector<std::string> tokens;
|
||||
std::string token;
|
||||
while (ss >> token) tokens.push_back(token);
|
||||
|
||||
std::vector<int> v_idx, t_idx;
|
||||
for (const auto& t : tokens) {
|
||||
std::stringstream ts(t);
|
||||
std::string part;
|
||||
int vi = -1, ti = -1;
|
||||
|
||||
std::getline(ts, part, '/');
|
||||
if (!part.empty()) vi = std::stoi(part) - 1;
|
||||
|
||||
if (std::getline(ts, part, '/') && !part.empty())
|
||||
ti = std::stoi(part) - 1;
|
||||
|
||||
v_idx.push_back(vi);
|
||||
t_idx.push_back(ti);
|
||||
}
|
||||
|
||||
// Triangulate face
|
||||
for (size_t i = 1; i + 1 < v_idx.size(); ++i) {
|
||||
Triangle tri;
|
||||
tri.v0 = positions[v_idx[0]];
|
||||
tri.v1 = positions[v_idx[i]];
|
||||
tri.v2 = positions[v_idx[i + 1]];
|
||||
|
||||
tri.uv0 = (t_idx[0] >= 0 && t_idx[0] < (int)texcoords.size()) ? texcoords[t_idx[0]] : Vec2(0, 0);
|
||||
tri.uv1 = (t_idx[i] >= 0 && t_idx[i] < (int)texcoords.size()) ? texcoords[t_idx[i]] : Vec2(0, 0);
|
||||
tri.uv2 = (t_idx[i + 1] >= 0 && t_idx[i + 1] < (int)texcoords.size()) ? texcoords[t_idx[i + 1]] : Vec2(0, 0);
|
||||
|
||||
if (materials.count(current_material)) {
|
||||
tri.color = materials[current_material].color;
|
||||
tri.texture = texture_map[current_material];
|
||||
} else {
|
||||
tri.color = Color(255, 255, 255); // Fallback white
|
||||
tri.texture = 0; // No texture
|
||||
}
|
||||
|
||||
triangles.push_back(tri);
|
||||
}
|
||||
} else if (keyword == "mtllib") {
|
||||
std::string mtl_file;
|
||||
ss >> mtl_file;
|
||||
fs::path mtl_path = base_path / mtl_file;
|
||||
materials = load_mtl_materials(mtl_path.string());
|
||||
|
||||
// Load textures for all materials
|
||||
for (const auto& mat_pair : materials) {
|
||||
const std::string& mat_name = mat_pair.first;
|
||||
const Material& mat = mat_pair.second;
|
||||
if (!mat.texture_path.empty()) {
|
||||
fs::path texture_full_path = base_path / mat.texture_path;
|
||||
texture_map[mat_name] = renderer->load_texture(texture_full_path.string());
|
||||
} else {
|
||||
texture_map[mat_name] = 0; // No texture
|
||||
}
|
||||
}
|
||||
} else if (keyword == "usemtl") {
|
||||
ss >> current_material;
|
||||
}
|
||||
}
|
||||
|
||||
return triangles;
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include "renderer.h" // includes Vec3, Vec2, Triangle, Color
|
||||
|
||||
struct Material
|
||||
{
|
||||
Color color;
|
||||
std::string texture_path;
|
||||
};
|
||||
|
||||
std::map<std::string, Material> load_mtl_materials(const std::string& mtl_path);
|
||||
std::vector<Triangle> load_obj(const std::string& obj_path, Renderer* renderer);
|
||||
134
Engine/loader/obj_loader.cpp
Normal file
134
Engine/loader/obj_loader.cpp
Normal file
@@ -0,0 +1,134 @@
|
||||
#include "obj_loader.h"
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <map>
|
||||
#include <filesystem>
|
||||
|
||||
std::vector<Triangle> ObjLoader::load_obj(const std::string& obj_filepath,
|
||||
Renderer& renderer)
|
||||
{
|
||||
std::vector<Triangle> triangles;
|
||||
std::vector<Vec3> vertices;
|
||||
std::vector<Vec2> uvs;
|
||||
std::vector<Vec3> normals;
|
||||
std::map<std::string, Texture> material_textures; // Maps material names to textures
|
||||
std::string current_material;
|
||||
std::string mtl_filepath;
|
||||
|
||||
// Helper to parse MTL file
|
||||
auto parse_mtl = [&](const std::string& mtl_path) {
|
||||
std::ifstream mtl_file(mtl_path);
|
||||
if (!mtl_file.is_open()) {
|
||||
// Silently skip if MTL file cannot be opened
|
||||
return;
|
||||
}
|
||||
|
||||
std::string line, current_mtl;
|
||||
while (std::getline(mtl_file, line)) {
|
||||
std::istringstream iss(line);
|
||||
std::string token;
|
||||
iss >> token;
|
||||
|
||||
if (token == "newmtl") {
|
||||
iss >> current_mtl;
|
||||
}
|
||||
else if (token == "map_Kd") {
|
||||
std::string texture_path;
|
||||
std::getline(iss, texture_path);
|
||||
texture_path.erase(0, texture_path.find_first_not_of(" \t"));
|
||||
|
||||
// Construct full texture path relative to MTL directory
|
||||
std::filesystem::path mtl_dir = std::filesystem::path(mtl_path).parent_path();
|
||||
std::filesystem::path tex_path = mtl_dir / texture_path;
|
||||
|
||||
try {
|
||||
material_textures[current_mtl] = renderer.load_texture(tex_path.string());
|
||||
} catch (const std::exception& e) {
|
||||
// Fallback to default texture (id=0) if loading fails
|
||||
material_textures[current_mtl] = Texture(0, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
mtl_file.close();
|
||||
};
|
||||
|
||||
// Open and parse OBJ file
|
||||
std::ifstream obj_file(obj_filepath);
|
||||
if (!obj_file.is_open()) {
|
||||
throw std::runtime_error("Failed to open OBJ file: " + obj_filepath);
|
||||
}
|
||||
|
||||
std::filesystem::path obj_dir = std::filesystem::path(obj_filepath).parent_path();
|
||||
|
||||
std::string line;
|
||||
while (std::getline(obj_file, line)) {
|
||||
std::istringstream iss(line);
|
||||
std::string token;
|
||||
iss >> token;
|
||||
|
||||
if (token == "v") {
|
||||
float x, y, z;
|
||||
iss >> x >> y >> z;
|
||||
vertices.emplace_back(x, y, z);
|
||||
}
|
||||
else if (token == "vt") {
|
||||
float u, v;
|
||||
iss >> u >> v;
|
||||
uvs.emplace_back(u, v);
|
||||
}
|
||||
else if (token == "vn") {
|
||||
float x, y, z;
|
||||
iss >> x >> y >> z;
|
||||
normals.emplace_back(x, y, z);
|
||||
}
|
||||
else if (token == "f") {
|
||||
std::vector<std::tuple<int, int, int>> face_vertices; // v/vt/vn indices
|
||||
std::string vertex;
|
||||
while (iss >> vertex) {
|
||||
int v_idx = 0, vt_idx = 0, vn_idx = 0;
|
||||
std::sscanf(vertex.c_str(), "%d/%d/%d", &v_idx, &vt_idx, &vn_idx);
|
||||
face_vertices.emplace_back(v_idx - 1, vt_idx - 1, vn_idx - 1); // Convert to 0-based
|
||||
}
|
||||
|
||||
// Triangulate if necessary (e.g., for quads)
|
||||
for (size_t i = 1; i < face_vertices.size() - 1; ++i) {
|
||||
Triangle tri;
|
||||
auto [v0, vt0, vn0] = face_vertices[0];
|
||||
auto [v1, vt1, vn1] = face_vertices[i];
|
||||
auto [v2, vt2, vn2] = face_vertices[i + 1];
|
||||
|
||||
// Assign vertices
|
||||
tri.v0 = vertices[v0];
|
||||
tri.v1 = vertices[v1];
|
||||
tri.v2 = vertices[v2];
|
||||
|
||||
// Assign UVs (use default if not specified)
|
||||
tri.uv0 = vt0 >= 0 && vt0 < uvs.size() ? uvs[vt0] : Vec2(0, 0);
|
||||
tri.uv1 = vt1 >= 0 && vt1 < uvs.size() ? uvs[vt1] : Vec2(0, 0);
|
||||
tri.uv2 = vt2 >= 0 && vt2 < uvs.size() ? uvs[vt2] : Vec2(0, 0);
|
||||
|
||||
// Assign default color
|
||||
tri.color = Color(255, 255, 255);
|
||||
|
||||
// Assign texture based on current material
|
||||
tri.texture = material_textures.count(current_material) ?
|
||||
material_textures[current_material] : Texture(0, 0, 0);
|
||||
|
||||
triangles.push_back(tri);
|
||||
}
|
||||
}
|
||||
else if (token == "mtllib") {
|
||||
std::string mtl_name;
|
||||
iss >> mtl_name;
|
||||
mtl_filepath = (obj_dir / mtl_name).string();
|
||||
parse_mtl(mtl_filepath);
|
||||
}
|
||||
else if (token == "usemtl") {
|
||||
iss >> current_material;
|
||||
}
|
||||
}
|
||||
|
||||
obj_file.close();
|
||||
return triangles;
|
||||
}
|
||||
15
Engine/loader/obj_loader.h
Normal file
15
Engine/loader/obj_loader.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef OBJ_LOADER_H
|
||||
#define OBJ_LOADER_H
|
||||
|
||||
#include "renderer.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class ObjLoader
|
||||
{
|
||||
public:
|
||||
static std::vector<Triangle> load_obj(const std::string& obj_filepath,
|
||||
Renderer& renderer);
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user