284 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			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);
 | |
| }
 |