diff --git a/assets/shaders/simple.frag b/assets/shaders/simple.frag index 89315ed..a763c51 100644 --- a/assets/shaders/simple.frag +++ b/assets/shaders/simple.frag @@ -1,9 +1,23 @@ #version 330 core - out vec4 FragColor; -uniform vec3 overrideColor; +in vec3 FragPos; +in vec3 Normal; + +uniform vec3 objectColor; +uniform vec3 lightColor; +uniform vec3 lightDir; /* Normalised direction vector for the light source. */ void main() { - FragColor = vec4(overrideColor, 1.0f); + /* Ambient lighting (cheap, constant base light) */ + float ambientStrength = 0.3; + vec3 ambient = ambientStrength * lightColor; + + /* Diffuse lighting (depends on angle of the surface) */ + vec3 norm = normalize(Normal); + float diff = max(dot(norm, normalize(-lightDir)), 0.0); + vec3 diffuse = diff * lightColor; + + vec3 result = (ambient + diffuse) * objectColor; + FragColor = vec4(result, 1.0); } diff --git a/assets/shaders/simple.vert b/assets/shaders/simple.vert index 7f12d3b..f2a6086 100644 --- a/assets/shaders/simple.vert +++ b/assets/shaders/simple.vert @@ -1,11 +1,18 @@ #version 330 core - layout (location = 0) in vec3 aPos; +layout (location = 1) in vec3 aNormal; + +out vec3 FragPos; +out vec3 Normal; uniform mat4 model; uniform mat4 view; uniform mat4 projection; void main() { - gl_Position = projection * view * model * vec4(aPos, 1.0); + /* Pass position and normal vectors to the fragment shader in world space. */ + FragPos = vec3(model * vec4(aPos, 1.0)); + Normal = mat3(transpose(inverse(model))) * aNormal; + + gl_Position = projection * view * vec4(FragPos, 1.0); } diff --git a/src/game/world.cpp b/src/game/world.cpp index 11e0075..04c9df9 100644 --- a/src/game/world.cpp +++ b/src/game/world.cpp @@ -25,8 +25,8 @@ void World::add_chunk(int chunk_x, int chunk_z, const float* heightmap) { } float World::get_height(float world_x, float world_z) const { - float chunk_width = (float)(BettolaLib::Game::CHUNK_WIDTH -1); - float chunk_height = (float)(BettolaLib::Game::CHUNK_HEIGHT-1); + float chunk_width = (float)(BettolaLib::Game::CHUNK_WIDTH - 1); + float chunk_height = (float)(BettolaLib::Game::CHUNK_HEIGHT - 1); int chunk_x = floor(world_x / chunk_width); int chunk_z = floor(world_z / chunk_height); diff --git a/src/game_client.cpp b/src/game_client.cpp index 2887221..3dac4b8 100644 --- a/src/game_client.cpp +++ b/src/game_client.cpp @@ -146,6 +146,7 @@ void GameClient::_process_game_state(const BettolaLib::Network::GameStateMessage BettolaMath::Vec3 cam_front = { input_msg.cam_front_x, input_msg.cam_front_y, input_msg.cam_front_z }; _player.set_velocity_direction(input_state, cam_front); + _player.update(input_msg.dt); } } else { /* Remote player. Find if we already know about them.. */ diff --git a/src/graphics/chunk_mesh.cpp b/src/graphics/chunk_mesh.cpp index 6289eb9..07c62bd 100644 --- a/src/graphics/chunk_mesh.cpp +++ b/src/graphics/chunk_mesh.cpp @@ -1,5 +1,8 @@ #include +#include +#include #include "bettola/game/chunk.h" +#include "bettola/math/vec3.h" #include "chunk_mesh.h" ChunkMesh::ChunkMesh(int chunk_x, int chunk_z, const BettolaLib::Game::Chunk& chunk) { @@ -7,9 +10,15 @@ ChunkMesh::ChunkMesh(int chunk_x, int chunk_z, const BettolaLib::Game::Chunk& ch for(int z = 0; z < BettolaLib::Game::CHUNK_HEIGHT; ++z) { for(int x = 0; x < BettolaLib::Game::CHUNK_WIDTH; ++x) { /* Vertex position. */ - _vertices.push_back((float)(chunk_x * (BettolaLib::Game::CHUNK_WIDTH-1) + x)); + _vertices.push_back((float)(chunk_x * (BettolaLib::Game::CHUNK_WIDTH-1) + x)); _vertices.push_back(chunk.heightmap[z * BettolaLib::Game::CHUNK_WIDTH+x] * 5.0f); /* 5x scale height */ _vertices.push_back((float)(chunk_z * (BettolaLib::Game::CHUNK_HEIGHT-1) + z)); + + /* Vertex normal. */ + BettolaMath::Vec3 normal = _calculate_normal(x, z, chunk); + _vertices.push_back(normal.x); + _vertices.push_back(normal.y); + _vertices.push_back(normal.z); } } @@ -36,6 +45,28 @@ ChunkMesh::ChunkMesh(int chunk_x, int chunk_z, const BettolaLib::Game::Chunk& ch _setup_mesh(); } +BettolaMath::Vec3 ChunkMesh::_calculate_normal(int x, int z, const BettolaLib::Game::Chunk& chunk) { + /* Get heights of adjacent certices. */ + float height_l = chunk.heightmap[z*BettolaLib::Game::CHUNK_WIDTH+(x>0?x-1 : x)] * 5.0f; + float height_r = chunk.heightmap[z*BettolaLib::Game::CHUNK_WIDTH+ + (x0?z-1 : z)*BettolaLib::Game::CHUNK_WIDTH+x]*5.0f; + float height_u = chunk.heightmap[(z 0.0f) { + normal.x /= mag; + normal.y /= mag; + normal.z /= mag; + } + + return normal; +} + ChunkMesh::~ChunkMesh(void) { glDeleteVertexArrays(1, &_vao); glDeleteBuffers(1, &_vbo); @@ -56,9 +87,13 @@ void ChunkMesh::_setup_mesh(void) { glBufferData(GL_ELEMENT_ARRAY_BUFFER, _indices.size()*sizeof(unsigned int), _indices.data(), GL_STATIC_DRAW); /* Position attrib. */ - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float), (void*)0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6*sizeof(float), (void*)0); glEnableVertexAttribArray(0); + /* Normal attrib. */ + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6*sizeof(float), (void*)(3*sizeof(float))); + glEnableVertexAttribArray(1); + glBindVertexArray(0); } diff --git a/src/graphics/chunk_mesh.h b/src/graphics/chunk_mesh.h index da5bd4f..0ca2cd8 100644 --- a/src/graphics/chunk_mesh.h +++ b/src/graphics/chunk_mesh.h @@ -1,8 +1,9 @@ #pragma once #include +#include "bettola/math/vec3.h" -#include "bettola//game/chunk.h" +#include "bettola/game/chunk.h" class ChunkMesh { public: @@ -12,6 +13,7 @@ public: void draw(void); private: + BettolaMath::Vec3 _calculate_normal(int x, int z, const BettolaLib::Game::Chunk& chunk); void _setup_mesh(void); unsigned int _vao, _vbo, _ebo; diff --git a/src/graphics/renderer.cpp b/src/graphics/renderer.cpp index 8806825..d98d5d3 100644 --- a/src/graphics/renderer.cpp +++ b/src/graphics/renderer.cpp @@ -146,47 +146,30 @@ void Renderer::render(const Camera& camera, const Player& player, _shader.use(); GL_CHECK_ERROR(); - BettolaMath::Mat4 view_matrix = camera.get_view_matrix(); + BettolaMath::Mat4 view = camera.get_view_matrix(); BettolaMath::Mat4 projection = BettolaMath::Mat4::perspective((45.0f * M_PI) / 180.0f, 800.0f/600.0f, 0.1f, 100.0f); - GLint view_loc = glGetUniformLocation(_shader.get_id(), "view"); - GLint proj_loc = glGetUniformLocation(_shader.get_id(), "projection"); - GLint model_loc = glGetUniformLocation(_shader.get_id(), "model"); - GLint color_loc = glGetUniformLocation(_shader.get_id(), "overrideColor"); - - glUniformMatrix4fv(view_loc, 1, GL_FALSE, view_matrix.get_ptr()); - GL_CHECK_ERROR(); - glUniformMatrix4fv(proj_loc, 1, GL_FALSE, projection.get_ptr()); - GL_CHECK_ERROR(); - - /* Draw the ground. */ - BettolaMath::Mat4 ground_model = BettolaMath::Mat4::translation(0.0f, 0.0f, 0.0f); - glUniformMatrix4fv(model_loc, 1, GL_FALSE, ground_model.get_ptr()); - glUniform3f(color_loc, 0.2f, 0.4f, 0.2f); - glBindVertexArray(_ground_vao); - glDrawArrays(GL_TRIANGLES, 0, 6); - glBindVertexArray(0); /* WHOOAAA! Got to make sure we unbind the VAO! */ - - /* Set players colour back. */ - glUniform3f(color_loc, 0.2f, 0.5f, 0.8f); + _shader.set_mat4("view", view); + _shader.set_mat4("projection", projection); /* Render world. */ - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); /* Wireframe to see geometry. */ + /* Set lighting uniforms for terrain. */ + _shader.set_vec3("objectColor", { 0.4f, 0.6f, 0.2f}); /* Green. */ + _shader.set_vec3("lightColor", { 1.0f, 1.0f, 1.0f}); + _shader.set_vec3("lightDir", {-0.5f, -1.0f, -0.5f}); /* From above and to the side. */ + for(auto const& [pos, mesh] : world.get_chunk_meshes()) { BettolaMath::Mat4 model; _shader.set_mat4("model", model); mesh->draw(); } - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); /* Reset to default. */ /* Draw the local player's cube. */ + _shader.set_vec3("objectColor", {0.2f, 0.5f, 0.8f}); /* Player colour. */ const auto& player_pos = player.get_position(); BettolaMath::Mat4 trans_matrix = BettolaMath::Mat4::translation(player_pos.x, player_pos.y, player_pos.z); - BettolaMath::Mat4 rot_matrix = BettolaMath::Mat4::rotation(-camera.get_yaw()-90.0f, - {0.0f,1.0f,0.0f}); - BettolaMath::Mat4 model = trans_matrix.multiply(rot_matrix); - glUniformMatrix4fv(model_loc, 1, GL_FALSE, model.get_ptr()); + _shader.set_mat4("model", trans_matrix); GL_CHECK_ERROR(); glBindVertexArray(_vao); @@ -202,7 +185,7 @@ void Renderer::render(const Camera& camera, const Player& player, BettolaMath::Mat4 remote_rot = BettolaMath::Mat4::rotation(-remote_player.get_yaw()-90.0f, {0.0f,1.0f,0.0f}); BettolaMath::Mat4 remote_model = remote_trans.multiply(remote_rot); - glUniformMatrix4fv(model_loc, 1, GL_FALSE, remote_model.get_ptr()); + _shader.set_mat4("model", remote_model); glDrawArrays(GL_TRIANGLES, 0, 36); glBindVertexArray(0); /* Unbind it! */ } diff --git a/src/graphics/shader.h b/src/graphics/shader.h index c9d6d3d..65cd257 100644 --- a/src/graphics/shader.h +++ b/src/graphics/shader.h @@ -1,6 +1,7 @@ #pragma once #include +#include "bettola/math/vec3.h" #include "bettola/math/mat4.h" class Shader { @@ -12,6 +13,7 @@ public: void use(void); void set_mat4(const std::string& name, const BettolaMath::Mat4& matrix) const; + void set_vec3(const std::string& name, const BettolaMath::Vec3& value) const; unsigned int get_id(void) const { return _program_id; } private: bool compile_shader(unsigned int& shader_id, const char* shader_source, int shader_type); diff --git a/src/graphics/shader_uniforms.cpp b/src/graphics/shader_uniforms.cpp index 5f5a88c..a2796a4 100644 --- a/src/graphics/shader_uniforms.cpp +++ b/src/graphics/shader_uniforms.cpp @@ -1,7 +1,12 @@ #include +#include "bettola/math/vec3.h" #include "bettola/math/mat4.h" #include "shader.h" void Shader::set_mat4(const std::string& name, const BettolaMath::Mat4& matrix) const { glUniformMatrix4fv(glGetUniformLocation(_program_id, name.c_str()), 1, GL_FALSE, matrix.get_ptr()); } + +void Shader::set_vec3(const std::string& name, const BettolaMath::Vec3& value) const { + glUniform3f(glGetUniformLocation(_program_id, name.c_str()), value.x, value.y, value.z); +} diff --git a/srv/game/world.cpp b/srv/game/world.cpp index ddf4c5e..3169788 100644 --- a/srv/game/world.cpp +++ b/srv/game/world.cpp @@ -24,8 +24,8 @@ void World::_generate_chunk(BettolaLib::Game::Chunk& chunk, int chunk_x, int chu for(int z = 0; z < BettolaLib::Game::CHUNK_HEIGHT; ++z) { for(int x = 0; x < BettolaLib::Game::CHUNK_WIDTH; ++x) { /* Calculate world coordinates. */ - float world_x = (float)(chunk_x * BettolaLib::Game::CHUNK_WIDTH + x); - float world_z = (float)(chunk_z * BettolaLib::Game::CHUNK_HEIGHT + z); + float world_x = (float)(chunk_x * (BettolaLib::Game::CHUNK_WIDTH - 1) + x); + float world_z = (float)(chunk_z * (BettolaLib::Game::CHUNK_HEIGHT - 1) + z); /* generate noise value. */ chunk.heightmap[z * BettolaLib::Game::CHUNK_WIDTH + x] = m_noise.GetNoise(world_x, world_z); diff --git a/srv/game/world_collision.cpp b/srv/game/world_collision.cpp index e3e3adc..0a0c6bc 100644 --- a/srv/game/world_collision.cpp +++ b/srv/game/world_collision.cpp @@ -8,7 +8,7 @@ float World::get_height(float world_x, float world_z) { * Mesh generation uses (WIDTH-1), so account for it * when calculating the chunk and local coords. */ - float chunk_width = (float)(BettolaLib::Game::CHUNK_WIDTH-1); + float chunk_width = (float)(BettolaLib::Game::CHUNK_WIDTH -1); float chunk_height = (float)(BettolaLib::Game::CHUNK_HEIGHT-1); int chunk_x = floor(world_x / chunk_width);