feat: add lights
This commit is contained in:
		| @@ -9,6 +9,7 @@ | ||||
|  | ||||
| #include "buffer.h" | ||||
| #include "camera.h" | ||||
| #include "light.h" | ||||
| #include "logger.h" | ||||
| #include "material.h" | ||||
| #include "mesh.h" | ||||
| @@ -19,6 +20,10 @@ | ||||
| #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; | ||||
| @@ -140,6 +145,7 @@ void Engine::setup() | ||||
|  | ||||
| 	// Create world | ||||
| 	world_ = std::make_unique<World>(); | ||||
| 	world_->set_ambient(glm::vec3(0.2f, 0.2f, 0.2f)); | ||||
| 	Logger::info("World created"); | ||||
|  | ||||
| 	// Create camera | ||||
| @@ -180,6 +186,26 @@ void Engine::setup() | ||||
| 		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"); | ||||
| } | ||||
|  | ||||
| @@ -227,6 +253,16 @@ void Engine::process_input(const double delta_time) | ||||
|  | ||||
| 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)); | ||||
| } | ||||
|   | ||||
							
								
								
									
										56
									
								
								src/light.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								src/light.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,56 @@ | ||||
| #include "light.h" | ||||
|  | ||||
| #include "../lib/glm/gtc/matrix_transform.hpp" | ||||
|  | ||||
| #include "shader.h" | ||||
| #include "state.h" | ||||
|  | ||||
| Light::Light() | ||||
|     : Entity() | ||||
|     , type_(Type::DIRECTIONAL) | ||||
|     , color_(1.0f, 1.0f, 1.0f) | ||||
|     , intensity_(1.0f) | ||||
|     , linear_attenuation_(0.0f) | ||||
| { | ||||
| } | ||||
|  | ||||
| void Light::prepare(int index, std::shared_ptr<Shader>& shader) const | ||||
| { | ||||
| 	if (!shader) | ||||
| 		return; | ||||
|  | ||||
| 	// Build uniform names for this light index | ||||
| 	std::string light_vector = | ||||
| 	    "lights[" + std::to_string(index) + "].vector"; | ||||
| 	std::string light_color = "lights[" + std::to_string(index) + "].color"; | ||||
| 	std::string light_intensity = | ||||
| 	    "lights[" + std::to_string(index) + "].intensity"; | ||||
| 	std::string light_linear_att = | ||||
| 	    "lights[" + std::to_string(index) + "].linear_att"; | ||||
|  | ||||
| 	// Get uniform locations | ||||
| 	int vector_loc	   = shader->uniform_location(light_vector.c_str()); | ||||
| 	int color_loc	   = shader->uniform_location(light_color.c_str()); | ||||
| 	int intensity_loc  = shader->uniform_location(light_intensity.c_str()); | ||||
| 	int linear_att_loc = shader->uniform_location(light_linear_att.c_str()); | ||||
|  | ||||
| 	// Calculate light vector in view space | ||||
| 	glm::vec4 light_vector_view; | ||||
| 	if (type_ == Type::DIRECTIONAL) { | ||||
| 		// For directional lights, w = 0 | ||||
| 		glm::vec3 direction = | ||||
| 		    glm::normalize(position_); // Use position as direction | ||||
| 		light_vector_view = | ||||
| 		    state::view_matrix * glm::vec4(direction, 0.0f); | ||||
| 	} else { | ||||
| 		// For point lights, w = 1 | ||||
| 		light_vector_view = | ||||
| 		    state::view_matrix * glm::vec4(position_, 1.0f); | ||||
| 	} | ||||
|  | ||||
| 	// Set uniforms | ||||
| 	Shader::set_vec4(vector_loc, light_vector_view); | ||||
| 	Shader::set_vec3(color_loc, color_); | ||||
| 	Shader::set_float(intensity_loc, intensity_); | ||||
| 	Shader::set_float(linear_att_loc, linear_attenuation_); | ||||
| } | ||||
							
								
								
									
										67
									
								
								src/light.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								src/light.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,67 @@ | ||||
| #ifndef LIGHT_H_ | ||||
| #define LIGHT_H_ | ||||
|  | ||||
| #include <memory> | ||||
|  | ||||
| #include "../lib/glm/glm.hpp" | ||||
|  | ||||
| #include "entity.h" | ||||
|  | ||||
| class Shader; | ||||
|  | ||||
| class Light : public Entity { | ||||
| public: | ||||
| 	enum class Type { | ||||
| 		DIRECTIONAL, | ||||
| 		POINT | ||||
| 	}; | ||||
|  | ||||
| 	Light(); | ||||
| 	~Light() override = default; | ||||
|  | ||||
| 	[[nodiscard]] Type type() const | ||||
| 	{ | ||||
| 		return type_; | ||||
| 	} | ||||
| 	void set_type(Type type) | ||||
| 	{ | ||||
| 		type_ = type; | ||||
| 	} | ||||
|  | ||||
| 	[[nodiscard]] const glm::vec3& color() const | ||||
| 	{ | ||||
| 		return color_; | ||||
| 	} | ||||
| 	void set_color(const glm::vec3& color) | ||||
| 	{ | ||||
| 		color_ = color; | ||||
| 	} | ||||
|  | ||||
| 	[[nodiscard]] float intensity() const | ||||
| 	{ | ||||
| 		return intensity_; | ||||
| 	} | ||||
| 	void set_intensity(float intensity) | ||||
| 	{ | ||||
| 		intensity_ = intensity; | ||||
| 	} | ||||
|  | ||||
| 	[[nodiscard]] float linear_attenuation() const | ||||
| 	{ | ||||
| 		return linear_attenuation_; | ||||
| 	} | ||||
| 	void set_linear_attenuation(float att) | ||||
| 	{ | ||||
| 		linear_attenuation_ = att; | ||||
| 	} | ||||
|  | ||||
| 	void prepare(int index, std::shared_ptr<Shader>& shader) const; | ||||
|  | ||||
| private: | ||||
| 	Type	  type_; | ||||
| 	glm::vec3 color_; | ||||
| 	float	  intensity_; | ||||
| 	float	  linear_attenuation_; | ||||
| }; | ||||
|  | ||||
| #endif // LIGHT_H_ | ||||
| @@ -1,14 +1,19 @@ | ||||
| #include "material.h" | ||||
|  | ||||
| #include "../lib/glm/glm.hpp" | ||||
| #include "../lib/glm/gtc/matrix_transform.hpp" | ||||
|  | ||||
| #include "light.h" | ||||
| #include "shader.h" | ||||
| #include "state.h" | ||||
| #include "texture.h" | ||||
|  | ||||
| Material::Material(const std::shared_ptr<Texture>& tex, | ||||
| 		   const std::shared_ptr<Shader>&  shader) | ||||
|     : shader_(shader), texture_(tex) | ||||
|     : shader_(shader) | ||||
|     , texture_(tex) | ||||
|     , color_(1.0f, 1.0f, 1.0f, 1.0f) | ||||
|     , shininess_(0) | ||||
| { | ||||
| } | ||||
|  | ||||
| @@ -42,6 +47,39 @@ void Material::prepare() | ||||
| 	const int mvp_loc = active_shader->uniform_location("mvp"); | ||||
| 	Shader::set_mat4(mvp_loc, mvp); | ||||
|  | ||||
| 	// Set ModelView matrix | ||||
| 	const glm::mat4 model_view = state::view_matrix * state::model_matrix; | ||||
| 	const int model_view_loc = active_shader->uniform_location("modelView"); | ||||
| 	Shader::set_mat4(model_view_loc, model_view); | ||||
|  | ||||
| 	// Set Normal matrix (transpose of inverse of ModelView) | ||||
| 	const glm::mat4 normal_matrix = | ||||
| 	    glm::transpose(glm::inverse(model_view)); | ||||
| 	const int normal_matrix_loc = | ||||
| 	    active_shader->uniform_location("normalMatrix"); | ||||
| 	Shader::set_mat4(normal_matrix_loc, normal_matrix); | ||||
|  | ||||
| 	// Set material properties | ||||
| 	const int material_color_loc = | ||||
| 	    active_shader->uniform_location("materialColor"); | ||||
| 	Shader::set_vec4(material_color_loc, color_); | ||||
|  | ||||
| 	const int shininess_loc = active_shader->uniform_location("shininess"); | ||||
| 	Shader::set_float(shininess_loc, static_cast<float>(shininess_)); | ||||
|  | ||||
| 	// Set ambient light | ||||
| 	const int ambient_loc = active_shader->uniform_location("ambientLight"); | ||||
| 	Shader::set_vec3(ambient_loc, state::ambient); | ||||
|  | ||||
| 	// Set number of lights | ||||
| 	const int num_lights_loc = active_shader->uniform_location("numLights"); | ||||
| 	Shader::set_int(num_lights_loc, static_cast<int>(state::lights.size())); | ||||
|  | ||||
| 	// Prepare each light | ||||
| 	for (size_t i = 0; i < state::lights.size(); ++i) { | ||||
| 		state::lights[i]->prepare(static_cast<int>(i), active_shader); | ||||
| 	} | ||||
|  | ||||
| 	// Set texture-related uniforms | ||||
| 	const int use_texture_loc = | ||||
| 	    active_shader->uniform_location("useTexture"); | ||||
|   | ||||
| @@ -3,6 +3,8 @@ | ||||
|  | ||||
| #include <memory> | ||||
|  | ||||
| #include "../lib/glm/glm.hpp" | ||||
|  | ||||
| class Shader; | ||||
| class Texture; | ||||
|  | ||||
| @@ -25,11 +27,31 @@ public: | ||||
| 		texture_ = tex; | ||||
| 	} | ||||
|  | ||||
| 	[[nodiscard]] const glm::vec4& color() const | ||||
| 	{ | ||||
| 		return color_; | ||||
| 	} | ||||
| 	void set_color(const glm::vec4& color) | ||||
| 	{ | ||||
| 		color_ = color; | ||||
| 	} | ||||
|  | ||||
| 	[[nodiscard]] uint8_t shininess() const | ||||
| 	{ | ||||
| 		return shininess_; | ||||
| 	} | ||||
| 	void set_shininess(uint8_t shininess) | ||||
| 	{ | ||||
| 		shininess_ = shininess; | ||||
| 	} | ||||
|  | ||||
| 	void prepare(); | ||||
|  | ||||
| private: | ||||
| 	std::shared_ptr<Shader>	 shader_; | ||||
| 	std::shared_ptr<Texture> texture_; | ||||
| 	glm::vec4		 color_; | ||||
| 	uint8_t			 shininess_; | ||||
| }; | ||||
|  | ||||
| #endif // MATERIAL_H_ | ||||
|   | ||||
| @@ -46,6 +46,14 @@ Vertex create_vertex(const tinyobj::attrib_t& attrib, | ||||
| 		vertex.tex_coord.y = attrib.texcoords[2 * idx.texcoord_index + 1]; | ||||
| 	} | ||||
|  | ||||
| 	// Normal | ||||
| 	vertex.normal = glm::vec3(0.0f, 0.0f, 1.0f); | ||||
| 	if (idx.normal_index >= 0) { | ||||
| 		vertex.normal.x = attrib.normals[3 * idx.normal_index + 0]; | ||||
| 		vertex.normal.y = attrib.normals[3 * idx.normal_index + 1]; | ||||
| 		vertex.normal.z = attrib.normals[3 * idx.normal_index + 2]; | ||||
| 	} | ||||
|  | ||||
| 	return vertex; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -73,6 +73,14 @@ void Shader::setup_attribs() const | ||||
| 		    loc, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), | ||||
| 		    reinterpret_cast<const void*>(offsetof(Vertex, tex_coord))); | ||||
| 	} | ||||
|  | ||||
| 	loc = glGetAttribLocation(program_id_, "vnormal"); | ||||
| 	if (loc != -1) { | ||||
| 		glEnableVertexAttribArray(loc); | ||||
| 		glVertexAttribPointer( | ||||
| 		    loc, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), | ||||
| 		    reinterpret_cast<const void*>(offsetof(Vertex, normal))); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| int Shader::uniform_location(const char* key) const | ||||
|   | ||||
							
								
								
									
										12
									
								
								src/state.h
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								src/state.h
									
									
									
									
									
								
							| @@ -2,16 +2,20 @@ | ||||
| #define STATE_H_ | ||||
|  | ||||
| #include <memory> | ||||
| #include <vector> | ||||
| #include "../lib/glm/glm.hpp" | ||||
|  | ||||
| class Shader; | ||||
| class Light; | ||||
|  | ||||
| namespace state { | ||||
|  | ||||
| inline std::shared_ptr<Shader> default_shader	 = nullptr; | ||||
| inline glm::mat4	       projection_matrix = glm::mat4(1.0f); | ||||
| inline glm::mat4	       view_matrix	 = glm::mat4(1.0f); | ||||
| inline glm::mat4	       model_matrix	 = glm::mat4(1.0f); | ||||
| inline std::shared_ptr<Shader>		   default_shader    = nullptr; | ||||
| inline glm::mat4			   projection_matrix = glm::mat4(1.0f); | ||||
| inline glm::mat4			   view_matrix	     = glm::mat4(1.0f); | ||||
| inline glm::mat4			   model_matrix	     = glm::mat4(1.0f); | ||||
| inline std::vector<std::shared_ptr<Light>> lights; | ||||
| inline glm::vec3 ambient = glm::vec3(0.0f, 0.0f, 0.0f); | ||||
|  | ||||
| } // namespace state | ||||
|  | ||||
|   | ||||
| @@ -7,6 +7,7 @@ struct Vertex { | ||||
| 	glm::vec3 position{0.0f, 0.0f, 0.0f}; | ||||
| 	glm::vec3 color{0.0f, 0.0f, 0.0f}; | ||||
| 	glm::vec2 tex_coord{0.0f, 0.0f}; | ||||
| 	glm::vec3 normal{0.0f, 0.0f, 0.0f}; | ||||
| }; | ||||
|  | ||||
| #endif // VERTEX_H_ | ||||
|   | ||||
| @@ -4,7 +4,9 @@ | ||||
|  | ||||
| #include "camera.h" | ||||
| #include "entity.h" | ||||
| #include "light.h" | ||||
| #include "logger.h" | ||||
| #include "state.h" | ||||
|  | ||||
| void World::add_entity(const std::shared_ptr<Entity>& entity) | ||||
| { | ||||
| @@ -17,10 +19,20 @@ void World::add_entity(const std::shared_ptr<Entity>& entity) | ||||
| 		cameras_.push_back(camera); | ||||
| 		Logger::info(sstr("Camera added to world (total cameras: ", | ||||
| 				  cameras_.size(), ")")); | ||||
| 	} else { | ||||
| 		Logger::info(sstr("Entity added to world (total entities: ", | ||||
| 				  entities_.size(), ")")); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	// Check if entity is a light | ||||
| 	std::shared_ptr<Light> light = std::dynamic_pointer_cast<Light>(entity); | ||||
| 	if (light) { | ||||
| 		lights_.push_back(light); | ||||
| 		Logger::info(sstr("Light added to world (total lights: ", | ||||
| 				  lights_.size(), ")")); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	Logger::info(sstr( | ||||
| 	    "Entity added to world (total entities: ", entities_.size(), ")")); | ||||
| } | ||||
|  | ||||
| void World::remove_entity(const std::shared_ptr<Entity>& entity) | ||||
| @@ -43,6 +55,16 @@ void World::remove_entity(const std::shared_ptr<Entity>& entity) | ||||
| 			Logger::info("Camera removed from world"); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Check if entity is a light and remove from lights list | ||||
| 	std::shared_ptr<Light> light = std::dynamic_pointer_cast<Light>(entity); | ||||
| 	if (light) { | ||||
| 		auto lightIt = std::find(lights_.begin(), lights_.end(), light); | ||||
| 		if (lightIt != lights_.end()) { | ||||
| 			lights_.erase(lightIt); | ||||
| 			Logger::info("Light removed from world"); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void World::update(float delta_time) | ||||
| @@ -54,6 +76,10 @@ void World::update(float delta_time) | ||||
|  | ||||
| void World::draw() | ||||
| { | ||||
| 	// Update state with world lighting information | ||||
| 	state::ambient = ambient_; | ||||
| 	state::lights  = lights_; | ||||
|  | ||||
| 	// Draw for each camera | ||||
| 	for (auto& camera : cameras_) { | ||||
| 		// Prepare the camera (sets viewport, projection, view, clears | ||||
|   | ||||
							
								
								
									
										14
									
								
								src/world.h
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								src/world.h
									
									
									
									
									
								
							| @@ -4,8 +4,11 @@ | ||||
| #include <memory> | ||||
| #include <vector> | ||||
|  | ||||
| #include "../lib/glm/glm.hpp" | ||||
|  | ||||
| class Entity; | ||||
| class Camera; | ||||
| class Light; | ||||
|  | ||||
| class World { | ||||
| public: | ||||
| @@ -27,12 +30,23 @@ public: | ||||
| 		return entities_[index]; | ||||
| 	} | ||||
|  | ||||
| 	[[nodiscard]] const glm::vec3& ambient() const | ||||
| 	{ | ||||
| 		return ambient_; | ||||
| 	} | ||||
| 	void set_ambient(const glm::vec3& ambient) | ||||
| 	{ | ||||
| 		ambient_ = ambient; | ||||
| 	} | ||||
|  | ||||
| 	void update(float delta_time); | ||||
| 	void draw(); | ||||
|  | ||||
| private: | ||||
| 	std::vector<std::shared_ptr<Entity>> entities_; | ||||
| 	std::vector<std::shared_ptr<Camera>> cameras_; | ||||
| 	std::vector<std::shared_ptr<Light>>  lights_; | ||||
| 	glm::vec3			     ambient_{0.0f, 0.0f, 0.0f}; | ||||
| }; | ||||
|  | ||||
| #endif // WORLD_H_ | ||||
|   | ||||
		Reference in New Issue
	
	Block a user