Files
utad-3d/src/engine.cpp
2025-10-14 11:45:46 +02:00

284 lines
7.4 KiB
C++

#include "engine.h"
#include <vector>
#include "../lib/glew/GL/glew.h"
#include "../lib/glfw/glfw3.h"
#include "../lib/glm/glm.hpp"
#include "../lib/glm/gtc/matrix_transform.hpp"
#include "buffer.h"
#include "camera.h"
#include "light.h"
#include "logger.h"
#include "material.h"
#include "mesh.h"
#include "model.h"
#include "shader.h"
#include "state.h"
#include "texture.h"
#include "vertex.h"
#include "world.h"
// Instance variables
std::shared_ptr<Light> point_light;
// Constants
constexpr int screen_width = 800;
constexpr int screen_height = 600;
constexpr double fps_limit = 1.0 / 60.0;
Engine::Engine()
: window_(nullptr)
, screen_width_(screen_width)
, screen_height_(screen_height)
, last_update_time_(0.0)
, last_frame_time_(0.0)
, angle_(0.0)
{
Logger::info("Engine created");
}
Engine::~Engine()
{
Logger::info("Engine destroyed");
destroy();
}
void Engine::initialize()
{
Logger::info("Initializing engine...");
// Initialize GLFW
if (!glfwInit()) {
Logger::error("Failed to initialize GLFW");
return;
}
Logger::info("GLFW initialized successfully");
glfwWindowHint(GLFW_RESIZABLE, false);
glfwWindowHint(GLFW_SAMPLES, 8);
// Create window
window_ = glfwCreateWindow(screen_width_, screen_height_,
"Daniel Poveda", nullptr, nullptr);
if (window_ == nullptr) {
Logger::error("Failed to create OpenGL window");
glfwTerminate();
return;
}
glfwMakeContextCurrent(window_);
// Enable OpenGL features
glEnable(GL_DEPTH_TEST);
glEnable(GL_SCISSOR_TEST);
Logger::info(
sstr("OpenGL initialized, version: ",
reinterpret_cast<const char*>(glGetString(GL_VERSION))));
// Initialize GLEW
glewExperimental = GL_TRUE;
GLenum err = glewInit();
if (err != GLEW_OK) {
Logger::error(sstr(
"Failed to initialize GLEW: ",
reinterpret_cast<const char*>(glewGetErrorString(err))));
glfwTerminate();
return;
}
// Initialize default shader
Logger::info("Loading default shaders...");
state::default_shader = std::make_shared<Shader>(
"data/shaders/vertex.glsl", "data/shaders/fragment.glsl");
if (std::strlen(state::default_shader->error()) > 0) {
Logger::error(sstr("Failed to initialize shaders: ",
state::default_shader->error()));
} else {
Logger::info("Default shaders loaded successfully");
}
Logger::info("Engine initialization complete");
}
void Engine::run()
{
Logger::info("Starting game loop...");
setup();
start();
last_update_time_ = glfwGetTime();
last_frame_time_ = glfwGetTime();
while (is_running()) {
const double now = glfwGetTime();
const double delta_time = now - last_update_time_;
process_input(delta_time);
update(delta_time);
if (now - last_frame_time_ >= fps_limit) {
render();
last_frame_time_ = now;
}
last_update_time_ = now;
}
Logger::info("Game loop ended");
}
void Engine::destroy()
{
if (window_) {
Logger::info("Shutting down engine...");
glfwTerminate();
window_ = nullptr;
Logger::info("Engine shutdown complete");
}
}
void Engine::setup()
{
Logger::info("Setting up scene...");
// Create world
world_ = std::make_unique<World>();
world_->set_ambient(glm::vec3(0.2f, 0.2f, 0.2f));
Logger::info("World created");
// Create camera
camera_ = std::make_shared<Camera>();
camera_->set_position(glm::vec3(0.0f, 4.0f, 12.0f));
camera_->set_rotation(glm::vec3(glm::radians(-20.0f), 0.0f, 0.0f));
camera_->set_projection(
glm::perspective(glm::radians(45.0f),
static_cast<float>(screen_width_)
/ static_cast<float>(screen_height_),
0.1f, 100.0f));
camera_->set_viewport(glm::ivec4(0, 0, screen_width_, screen_height_));
camera_->set_clear_color(glm::vec3(0.1f, 0.1f, 0.1f));
world_->add_entity(camera_);
Logger::info("Camera created and added to world");
// 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, -1.25f, 0.0f));
models_.push_back(box_stack_model);
world_->add_entity(box_stack_model);
Logger::info("Box stack model loaded and added to world");
}
// 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_rotation(
glm::vec3(0.0f, glm::radians(-30.0f), 0.0f));
gunslinger_model->set_scale(glm::vec3(0.1f, 0.1f, 0.1f));
models_.push_back(gunslinger_model);
world_->add_entity(gunslinger_model);
Logger::info("Gunslinger model loaded and added to world");
}
// Create directional light
auto directional_light = std::make_shared<Light>();
directional_light->set_type(Light::Type::DIRECTIONAL);
directional_light->set_color(glm::vec3(1.0f, 1.0f, 1.0f));
directional_light->set_intensity(1.0f);
directional_light->set_position(glm::normalize(
glm::vec3(1.0f, 1.0f, 1.0f))); // Use position as direction
world_->add_entity(directional_light);
Logger::info("Directional light created and added to world");
// Create point light
point_light = std::make_shared<Light>();
point_light->set_type(Light::Type::POINT);
point_light->set_color(glm::vec3(1.0f, 0.0f, 0.0f));
point_light->set_intensity(2.0f);
point_light->set_linear_attenuation(0.2f);
point_light->set_position(glm::vec3(5.0f, 0.0f, 0.0f));
world_->add_entity(point_light);
Logger::info("Point light created and added to world");
Logger::info("Scene setup complete");
}
void Engine::start()
{
// Called once after setup, before the main loop
// Can be used for initialization that needs the scene to be ready
}
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 orbiting light
angle_ += delta_time;
// Make point light orbit around the model
if (point_light) {
constexpr float radius = 7.5f;
point_light->set_position(glm::vec3(radius * cos(angle_), 2.0f,
radius * sin(angle_)));
}
// Update world
world_->update(static_cast<float>(delta_time));
}
void Engine::render()
{
// Draw world
world_->draw();
// Swap buffers
glfwSwapBuffers(window_);
}
bool Engine::is_running() const
{
return window_ && !glfwWindowShouldClose(window_)
&& !glfwGetKey(window_, GLFW_KEY_ESCAPE);
}