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:
3
.vscode/c_cpp_properties.json
vendored
3
.vscode/c_cpp_properties.json
vendored
@@ -5,6 +5,9 @@
|
||||
"includePath": [
|
||||
"${workspaceFolder}/Engine/renderer",
|
||||
"${workspaceFolder}/Engine/loader",
|
||||
"${workspaceFolder}/Engine/math",
|
||||
"${workspaceFolder}/Engine/input",
|
||||
"${workspaceFolder}/Engine/time",
|
||||
"${workspaceFolder}/Engine",
|
||||
"${workspaceFolder}/external/**",
|
||||
"/usr/include",
|
||||
|
||||
@@ -11,13 +11,18 @@ set(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||
include_directories(${CMAKE_SOURCE_DIR}/Engine)
|
||||
include_directories(${CMAKE_SOURCE_DIR}/Engine/renderer)
|
||||
include_directories(${CMAKE_SOURCE_DIR}/Engine/loader)
|
||||
include_directories(${CMAKE_SOURCE_DIR}/Engine/input)
|
||||
include_directories(${CMAKE_SOURCE_DIR}/Engine/math)
|
||||
include_directories(${CMAKE_SOURCE_DIR}/Engine/time)
|
||||
include_directories(${CMAKE_SOURCE_DIR}/examples)
|
||||
include_directories(${CMAKE_SOURCE_DIR}/external/stb)
|
||||
|
||||
# Source files
|
||||
set(SOURCES
|
||||
${CMAKE_SOURCE_DIR}/Engine/renderer/renderer.cpp
|
||||
${CMAKE_SOURCE_DIR}/Engine/loader/mesh.cpp
|
||||
${CMAKE_SOURCE_DIR}/Engine/loader/obj_loader.cpp
|
||||
${CMAKE_SOURCE_DIR}/Engine/input/input.cpp
|
||||
${CMAKE_SOURCE_DIR}/Engine/time/time_manager.cpp
|
||||
${CMAKE_SOURCE_DIR}/examples/cube.cpp
|
||||
)
|
||||
|
||||
|
||||
157
Engine/input/input.cpp
Normal file
157
Engine/input/input.cpp
Normal file
@@ -0,0 +1,157 @@
|
||||
#include "input.h"
|
||||
#include <algorithm>
|
||||
|
||||
InputManager::InputManager(GLFWwindow* win) : window(win), scroll_delta(0.0f, 0.0f) {
|
||||
std::fill(previous_key_states.begin(), previous_key_states.end(), false);
|
||||
std::fill(previous_mouse_button_states.begin(), previous_mouse_button_states.end(), false);
|
||||
glfwSetWindowUserPointer(window, this);
|
||||
glfwSetScrollCallback(window, [](GLFWwindow* w, double xoffset, double yoffset) {
|
||||
InputManager* manager = static_cast<InputManager*>(glfwGetWindowUserPointer(w));
|
||||
manager->scroll_delta.x += static_cast<float>(xoffset);
|
||||
manager->scroll_delta.y += static_cast<float>(yoffset);
|
||||
});
|
||||
double x, y;
|
||||
glfwGetCursorPos(window, &x, &y);
|
||||
last_mouse_position = Vec2(static_cast<float>(x), static_cast<float>(y));
|
||||
}
|
||||
|
||||
int InputManager::get_key_code(InputKey key) {
|
||||
switch (key) {
|
||||
case InputKey::KEY_A: return GLFW_KEY_A;
|
||||
case InputKey::KEY_B: return GLFW_KEY_B;
|
||||
case InputKey::KEY_C: return GLFW_KEY_C;
|
||||
case InputKey::KEY_D: return GLFW_KEY_D;
|
||||
case InputKey::KEY_E: return GLFW_KEY_E;
|
||||
case InputKey::KEY_F: return GLFW_KEY_F;
|
||||
case InputKey::KEY_G: return GLFW_KEY_G;
|
||||
case InputKey::KEY_H: return GLFW_KEY_H;
|
||||
case InputKey::KEY_I: return GLFW_KEY_I;
|
||||
case InputKey::KEY_J: return GLFW_KEY_J;
|
||||
case InputKey::KEY_K: return GLFW_KEY_K;
|
||||
case InputKey::KEY_L: return GLFW_KEY_L;
|
||||
case InputKey::KEY_M: return GLFW_KEY_M;
|
||||
case InputKey::KEY_N: return GLFW_KEY_N;
|
||||
case InputKey::KEY_O: return GLFW_KEY_O;
|
||||
case InputKey::KEY_P: return GLFW_KEY_P;
|
||||
case InputKey::KEY_Q: return GLFW_KEY_Q;
|
||||
case InputKey::KEY_R: return GLFW_KEY_R;
|
||||
case InputKey::KEY_S: return GLFW_KEY_S;
|
||||
case InputKey::KEY_T: return GLFW_KEY_T;
|
||||
case InputKey::KEY_U: return GLFW_KEY_U;
|
||||
case InputKey::KEY_V: return GLFW_KEY_V;
|
||||
case InputKey::KEY_W: return GLFW_KEY_W;
|
||||
case InputKey::KEY_X: return GLFW_KEY_X;
|
||||
case InputKey::KEY_Y: return GLFW_KEY_Y;
|
||||
case InputKey::KEY_Z: return GLFW_KEY_Z;
|
||||
case InputKey::KEY_0: return GLFW_KEY_0;
|
||||
case InputKey::KEY_1: return GLFW_KEY_1;
|
||||
case InputKey::KEY_2: return GLFW_KEY_2;
|
||||
case InputKey::KEY_3: return GLFW_KEY_3;
|
||||
case InputKey::KEY_4: return GLFW_KEY_4;
|
||||
case InputKey::KEY_5: return GLFW_KEY_5;
|
||||
case InputKey::KEY_6: return GLFW_KEY_6;
|
||||
case InputKey::KEY_7: return GLFW_KEY_7;
|
||||
case InputKey::KEY_8: return GLFW_KEY_8;
|
||||
case InputKey::KEY_9: return GLFW_KEY_9;
|
||||
case InputKey::KEY_UP: return GLFW_KEY_UP;
|
||||
case InputKey::KEY_DOWN: return GLFW_KEY_DOWN;
|
||||
case InputKey::KEY_LEFT: return GLFW_KEY_LEFT;
|
||||
case InputKey::KEY_RIGHT: return GLFW_KEY_RIGHT;
|
||||
case InputKey::KEY_ENTER: return GLFW_KEY_ENTER;
|
||||
case InputKey::KEY_BACKSPACE: return GLFW_KEY_BACKSPACE;
|
||||
case InputKey::KEY_TAB: return GLFW_KEY_TAB;
|
||||
case InputKey::KEY_SHIFT: return GLFW_KEY_LEFT_SHIFT;
|
||||
case InputKey::KEY_CONTROL: return GLFW_KEY_LEFT_CONTROL;
|
||||
case InputKey::KEY_ALT: return GLFW_KEY_LEFT_ALT;
|
||||
case InputKey::KEY_CAPS_LOCK: return GLFW_KEY_CAPS_LOCK;
|
||||
case InputKey::KEY_SPACE: return GLFW_KEY_SPACE;
|
||||
case InputKey::KEY_ESCAPE: return GLFW_KEY_ESCAPE;
|
||||
default: return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int InputManager::get_mouse_button_code(InputKey key) {
|
||||
switch (key) {
|
||||
case InputKey::MOUSE_LEFT: return GLFW_MOUSE_BUTTON_LEFT;
|
||||
case InputKey::MOUSE_RIGHT: return GLFW_MOUSE_BUTTON_RIGHT;
|
||||
case InputKey::MOUSE_MIDDLE: return GLFW_MOUSE_BUTTON_MIDDLE;
|
||||
case InputKey::MOUSE_BUTTON_4: return GLFW_MOUSE_BUTTON_4;
|
||||
case InputKey::MOUSE_BUTTON_5: return GLFW_MOUSE_BUTTON_5;
|
||||
case InputKey::MOUSE_BUTTON_6: return GLFW_MOUSE_BUTTON_6;
|
||||
case InputKey::MOUSE_BUTTON_7: return GLFW_MOUSE_BUTTON_7;
|
||||
case InputKey::MOUSE_BUTTON_8: return GLFW_MOUSE_BUTTON_8;
|
||||
default: return -1;
|
||||
}
|
||||
}
|
||||
|
||||
bool InputManager::is_pressed(InputKey key) {
|
||||
int key_code = get_key_code(key);
|
||||
if (key_code != -1) {
|
||||
return glfwGetKey(window, key_code) == GLFW_PRESS;
|
||||
}
|
||||
int button = get_mouse_button_code(key);
|
||||
if (button != -1) {
|
||||
return glfwGetMouseButton(window, button) == GLFW_PRESS;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool InputManager::was_pressed(InputKey key) {
|
||||
int key_code = get_key_code(key);
|
||||
if (key_code != -1) {
|
||||
bool current = glfwGetKey(window, key_code) == GLFW_PRESS;
|
||||
bool previous = previous_key_states[key_code];
|
||||
return current && !previous;
|
||||
}
|
||||
int button = get_mouse_button_code(key);
|
||||
if (button != -1) {
|
||||
bool current = glfwGetMouseButton(window, button) == GLFW_PRESS;
|
||||
bool previous = previous_mouse_button_states[button];
|
||||
return current && !previous;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool InputManager::was_released(InputKey key) {
|
||||
int key_code = get_key_code(key);
|
||||
if (key_code != -1) {
|
||||
bool current = glfwGetKey(window, key_code) == GLFW_PRESS;
|
||||
bool previous = previous_key_states[key_code];
|
||||
return !current && previous;
|
||||
}
|
||||
int button = get_mouse_button_code(key);
|
||||
if (button != -1) {
|
||||
bool current = glfwGetMouseButton(window, button) == GLFW_PRESS;
|
||||
bool previous = previous_mouse_button_states[button];
|
||||
return !current && previous;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Vec2 InputManager::get_mouse_position() {
|
||||
double x, y;
|
||||
glfwGetCursorPos(window, &x, &y);
|
||||
return Vec2(static_cast<float>(x), static_cast<float>(y));
|
||||
}
|
||||
|
||||
Vec2 InputManager::get_mouse_delta() {
|
||||
Vec2 current = get_mouse_position();
|
||||
Vec2 delta = current - last_mouse_position;
|
||||
last_mouse_position = current;
|
||||
return delta;
|
||||
}
|
||||
|
||||
Vec2 InputManager::get_scroll_delta() {
|
||||
Vec2 delta = scroll_delta;
|
||||
scroll_delta = Vec2(0.0f, 0.0f);
|
||||
return delta;
|
||||
}
|
||||
|
||||
void InputManager::update() {
|
||||
for (int key = 0; key < 400; ++key) {
|
||||
previous_key_states[key] = (glfwGetKey(window, key) == GLFW_PRESS);
|
||||
}
|
||||
for (int button = 0; button < 8; ++button) {
|
||||
previous_mouse_button_states[button] = (glfwGetMouseButton(window, button) == GLFW_PRESS);
|
||||
}
|
||||
}
|
||||
97
Engine/input/input.h
Normal file
97
Engine/input/input.h
Normal file
@@ -0,0 +1,97 @@
|
||||
#include <renderer.h>
|
||||
#include <math.h>
|
||||
#include <array>
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
enum class InputEvent
|
||||
{
|
||||
KEY_PRESS,
|
||||
KEY_RELEASE,
|
||||
MOUSE_MOVE,
|
||||
MOUSE_BUTTON_PRESS,
|
||||
MOUSE_BUTTON_RELEASE,
|
||||
};
|
||||
|
||||
enum class InputKey
|
||||
{
|
||||
KEY_A,
|
||||
KEY_B,
|
||||
KEY_C,
|
||||
KEY_D,
|
||||
KEY_E,
|
||||
KEY_F,
|
||||
KEY_G,
|
||||
KEY_H,
|
||||
KEY_I,
|
||||
KEY_J,
|
||||
KEY_K,
|
||||
KEY_L,
|
||||
KEY_M,
|
||||
KEY_N,
|
||||
KEY_O,
|
||||
KEY_P,
|
||||
KEY_Q,
|
||||
KEY_R,
|
||||
KEY_S,
|
||||
KEY_T,
|
||||
KEY_U,
|
||||
KEY_V,
|
||||
KEY_W,
|
||||
KEY_X,
|
||||
KEY_Y,
|
||||
KEY_Z,
|
||||
KEY_0,
|
||||
KEY_1,
|
||||
KEY_2,
|
||||
KEY_3,
|
||||
KEY_4,
|
||||
KEY_5,
|
||||
KEY_6,
|
||||
KEY_7,
|
||||
KEY_8,
|
||||
KEY_9,
|
||||
KEY_UP,
|
||||
KEY_DOWN,
|
||||
KEY_LEFT,
|
||||
KEY_RIGHT,
|
||||
KEY_ENTER,
|
||||
KEY_BACKSPACE,
|
||||
KEY_TAB,
|
||||
KEY_SHIFT,
|
||||
KEY_CONTROL,
|
||||
KEY_ALT,
|
||||
KEY_CAPS_LOCK,
|
||||
KEY_SPACE,
|
||||
KEY_ESCAPE,
|
||||
MOUSE_LEFT,
|
||||
MOUSE_RIGHT,
|
||||
MOUSE_MIDDLE,
|
||||
MOUSE_BUTTON_4,
|
||||
MOUSE_BUTTON_5,
|
||||
MOUSE_BUTTON_6,
|
||||
MOUSE_BUTTON_7,
|
||||
MOUSE_BUTTON_8,
|
||||
};
|
||||
|
||||
class InputManager
|
||||
{
|
||||
private:
|
||||
GLFWwindow* window;
|
||||
Vec2 scroll_delta;
|
||||
Vec2 last_mouse_position;
|
||||
std::array<bool, 400> previous_key_states;
|
||||
std::array<bool, 8> previous_mouse_button_states;
|
||||
|
||||
int get_key_code(InputKey key);
|
||||
int get_mouse_button_code(InputKey key);
|
||||
|
||||
public:
|
||||
InputManager(GLFWwindow* win);
|
||||
bool is_pressed(InputKey key);
|
||||
bool was_pressed(InputKey key);
|
||||
bool was_released(InputKey key);
|
||||
Vec2 get_mouse_position();
|
||||
Vec2 get_mouse_delta();
|
||||
Vec2 get_scroll_delta();
|
||||
void update();
|
||||
};
|
||||
@@ -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
|
||||
3
Engine/math/math.h
Normal file
3
Engine/math/math.h
Normal file
@@ -0,0 +1,3 @@
|
||||
#include <vec3.h>
|
||||
#include <vec2.h>
|
||||
#include <matrix4.h>
|
||||
147
Engine/math/matrix4.h
Normal file
147
Engine/math/matrix4.h
Normal file
@@ -0,0 +1,147 @@
|
||||
#pragma once
|
||||
|
||||
#include "vec3.h"
|
||||
#include <cmath>
|
||||
|
||||
#ifndef FIXED_POINT_PRECISION
|
||||
#define FIXED_POINT_PRECISION 4096
|
||||
#endif
|
||||
|
||||
inline int fixed_sin(int angle)
|
||||
{
|
||||
return std::sin(angle / static_cast<float>(FIXED_POINT_PRECISION)) * FIXED_POINT_PRECISION;
|
||||
}
|
||||
|
||||
inline int fixed_cos(int angle)
|
||||
{
|
||||
return std::cos(angle / static_cast<float>(FIXED_POINT_PRECISION)) * FIXED_POINT_PRECISION;
|
||||
}
|
||||
|
||||
namespace Math {
|
||||
|
||||
struct Matrix4
|
||||
{
|
||||
int m[16];
|
||||
|
||||
inline Matrix4()
|
||||
{
|
||||
for (int i = 0; i < 16; ++i)
|
||||
m[i] = (i % 5 == 0) ? FIXED_POINT_PRECISION : 0;
|
||||
}
|
||||
|
||||
inline static Matrix4 identity()
|
||||
{
|
||||
return Matrix4();
|
||||
}
|
||||
|
||||
inline static Matrix4 multiply(const Matrix4 &a, const Matrix4 &b)
|
||||
{
|
||||
Matrix4 result;
|
||||
for (int row = 0; row < 4; ++row)
|
||||
{
|
||||
for (int col = 0; col < 4; ++col)
|
||||
{
|
||||
int sum = 0;
|
||||
for (int k = 0; k < 4; ++k)
|
||||
{
|
||||
sum += (a.m[row * 4 + k] * b.m[k * 4 + col]) / FIXED_POINT_PRECISION;
|
||||
}
|
||||
result.m[row * 4 + col] = sum;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
inline static Vec3 multiply(const Matrix4 &mat, const Vec3 &v)
|
||||
{
|
||||
int x = (mat.m[0] * v.x + mat.m[1] * v.y + mat.m[2] * v.z + mat.m[3]) / FIXED_POINT_PRECISION;
|
||||
int y = (mat.m[4] * v.x + mat.m[5] * v.y + mat.m[6] * v.z + mat.m[7]) / FIXED_POINT_PRECISION;
|
||||
int z = (mat.m[8] * v.x + mat.m[9] * v.y + mat.m[10] * v.z + mat.m[11]) / FIXED_POINT_PRECISION;
|
||||
return Vec3(x, y, z);
|
||||
}
|
||||
|
||||
inline static Matrix4 translation(int tx, int ty, int tz)
|
||||
{
|
||||
Matrix4 result = identity();
|
||||
result.m[12] = tx * FIXED_POINT_PRECISION;
|
||||
result.m[13] = ty * FIXED_POINT_PRECISION;
|
||||
result.m[14] = tz * FIXED_POINT_PRECISION;
|
||||
return result;
|
||||
}
|
||||
|
||||
inline static Matrix4 scale(int sx, int sy, int sz)
|
||||
{
|
||||
Matrix4 result;
|
||||
result.m[0] = sx * FIXED_POINT_PRECISION;
|
||||
result.m[5] = sy * FIXED_POINT_PRECISION;
|
||||
result.m[10] = sz * FIXED_POINT_PRECISION;
|
||||
return result;
|
||||
}
|
||||
|
||||
inline static Matrix4 rotateX(int angle)
|
||||
{
|
||||
Matrix4 mat = identity();
|
||||
float rad = angle / static_cast<float>(FIXED_POINT_PRECISION);
|
||||
int cos_val = static_cast<int>(std::cos(rad) * FIXED_POINT_PRECISION);
|
||||
int sin_val = static_cast<int>(std::sin(rad) * FIXED_POINT_PRECISION);
|
||||
mat.m[5] = cos_val;
|
||||
mat.m[6] = -sin_val;
|
||||
mat.m[9] = sin_val;
|
||||
mat.m[10] = cos_val;
|
||||
return mat;
|
||||
}
|
||||
|
||||
inline static Matrix4 rotateY(int angle)
|
||||
{
|
||||
Matrix4 mat = identity();
|
||||
float rad = angle / static_cast<float>(FIXED_POINT_PRECISION);
|
||||
int cos_val = static_cast<int>(std::cos(rad) * FIXED_POINT_PRECISION);
|
||||
int sin_val = static_cast<int>(std::sin(rad) * FIXED_POINT_PRECISION);
|
||||
mat.m[0] = cos_val;
|
||||
mat.m[2] = sin_val;
|
||||
mat.m[8] = -sin_val;
|
||||
mat.m[10] = cos_val;
|
||||
return mat;
|
||||
}
|
||||
|
||||
inline static Matrix4 rotateZ(int angle)
|
||||
{
|
||||
Matrix4 mat = identity();
|
||||
float rad = angle / static_cast<float>(FIXED_POINT_PRECISION);
|
||||
int cos_val = static_cast<int>(std::cos(rad) * FIXED_POINT_PRECISION);
|
||||
int sin_val = static_cast<int>(std::sin(rad) * FIXED_POINT_PRECISION);
|
||||
mat.m[0] = cos_val;
|
||||
mat.m[1] = -sin_val;
|
||||
mat.m[4] = sin_val;
|
||||
mat.m[5] = cos_val;
|
||||
return mat;
|
||||
}
|
||||
|
||||
inline static Matrix4 perspective(float fov_rad, float aspect, float near, float far)
|
||||
{
|
||||
Matrix4 result = {};
|
||||
float tanHalfFov = std::tan(fov_rad / 2.0f);
|
||||
result.m[0] = static_cast<int>((1.0f / (aspect * tanHalfFov)) * FIXED_POINT_PRECISION);
|
||||
result.m[5] = static_cast<int>((1.0f / tanHalfFov) * FIXED_POINT_PRECISION);
|
||||
result.m[10] = static_cast<int>(((far + near) / (near - far)) * FIXED_POINT_PRECISION);
|
||||
result.m[11] = static_cast<int>(((2.0f * far * near) / (near - far)) * FIXED_POINT_PRECISION);
|
||||
result.m[14] = -FIXED_POINT_PRECISION;
|
||||
result.m[15] = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
inline static Matrix4 orthographic(float left, float right, float bottom, float top, float near, float far)
|
||||
{
|
||||
Matrix4 result = {};
|
||||
result.m[0] = static_cast<int>((2.0f / (right - left)) * FIXED_POINT_PRECISION);
|
||||
result.m[5] = static_cast<int>((2.0f / (top - bottom)) * FIXED_POINT_PRECISION);
|
||||
result.m[10] = static_cast<int>((-2.0f / (far - near)) * FIXED_POINT_PRECISION);
|
||||
result.m[12] = static_cast<int>((-(right + left) / (right - left)) * FIXED_POINT_PRECISION);
|
||||
result.m[13] = static_cast<int>((-(top + bottom) / (top - bottom)) * FIXED_POINT_PRECISION);
|
||||
result.m[14] = static_cast<int>((-(far + near) / (far - near)) * FIXED_POINT_PRECISION);
|
||||
result.m[15] = FIXED_POINT_PRECISION;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
96
Engine/math/vec2.h
Normal file
96
Engine/math/vec2.h
Normal file
@@ -0,0 +1,96 @@
|
||||
#pragma once
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
|
||||
#ifndef FIXED_POINT_PRECISION
|
||||
#define FIXED_POINT_PRECISION 4096
|
||||
#endif
|
||||
|
||||
namespace Math {
|
||||
struct Vec2
|
||||
{
|
||||
int x, y;
|
||||
|
||||
inline Vec2(int x_ = 0, int y_ = 0) : x(x_), y(y_) {}
|
||||
|
||||
inline Vec2 operator+(const Vec2 &other) const
|
||||
{
|
||||
return Vec2(x + other.x, y + other.y);
|
||||
}
|
||||
|
||||
inline Vec2 operator-(const Vec2 &other) const
|
||||
{
|
||||
return Vec2(x - other.x, y - other.y);
|
||||
}
|
||||
|
||||
inline Vec2 operator*(int scalar_fixed) const
|
||||
{
|
||||
return Vec2((x * scalar_fixed) / FIXED_POINT_PRECISION,
|
||||
(y * scalar_fixed) / FIXED_POINT_PRECISION);
|
||||
}
|
||||
|
||||
inline Vec2 operator/(int scalar_fixed) const
|
||||
{
|
||||
return Vec2((x * FIXED_POINT_PRECISION) / scalar_fixed,
|
||||
(y * FIXED_POINT_PRECISION) / scalar_fixed);
|
||||
}
|
||||
|
||||
inline Vec2 &operator+=(const Vec2 &other)
|
||||
{
|
||||
x += other.x;
|
||||
y += other.y;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Vec2 &operator-=(const Vec2 &other)
|
||||
{
|
||||
x -= other.x;
|
||||
y -= other.y;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline static int dot(const Vec2 &a, const Vec2 &b)
|
||||
{
|
||||
return a.x * b.x + a.y * b.y;
|
||||
}
|
||||
|
||||
inline static int cross(const Vec2 &a, const Vec2 &b)
|
||||
{
|
||||
return a.x * b.y - a.y * b.x;
|
||||
}
|
||||
|
||||
inline int length_squared() const
|
||||
{
|
||||
return x * x + y * y;
|
||||
}
|
||||
|
||||
inline int length() const
|
||||
{
|
||||
return static_cast<int>(std::sqrt(static_cast<float>(length_squared())));
|
||||
}
|
||||
|
||||
inline Vec2 normalized() const
|
||||
{
|
||||
int len = length();
|
||||
if (len == 0) return *this;
|
||||
return Vec2((x * FIXED_POINT_PRECISION) / len,
|
||||
(y * FIXED_POINT_PRECISION) / len);
|
||||
}
|
||||
|
||||
inline int distance_squared(const Vec2 &other) const
|
||||
{
|
||||
int dx = x - other.x;
|
||||
int dy = y - other.y;
|
||||
return dx * dx + dy * dy;
|
||||
}
|
||||
|
||||
inline int distance(const Vec2 &other) const
|
||||
{
|
||||
return static_cast<int>(std::sqrt(static_cast<float>(distance_squared(other))));
|
||||
}
|
||||
|
||||
inline static Vec2 zero() { return Vec2(0, 0); }
|
||||
};
|
||||
|
||||
}
|
||||
198
Engine/math/vec3.h
Normal file
198
Engine/math/vec3.h
Normal file
@@ -0,0 +1,198 @@
|
||||
#pragma once
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
using std::sqrt;
|
||||
|
||||
#ifndef FIXED_POINT_PRECISION
|
||||
#define FIXED_POINT_PRECISION 4096
|
||||
#endif
|
||||
|
||||
namespace Math {
|
||||
struct Vec3
|
||||
{
|
||||
int x, y, z;
|
||||
|
||||
inline Vec3(int x_ = 0, int y_ = 0, int z_ = 0) : x(x_), y(y_), z(z_) {}
|
||||
|
||||
inline Vec3 operator+(const Vec3 &other) const
|
||||
{
|
||||
return Vec3(x + other.x, y + other.y, z + other.z);
|
||||
}
|
||||
|
||||
inline Vec3 operator-(const Vec3 &other) const
|
||||
{
|
||||
return Vec3(x - other.x, y - other.y, z - other.z);
|
||||
}
|
||||
|
||||
inline Vec3 operator*(int scalar) const
|
||||
{
|
||||
return Vec3(x * scalar, y * scalar, z * scalar);
|
||||
}
|
||||
|
||||
inline Vec3 operator/(int scalar) const
|
||||
{
|
||||
return Vec3(x / scalar, y / scalar, z / scalar);
|
||||
}
|
||||
|
||||
inline Vec3 &operator+=(const Vec3 &other)
|
||||
{
|
||||
x += other.x;
|
||||
y += other.y;
|
||||
z += other.z;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Vec3 &operator-=(const Vec3 &other)
|
||||
{
|
||||
x -= other.x;
|
||||
y -= other.y;
|
||||
z -= other.z;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline static int dot(const Vec3 &a, const Vec3 &b)
|
||||
{
|
||||
return a.x * b.x + a.y * b.y + a.z * b.z;
|
||||
}
|
||||
|
||||
inline static Vec3 cross(const Vec3 &a, const Vec3 &b)
|
||||
{
|
||||
return Vec3(
|
||||
a.y * b.z - a.z * b.y,
|
||||
a.z * b.x - a.x * b.z,
|
||||
a.x * b.y - a.y * b.x);
|
||||
}
|
||||
|
||||
inline float length() const
|
||||
{
|
||||
return std::sqrt(static_cast<float>(x * x + y * y + z * z));
|
||||
}
|
||||
|
||||
inline Vec3 normalized() const
|
||||
{
|
||||
float len = length();
|
||||
return len == 0.0f ? *this : Vec3(static_cast<int>(x / len), static_cast<int>(y / len), static_cast<int>(z / len));
|
||||
}
|
||||
|
||||
inline float distance(const Vec3 &other) const
|
||||
{
|
||||
return std::sqrt(static_cast<float>((x - other.x) * (x - other.x) + (y - other.y) * (y - other.y) + (z - other.z) * (z - other.z)));
|
||||
}
|
||||
|
||||
inline float angle(const Vec3 &other) const
|
||||
{
|
||||
float dotProduct = dot(*this, other);
|
||||
float lengths = length() * other.length();
|
||||
return std::acos(dotProduct / lengths);
|
||||
}
|
||||
|
||||
inline Vec3 reflect(const Vec3 &normal) const
|
||||
{
|
||||
int dotProduct = dot(*this, normal);
|
||||
return *this - normal * (dotProduct * 2);
|
||||
}
|
||||
|
||||
inline Vec3 refract(const Vec3 &normal, float ior) const
|
||||
{
|
||||
float cosi = -dot(*this, normal);
|
||||
float eta = 1.0f / ior;
|
||||
float k = 1.0f - eta * eta * (1.0f - cosi * cosi);struct Vec2
|
||||
{
|
||||
int x, y;
|
||||
|
||||
inline Vec2(int x_ = 0, int y_ = 0) : x(x_), y(y_) {}
|
||||
|
||||
inline Vec2 operator+(const Vec2 &other) const
|
||||
{
|
||||
return Vec2(x + other.x, y + other.y);
|
||||
}
|
||||
|
||||
inline Vec2 operator-(const Vec2 &other) const
|
||||
{
|
||||
return Vec2(x - other.x, y - other.y);
|
||||
}
|
||||
|
||||
inline Vec2 operator*(int scalar_fixed) const
|
||||
{
|
||||
return Vec2((x * scalar_fixed) / FIXED_POINT_PRECISION,
|
||||
(y * scalar_fixed) / FIXED_POINT_PRECISION);
|
||||
}
|
||||
|
||||
inline Vec2 operator/(int scalar_fixed) const
|
||||
{
|
||||
return Vec2((x * FIXED_POINT_PRECISION) / scalar_fixed,
|
||||
(y * FIXED_POINT_PRECISION) / scalar_fixed);
|
||||
}
|
||||
|
||||
inline Vec2 &operator+=(const Vec2 &other)
|
||||
{
|
||||
x += other.x;
|
||||
y += other.y;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Vec2 &operator-=(const Vec2 &other)
|
||||
{
|
||||
x -= other.x;
|
||||
y -= other.y;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline static int dot(const Vec2 &a, const Vec2 &b)
|
||||
{
|
||||
return a.x * b.x + a.y * b.y;
|
||||
}
|
||||
|
||||
inline static int cross(const Vec2 &a, const Vec2 &b)
|
||||
{
|
||||
return a.x * b.y - a.y * b.x;
|
||||
}
|
||||
|
||||
inline int length_squared() const
|
||||
{
|
||||
return x * x + y * y;
|
||||
}
|
||||
|
||||
inline int length() const
|
||||
{
|
||||
return static_cast<int>(std::sqrt(static_cast<float>(length_squared())));
|
||||
}
|
||||
|
||||
inline Vec2 normalized() const
|
||||
{
|
||||
int len = length();
|
||||
if (len == 0) return *this;
|
||||
return Vec2((x * FIXED_POINT_PRECISION) / len,
|
||||
(y * FIXED_POINT_PRECISION) / len);
|
||||
}
|
||||
|
||||
inline int distance_squared(const Vec2 &other) const
|
||||
{
|
||||
int dx = x - other.x;
|
||||
int dy = y - other.y;
|
||||
return dx * dx + dy * dy;
|
||||
}
|
||||
|
||||
inline int distance(const Vec2 &other) const
|
||||
{
|
||||
return static_cast<int>(std::sqrt(static_cast<float>(distance_squared(other))));
|
||||
}
|
||||
|
||||
inline static Vec2 zero() { return Vec2(0, 0); }
|
||||
};
|
||||
|
||||
if (k < 0.0f)
|
||||
return Vec3(0, 0, 0);
|
||||
return *this * eta + normal * (eta * cosi - std::sqrt(k));
|
||||
}
|
||||
|
||||
inline Vec3 project(const Vec3 &other) const
|
||||
{
|
||||
float dotProduct = dot(*this, other);
|
||||
float lenSquared = other.x * other.x + other.y * other.y + other.z * other.z;
|
||||
return other * (dotProduct / lenSquared);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,2 +1,5 @@
|
||||
#include <renderer.h>
|
||||
#include <mesh.h>
|
||||
#include <mesh.h>
|
||||
#include <time_manager.h>
|
||||
#include <math.h>
|
||||
#include <input.h>
|
||||
@@ -8,45 +8,16 @@
|
||||
#define FIXED_POINT_PRECISION 4096
|
||||
#define FIXED_POINT_SHIFT 12
|
||||
|
||||
Vec3::Vec3(int x_, int y_, int z_) : x(x_), y(y_), z(z_) {}
|
||||
|
||||
Vec2::Vec2(float x_, float y_) : x(x_), y(y_) {}
|
||||
|
||||
Color::Color(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {}
|
||||
|
||||
Matrix4::Matrix4()
|
||||
{
|
||||
for (int i = 0; i < 16; i++)
|
||||
m[i] = (i % 5 == 0) ? FIXED_POINT_PRECISION : 0;
|
||||
}
|
||||
Texture::Texture(GLuint id_, int width_, int height_) : id(id_), width(width_), height(height_) {}
|
||||
|
||||
Vec3 matrix_multiply(const Matrix4 &mat, const Vec3 &v)
|
||||
Texture::~Texture()
|
||||
{
|
||||
int x = (mat.m[0] * v.x + mat.m[1] * v.y + mat.m[2] * v.z + mat.m[3] * FIXED_POINT_PRECISION) / FIXED_POINT_PRECISION;
|
||||
int y = (mat.m[4] * v.x + mat.m[5] * v.y + mat.m[6] * v.z + mat.m[7] * FIXED_POINT_PRECISION) / FIXED_POINT_PRECISION;
|
||||
int z = (mat.m[8] * v.x + mat.m[9] * v.y + mat.m[10] * v.z + mat.m[11] * FIXED_POINT_PRECISION) / FIXED_POINT_PRECISION;
|
||||
return Vec3(x, y, z);
|
||||
}
|
||||
|
||||
Matrix4 matrix_multiply(const Matrix4 &a, const Matrix4 &b)
|
||||
{
|
||||
Matrix4 result;
|
||||
|
||||
for (int row = 0; row < 4; ++row)
|
||||
if (id != 0)
|
||||
{
|
||||
for (int col = 0; col < 4; ++col)
|
||||
{
|
||||
result.m[row * 4 + col] =
|
||||
(a.m[row * 4 + 0] * b.m[col + 0]) +
|
||||
(a.m[row * 4 + 1] * b.m[col + 4]) +
|
||||
(a.m[row * 4 + 2] * b.m[col + 8]) +
|
||||
(a.m[row * 4 + 3] * b.m[col + 12]);
|
||||
|
||||
result.m[row * 4 + col] /= 4096;
|
||||
}
|
||||
glDeleteTextures(1, &id);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Color convert_color(const Color &color)
|
||||
@@ -115,9 +86,9 @@ void Renderer::render(const std::vector<Triangle> &triangles)
|
||||
for (const auto &tri : triangles)
|
||||
{
|
||||
Triangle t;
|
||||
t.v0 = matrix_multiply(view_matrix, tri.v0);
|
||||
t.v1 = matrix_multiply(view_matrix, tri.v1);
|
||||
t.v2 = matrix_multiply(view_matrix, tri.v2);
|
||||
t.v0 = Matrix4::multiply(view_matrix, tri.v0);
|
||||
t.v1 = Matrix4::multiply(view_matrix, tri.v1);
|
||||
t.v2 = Matrix4::multiply(view_matrix, tri.v2);
|
||||
t.uv0 = tri.uv0;
|
||||
t.uv1 = tri.uv1;
|
||||
t.uv2 = tri.uv2;
|
||||
@@ -180,33 +151,47 @@ void Renderer::render(const std::vector<Triangle> &triangles)
|
||||
screen_tri.v0.y += (rand() % 3) - 1;
|
||||
}
|
||||
|
||||
screen_tri.uv0 = tri.uv0;
|
||||
screen_tri.uv1 = tri.uv1;
|
||||
screen_tri.uv2 = tri.uv2;
|
||||
float dx1 = screen_tri.v1.x - screen_tri.v0.x;
|
||||
float dy1 = screen_tri.v1.y - screen_tri.v0.y;
|
||||
float dx2 = screen_tri.v2.x - screen_tri.v0.x;
|
||||
float dy2 = screen_tri.v2.y - screen_tri.v0.y;
|
||||
|
||||
float area = dx1 * dy2 - dx2 * dy1;
|
||||
if (std::abs(area) > 1e-6)
|
||||
{
|
||||
screen_tri.uv0 = tri.uv0;
|
||||
screen_tri.uv1 = tri.uv1;
|
||||
screen_tri.uv2 = tri.uv2;
|
||||
}
|
||||
else
|
||||
{
|
||||
screen_tri.uv0 = tri.uv0;
|
||||
screen_tri.uv1 = tri.uv1;
|
||||
screen_tri.uv2 = tri.uv2;
|
||||
}
|
||||
|
||||
screen_tri.color = convert_color(tri.color);
|
||||
screen_tri.texture = tri.texture;
|
||||
|
||||
screen_triangles.push_back(screen_tri);
|
||||
}
|
||||
|
||||
// Group triangles by texture
|
||||
std::map<GLuint, std::vector<Triangle>> texture_groups;
|
||||
for (const auto& tri : screen_triangles)
|
||||
{
|
||||
texture_groups[tri.texture].push_back(tri);
|
||||
texture_groups[tri.texture.id].push_back(tri);
|
||||
}
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
// Render each texture group
|
||||
for (const auto& group : texture_groups)
|
||||
{
|
||||
GLuint tex = group.first;
|
||||
GLuint tex_id = group.first;
|
||||
const std::vector<Triangle>& tris = group.second;
|
||||
|
||||
if (tex != 0)
|
||||
if (tex_id != 0)
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_2D, tex);
|
||||
glBindTexture(GL_TEXTURE_2D, tex_id);
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
@@ -222,19 +207,19 @@ void Renderer::render(const std::vector<Triangle> &triangles)
|
||||
Color c = tri.color;
|
||||
glColor3ub(c.r, c.g, c.b);
|
||||
|
||||
if (tex != 0)
|
||||
if (tex_id != 0)
|
||||
{
|
||||
glTexCoord2f(tri.uv0.x, tri.uv0.y);
|
||||
}
|
||||
glVertex3f(static_cast<float>(tri.v0.x), static_cast<float>(tri.v0.y), static_cast<float>(tri.v0.z) / 32767.0f);
|
||||
|
||||
if (tex != 0)
|
||||
if (tex_id != 0)
|
||||
{
|
||||
glTexCoord2f(tri.uv1.x, tri.uv1.y);
|
||||
}
|
||||
glVertex3f(static_cast<float>(tri.v1.x), static_cast<float>(tri.v1.y), static_cast<float>(tri.v1.z) / 32767.0f);
|
||||
|
||||
if (tex != 0)
|
||||
if (tex_id != 0)
|
||||
{
|
||||
glTexCoord2f(tri.uv2.x, tri.uv2.y);
|
||||
}
|
||||
@@ -251,7 +236,7 @@ void Renderer::render(const std::vector<Triangle> &triangles)
|
||||
glVertex3f(static_cast<float>(tri.v1.x), static_cast<float>(tri.v1.y), static_cast<float>(tri.v1.z) / 32767.0f);
|
||||
glVertex3f(static_cast<float>(tri.v2.x), static_cast<float>(tri.v2.y), static_cast<float>(tri.v2.z) / 32767.0f);
|
||||
glEnd();
|
||||
if (tex != 0) glEnable(GL_TEXTURE_2D);
|
||||
if (tex_id != 0) glEnable(GL_TEXTURE_2D);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -268,7 +253,7 @@ bool Renderer::should_close()
|
||||
return glfwWindowShouldClose(window);
|
||||
}
|
||||
|
||||
GLuint Renderer::load_texture(const std::string &filepath)
|
||||
Texture Renderer::load_texture(const std::string &filepath)
|
||||
{
|
||||
int width, height, channels;
|
||||
unsigned char *image = stbi_load(filepath.c_str(), &width, &height, &channels, 3);
|
||||
@@ -319,9 +304,9 @@ GLuint Renderer::load_texture(const std::string &filepath)
|
||||
}
|
||||
}
|
||||
|
||||
GLuint texture;
|
||||
glGenTextures(1, &texture);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
GLuint texture_id;
|
||||
glGenTextures(1, &texture_id);
|
||||
glBindTexture(GL_TEXTURE_2D, texture_id);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, new_width, new_height, 0, GL_RGB, GL_UNSIGNED_BYTE, resized);
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
@@ -333,5 +318,5 @@ GLuint Renderer::load_texture(const std::string &filepath)
|
||||
stbi_image_free(image);
|
||||
delete[] resized;
|
||||
|
||||
return texture;
|
||||
return Texture(texture_id, new_width, new_height);
|
||||
}
|
||||
@@ -6,18 +6,9 @@
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <math.h>
|
||||
|
||||
struct Vec3
|
||||
{
|
||||
int x, y, z;
|
||||
Vec3(int x_ = 0, int y_ = 0, int z_ = 0);
|
||||
};
|
||||
|
||||
struct Vec2
|
||||
{
|
||||
float x, y;
|
||||
Vec2(float x_ = 0.0f, float y_ = 0.0f);
|
||||
};
|
||||
using namespace Math;
|
||||
|
||||
struct Color
|
||||
{
|
||||
@@ -25,10 +16,13 @@ struct Color
|
||||
Color(uint8_t r_ = 255, uint8_t g_ = 255, uint8_t b_ = 255);
|
||||
};
|
||||
|
||||
struct Matrix4
|
||||
struct Texture
|
||||
{
|
||||
int m[16];
|
||||
Matrix4();
|
||||
GLuint id;
|
||||
int width;
|
||||
int height;
|
||||
Texture(GLuint id_ = 0, int width_ = 0, int height_ = 0);
|
||||
~Texture();
|
||||
};
|
||||
|
||||
struct Triangle
|
||||
@@ -36,13 +30,8 @@ struct Triangle
|
||||
Vec3 v0, v1, v2;
|
||||
Vec2 uv0, uv1, uv2;
|
||||
Color color;
|
||||
GLuint texture;
|
||||
Texture texture;
|
||||
};
|
||||
|
||||
Vec3 matrix_multiply(const Matrix4 &mat, const Vec3 &v);
|
||||
|
||||
Matrix4 matrix_multiply(const Matrix4 &a, const Matrix4 &b);
|
||||
|
||||
Color convert_color(const Color &color);
|
||||
|
||||
class Renderer
|
||||
@@ -64,7 +53,7 @@ public:
|
||||
void render(const std::vector<Triangle> &triangles);
|
||||
void end_frame();
|
||||
bool should_close();
|
||||
GLuint load_texture(const std::string &filepath);
|
||||
Texture load_texture(const std::string &filepath);
|
||||
GLFWwindow *get_window() { return window; }
|
||||
};
|
||||
|
||||
|
||||
32
Engine/time/time_manager.cpp
Normal file
32
Engine/time/time_manager.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#include "time_manager.h"
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
Time::Time()
|
||||
: lastFrameTime(glfwGetTime())
|
||||
, currentFrameTime(lastFrameTime)
|
||||
, deltaTime(0)
|
||||
, elapsedTime(0.0f) {
|
||||
}
|
||||
|
||||
void Time::Update() {
|
||||
currentFrameTime = glfwGetTime();
|
||||
float deltaTimeSeconds = static_cast<float>(currentFrameTime - lastFrameTime);
|
||||
|
||||
// Store delta time in fixed-point and update elapsed time
|
||||
deltaTime = SecondsToFixed(deltaTimeSeconds);
|
||||
elapsedTime += deltaTimeSeconds;
|
||||
|
||||
lastFrameTime = currentFrameTime;
|
||||
}
|
||||
|
||||
int32_t Time::GetDeltaTime() const {
|
||||
return deltaTime;
|
||||
}
|
||||
|
||||
float Time::GetElapsedTime() const {
|
||||
return elapsedTime;
|
||||
}
|
||||
|
||||
int32_t Time::SecondsToFixed(float seconds) const {
|
||||
return static_cast<int32_t>(seconds * FIXED_POINT_SCALE);
|
||||
}
|
||||
32
Engine/time/time_manager.h
Normal file
32
Engine/time/time_manager.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#ifndef TIME_H
|
||||
#define TIME_H
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
class Time {
|
||||
public:
|
||||
Time();
|
||||
|
||||
// Update time and calculate delta time (call each frame)
|
||||
void Update();
|
||||
|
||||
// Get delta time in seconds (fixed-point representation)
|
||||
int32_t GetDeltaTime() const;
|
||||
|
||||
// Get total elapsed time in seconds
|
||||
float GetElapsedTime() const;
|
||||
|
||||
private:
|
||||
// Fixed-point scale for PS1-style precision
|
||||
static constexpr int32_t FIXED_POINT_SCALE = 4096; // 12-bit fractional part
|
||||
|
||||
double lastFrameTime; // Last frame timestamp (in seconds, GLFW time)
|
||||
double currentFrameTime; // Current frame timestamp
|
||||
int32_t deltaTime; // Delta time in fixed-point
|
||||
float elapsedTime; // Total elapsed time in seconds
|
||||
|
||||
// Convert float seconds to fixed-point
|
||||
int32_t SecondsToFixed(float seconds) const;
|
||||
};
|
||||
|
||||
#endif // TIME_H
|
||||
@@ -145,66 +145,6 @@ std::vector<Triangle> create_cube(int size = 4096)
|
||||
return triangles;
|
||||
}
|
||||
|
||||
Matrix4 create_rotation_y(float angle)
|
||||
{
|
||||
Matrix4 mat;
|
||||
int cos_val = static_cast<int>(std::cos(angle) * 4096);
|
||||
int sin_val = static_cast<int>(std::sin(angle) * 4096);
|
||||
|
||||
mat.m[0] = cos_val;
|
||||
mat.m[2] = -sin_val;
|
||||
mat.m[8] = sin_val;
|
||||
mat.m[10] = cos_val;
|
||||
|
||||
return mat;
|
||||
}
|
||||
|
||||
Matrix4 create_rotation_x(float angle)
|
||||
{
|
||||
Matrix4 mat;
|
||||
int cos_val = static_cast<int>(std::cos(angle) * 4096);
|
||||
int sin_val = static_cast<int>(std::sin(angle) * 4096);
|
||||
|
||||
mat.m[5] = cos_val;
|
||||
mat.m[6] = -sin_val;
|
||||
mat.m[9] = sin_val;
|
||||
mat.m[10] = cos_val;
|
||||
|
||||
return mat;
|
||||
}
|
||||
|
||||
Matrix4 create_rotation_z(float angle)
|
||||
{
|
||||
Matrix4 mat;
|
||||
int cos_val = static_cast<int>(std::cos(angle) * 4096);
|
||||
int sin_val = static_cast<int>(std::sin(angle) * 4096);
|
||||
|
||||
mat.m[0] = cos_val;
|
||||
mat.m[1] = -sin_val;
|
||||
mat.m[4] = sin_val;
|
||||
mat.m[5] = cos_val;
|
||||
|
||||
return mat;
|
||||
}
|
||||
|
||||
Matrix4 create_translation(int x, int y, int z)
|
||||
{
|
||||
Matrix4 mat;
|
||||
mat.m[3] = x;
|
||||
mat.m[7] = y;
|
||||
mat.m[11] = z;
|
||||
return mat;
|
||||
}
|
||||
|
||||
Matrix4 create_view_matrix(float camera_distance)
|
||||
{
|
||||
Matrix4 view;
|
||||
|
||||
view.m[11] = -camera_distance * 4096;
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
GLuint create_checkerboard_texture(int width, int height)
|
||||
{
|
||||
unsigned char *data = new unsigned char[width * height * 3];
|
||||
@@ -239,7 +179,6 @@ int main()
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
Renderer renderer(800, 600, "Cube");
|
||||
|
||||
std::vector<Triangle> cube = create_cube(4096 * 4);
|
||||
@@ -247,26 +186,25 @@ int main()
|
||||
GLuint texture = create_checkerboard_texture(128, 128);
|
||||
|
||||
float rotation = 0.0f;
|
||||
float camera_distance = 0.7f;
|
||||
|
||||
auto last_time = std::chrono::high_resolution_clock::now();
|
||||
|
||||
while (!renderer.should_close())
|
||||
{
|
||||
|
||||
auto current_time = std::chrono::high_resolution_clock::now();
|
||||
float delta_time = std::chrono::duration<float>(current_time - last_time).count();
|
||||
last_time = current_time;
|
||||
|
||||
rotation += delta_time * 2.0f;
|
||||
|
||||
Matrix4 view = create_view_matrix(camera_distance);
|
||||
Matrix4 view = Matrix4::perspective(90, 800.0f / 600.0f, 0.1f, 1000.0f);
|
||||
|
||||
Matrix4 rot_x = create_rotation_x(rotation * 0.7f);
|
||||
Matrix4 rot_y = create_rotation_y(rotation);
|
||||
Matrix4 rot_z = create_rotation_z(rotation * 1.3f);
|
||||
int fixed_rotation = static_cast<int>(rotation * FIXED_POINT_PRECISION);
|
||||
Matrix4 rot_x = Matrix4::rotateX(static_cast<int>(rotation * 0.7f * FIXED_POINT_PRECISION));
|
||||
Matrix4 rot_y = Matrix4::rotateY(fixed_rotation);
|
||||
Matrix4 rot_z = Matrix4::rotateZ(static_cast<int>(rotation * 1.3f * FIXED_POINT_PRECISION));
|
||||
|
||||
Matrix4 rotation_matrix = matrix_multiply(rot_z, matrix_multiply(rot_y, rot_x));
|
||||
Matrix4 rotation_matrix = Matrix4::multiply(rot_z, Matrix4::multiply(rot_y, rot_x));
|
||||
|
||||
renderer.set_view_matrix(view);
|
||||
|
||||
@@ -276,9 +214,9 @@ int main()
|
||||
for (const auto &tri : cube)
|
||||
{
|
||||
Triangle t;
|
||||
t.v0 = matrix_multiply(rotation_matrix, tri.v0);
|
||||
t.v1 = matrix_multiply(rotation_matrix, tri.v1);
|
||||
t.v2 = matrix_multiply(rotation_matrix, tri.v2);
|
||||
t.v0 = Matrix4::multiply(rotation_matrix, tri.v0);
|
||||
t.v1 = Matrix4::multiply(rotation_matrix, tri.v1);
|
||||
t.v2 = Matrix4::multiply(rotation_matrix, tri.v2);
|
||||
t.uv0 = tri.uv0;
|
||||
t.uv1 = tri.uv1;
|
||||
t.uv2 = tri.uv2;
|
||||
|
||||
Reference in New Issue
Block a user