From e208f496746648db5b034c587e18240dc4ed9982 Mon Sep 17 00:00:00 2001 From: Ritchie Cunningham Date: Sun, 14 Sep 2025 20:26:37 +0100 Subject: [PATCH] [Add] Gone with 3D renderer instead. added z coord. --- assets/shaders/simple.frag | 2 +- assets/shaders/simple.vert | 5 +- libbettola/include/bettola/math/mat4.h | 4 + libbettola/include/bettola/math/vec3.h | 9 ++ libbettola/src/bettola/math/mat4.cpp | 142 ++++++++++++++++----- src/bettola.cpp | 18 ++- src/bettola.h | 2 - src/game_client.h | 1 + src/graphics/renderer.cpp | 169 ++++++++++++++++--------- src/graphics/renderer.h | 1 - srv/game/game.h | 1 - 11 files changed, 246 insertions(+), 108 deletions(-) create mode 100644 libbettola/include/bettola/math/vec3.h diff --git a/assets/shaders/simple.frag b/assets/shaders/simple.frag index e1c3b40..cd30975 100644 --- a/assets/shaders/simple.frag +++ b/assets/shaders/simple.frag @@ -3,5 +3,5 @@ out vec4 FragColor; void main() { - FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f); /* Orange??!? */ + FragColor = vec4(0.2f, 0.5f, 0.8f, 1.0f); } diff --git a/assets/shaders/simple.vert b/assets/shaders/simple.vert index aa00079..7f12d3b 100644 --- a/assets/shaders/simple.vert +++ b/assets/shaders/simple.vert @@ -2,9 +2,10 @@ layout (location = 0) in vec3 aPos; -uniform mat4 projection; uniform mat4 model; +uniform mat4 view; +uniform mat4 projection; void main() { - gl_Position = projection * model * vec4(aPos, 1.0); + gl_Position = projection * view * model * vec4(aPos, 1.0); } diff --git a/libbettola/include/bettola/math/mat4.h b/libbettola/include/bettola/math/mat4.h index 88ba556..85b655b 100644 --- a/libbettola/include/bettola/math/mat4.h +++ b/libbettola/include/bettola/math/mat4.h @@ -1,4 +1,5 @@ #pragma once +#include "vec3.h" namespace BettolaMath { struct Mat4 { @@ -10,6 +11,9 @@ struct Mat4 { static Mat4 orthographic(float left, float right, float bottom, float top, float near, float far); static Mat4 translation(float x, float y, float z); static Mat4 scale(float x, float y, float z); + static Mat4 rotation(float angle, const Vec3& axis); + static Mat4 perspective(float fov_y, float aspect, float near, float far); + static Mat4 look_at(const Vec3& pos, const Vec3& target, const Vec3& up); const float* get_ptr(void) const { return elements; } }; diff --git a/libbettola/include/bettola/math/vec3.h b/libbettola/include/bettola/math/vec3.h new file mode 100644 index 0000000..1b7b419 --- /dev/null +++ b/libbettola/include/bettola/math/vec3.h @@ -0,0 +1,9 @@ +#pragma once + +namespace BettolaMath { + +struct Vec3 { + float x, y , z; +}; + +} /* namespace BettolaMath. */ diff --git a/libbettola/src/bettola/math/mat4.cpp b/libbettola/src/bettola/math/mat4.cpp index e03fd7b..dd52b5f 100644 --- a/libbettola/src/bettola/math/mat4.cpp +++ b/libbettola/src/bettola/math/mat4.cpp @@ -1,66 +1,140 @@ #include -#include - +#include #include "math/mat4.h" +#include "math/vec3.h" namespace BettolaMath { Mat4::Mat4(void) { - memset(elements, 0, 16*sizeof(float)); - - elements[0 + 0 * 4] = 1.0f; - elements[1 + 1 * 4] = 1.0f; - elements[2 + 2 * 4] = 1.0f; - elements[3 + 3 * 4] = 1.0f; + memset(elements, 0, 16 * sizeof(float)); + elements[0] = 1.0f; + elements[5] = 1.0f; + elements[10] = 1.0f; + elements[15] = 1.0f; } Mat4& Mat4::multiply(const Mat4& other) { float data[16]; - for(int y = 0; y < 4; y++) { - for(int x = 0; x < 4; x++) { + for(int col = 0; col < 4; col++) { + for(int row = 0; row < 4; row++) { float sum = 0.0f; - for(int e = 0; e < 4; e++) { - sum += elements[x+e*4] * other.elements[e+y*4]; + for(int i = 0; i < 4; i++) { + sum += elements[row + i * 4] * other.elements[i + col * 4]; } - data[x+y*4] = sum; + data[row + col * 4] = sum; } } - memcpy(elements, data, 16*sizeof(float)); + memcpy(elements, data, 16 * sizeof(float)); return *this; } Mat4 Mat4::orthographic(float left, float right, float bottom, float top, float near, float far) { Mat4 result; - - result.elements[0 + 0 * 4] = 2.0f / (right-left); - result.elements[1 + 1 * 4] = 2.0f / (top-bottom); - result.elements[2 + 2 * 4] = -2.0f / (far-near); - - result.elements[0 + 3 * 4] = -(right+left) / (right-left); - result.elements[1 + 3 * 4] = -(top+bottom) / (top-bottom); - result.elements[2 + 3 * 4] = -(far+near) / (far-near); - + result.elements[0] = 2.0f / (right - left); + result.elements[5] = 2.0f / (top - bottom); + result.elements[10] = -2.0f / (far - near); + result.elements[12] = -(right + left) / (right - left); + result.elements[13] = -(top + bottom) / (top - bottom); + result.elements[14] = -(far + near) / (far - near); return result; } Mat4 Mat4::translation(float x, float y, float z) { - Mat4 result; /* Starts as identity. */ - - result.elements[0 + 3 * 4] = x; - result.elements[1 + 3 * 4] = y; - result.elements[2 + 3 * 4] = z; - + Mat4 result; + result.elements[12] = x; + result.elements[13] = y; + result.elements[14] = z; return result; } Mat4 Mat4::scale(float x, float y, float z) { - Mat4 result; /* Starts as identity. */ + Mat4 result; + result.elements[0] = x; + result.elements[5] = y; + result.elements[10] = z; + return result; +} - result.elements[0 + 0 * 4] = x; - result.elements[1 + 1 * 4] = y; - result.elements[2 + 2 * 4] = z; +Mat4 Mat4::rotation(float angle, const Vec3& axis) { + Mat4 result; + float r = angle * M_PI / 180.0f; /* Convert to rads. */ + float c = cos(r); + float s = sin(r); + float omc = 1.0f - c; + + float x = axis.x; + float y = axis.y; + float z = axis.z; + + result.elements[0] = x * x * omc + c; + result.elements[1] = y * x * omc + z * s; + result.elements[2] = x * z * omc - y * s; + + result.elements[4] = x * y * omc - z * s; + result.elements[5] = y * y * omc + c; + result.elements[6] = y * z * omc + x * s; + + result.elements[8] = x * z * omc + y * s; + result.elements[9] = y * z * omc - x * s; + result.elements[10] = z * z * omc + c; + + return result; +} + +Mat4 Mat4::perspective(float fov_y, float aspect, float near, float far) { + Mat4 result; + float const tan_half_fov = tan(fov_y / 2.0f); + result.elements[0] = 1.0f / (aspect * tan_half_fov); + result.elements[5] = 1.0f / tan_half_fov; + result.elements[10] = -(far + near) / (far - near); + result.elements[11] = -1.0f; + result.elements[14] = -(2.0f * far * near) / (far - near); + result.elements[15] = 0.0f; + return result; +} + +Mat4 Mat4::look_at(const Vec3& pos, const Vec3& target, const Vec3& up) { + Vec3 f; + f.x = target.x - pos.x; + f.y = target.y - pos.y; + f.z = target.z - pos.z; + float f_mag = sqrt(f.x*f.x + f.y*f.y + f.z*f.z); + f.x /= f_mag; f.y /= f_mag; f.z /= f_mag; + + Vec3 s; + s.x = f.y * up.z - f.z * up.y; + s.y = f.z * up.x - f.x * up.z; + s.z = f.x * up.y - f.y * up.x; + float s_mag = sqrt(s.x*s.x + s.y*s.y + s.z*s.z); + s.x /= s_mag; s.y /= s_mag; s.z /= s_mag; + + Vec3 u; + u.x = s.y * f.z - s.z * f.y; + u.y = s.z * f.x - s.x * f.z; + u.z = s.x * f.y - s.y * f.x; + + Mat4 result; + result.elements[0] = s.x; + result.elements[1] = u.x; + result.elements[2] = -f.x; + result.elements[3] = 0.0f; + + result.elements[4] = s.y; + result.elements[5] = u.y; + result.elements[6] = -f.y; + result.elements[7] = 0.0f; + + result.elements[8] = s.z; + result.elements[9] = u.z; + result.elements[10] = -f.z; + result.elements[11] = 0.0f; + + result.elements[12] = -(s.x * pos.x + s.y * pos.y + s.z * pos.z); + result.elements[13] = -(u.x * pos.x + u.y * pos.y + u.z * pos.z); + result.elements[14] = (f.x * pos.x + f.y * pos.y + f.z * pos.z); + result.elements[15] = 1.0f; return result; } -} /* namespace BettolaMath. */ +} // namespace BettolaMath diff --git a/src/bettola.cpp b/src/bettola.cpp index cc3aabd..b24c892 100644 --- a/src/bettola.cpp +++ b/src/bettola.cpp @@ -97,19 +97,17 @@ void Bettola::process_events(void) { } void Bettola::update(double dt) { + /* Set local player's velocity for client-side prediction. */ + float dir_x = (_input.right ? 1.0f : 0.0f) - (_input.left ? 1.0f : 0.0f); + float dir_y = (_input.down ? 1.0f : 0.0f) - (_input.up ? 1.0f : 0.0f); + _game_client.get_player_for_write().set_velocity_direction(dir_x, dir_y); + + /* Process network messages and send input to the server. */ _game_client.process_network_messages(); _game_client.send_input(_input, dt); + + /* Update all players (local prediction and remote interpolation). */ _game_client.update_players(dt); - - static char window_title[256]; - static double time_since_title_update = 0.0; - - time_since_title_update += dt; - if(time_since_title_update > 0.25) { - snprintf(window_title, 256, "Bettola - FPS %.2f", 1.0/dt); - SDL_SetWindowTitle(_window, window_title); - time_since_title_update = 0.0; - } } void Bettola::render(void) { diff --git a/src/bettola.h b/src/bettola.h index 14e9542..e7fed76 100644 --- a/src/bettola.h +++ b/src/bettola.h @@ -1,9 +1,7 @@ #pragma once #include -#include #include -#include #include "graphics/renderer.h" #include "game_client.h" diff --git a/src/game_client.h b/src/game_client.h index fe10a15..53a96b3 100644 --- a/src/game_client.h +++ b/src/game_client.h @@ -20,6 +20,7 @@ public: void send_input(const Player::InputState& input, float dt); const Player& get_player(void) const { return _player; } + Player& get_player_for_write(void) { return _player; } const std::vector& get_remote_players(void) const { return _remote_players; } void update_players(float dt); diff --git a/src/graphics/renderer.cpp b/src/graphics/renderer.cpp index 8507c4f..0e936ff 100644 --- a/src/graphics/renderer.cpp +++ b/src/graphics/renderer.cpp @@ -1,6 +1,24 @@ #include #include +#include +#include + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + #include "renderer.h" +#include "math/mat4.h" +#include "math/vec3.h" /* Going to need this for the camera. */ + +#define GL_CHECK_ERROR() \ + do { \ + GLenum err = glGetError(); \ + if(err != GL_NO_ERROR) { \ + fprintf(stderr, "OpenGL error at %s:%d: %s\n", __FILE__, __LINE__, \ + (const char*)glewGetErrorString(err)); \ + } \ + } while(0) Renderer::Renderer(void) : _vao(0), _vbo(0) {} @@ -25,60 +43,113 @@ bool Renderer::init(int screen_width, int screen_height) { return false; } - _projection = BettolaMath::Mat4::orthographic(0.0f, (float)screen_width, (float)screen_height, 0.0f, -1.0f, 1.0f); - _shader.use(); - unsigned int proj_loc = glGetUniformLocation(_shader.get_id(), "projection"); - glUniformMatrix4fv(proj_loc, 1, GL_FALSE, _projection.get_ptr()); + // Definitive, correct 3D cube vertices + float vertices[] = { + -0.5f, -0.5f, -0.5f, + 0.5f, -0.5f, -0.5f, + 0.5f, 0.5f, -0.5f, + 0.5f, 0.5f, -0.5f, + -0.5f, 0.5f, -0.5f, + -0.5f, -0.5f, -0.5f, - _setup_quad(); + -0.5f, -0.5f, 0.5f, + 0.5f, -0.5f, 0.5f, + 0.5f, 0.5f, 0.5f, + 0.5f, 0.5f, 0.5f, + -0.5f, 0.5f, 0.5f, + -0.5f, -0.5f, 0.5f, + + -0.5f, 0.5f, 0.5f, + -0.5f, 0.5f, -0.5f, + -0.5f, -0.5f, -0.5f, + -0.5f, -0.5f, -0.5f, + -0.5f, -0.5f, 0.5f, + -0.5f, 0.5f, 0.5f, + + 0.5f, 0.5f, 0.5f, + 0.5f, 0.5f, -0.5f, + 0.5f, -0.5f, -0.5f, + 0.5f, -0.5f, -0.5f, + 0.5f, -0.5f, 0.5f, + 0.5f, 0.5f, 0.5f, + + -0.5f, -0.5f, -0.5f, + 0.5f, -0.5f, -0.5f, + 0.5f, -0.5f, 0.5f, + 0.5f, -0.5f, 0.5f, + -0.5f, -0.5f, 0.5f, + -0.5f, -0.5f, -0.5f, + + -0.5f, 0.5f, -0.5f, + 0.5f, 0.5f, -0.5f, + 0.5f, 0.5f, 0.5f, + 0.5f, 0.5f, 0.5f, + -0.5f, 0.5f, 0.5f, + -0.5f, 0.5f, -0.5f + }; + + glGenVertexArrays(1, &_vao); + glGenBuffers(1, &_vbo); + glBindVertexArray(_vao); + glBindBuffer(GL_ARRAY_BUFFER, _vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float), (void*)0); + glEnableVertexAttribArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); glViewport(0,0,screen_width, screen_height); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + GL_CHECK_ERROR(); + glEnable(GL_DEPTH_TEST); /* Depth testing for 3D! */ + GL_CHECK_ERROR(); return true; } void Renderer::render(const Player& player, const std::vector& remote_players) { glClearColor(0.1f, 0.1f, 0.3f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); /* Need to clear depth buffer too. */ + GL_CHECK_ERROR(); _shader.use(); + GL_CHECK_ERROR(); - BettolaMath::Mat4 trans_matrix = BettolaMath::Mat4::translation(player.get_x(), - player.get_y(), 0.0f); + /* Make the camera stalk the player. */ + BettolaMath::Vec3 camera_pos = { player.get_x(), 5.0f, player.get_y() + 8.0f }; + BettolaMath::Vec3 player_pos = { player.get_x(), 0.0f, player.get_y() }; + BettolaMath::Mat4 view = BettolaMath::Mat4::look_at(camera_pos, player_pos, {0.0f,1.0f,0.0f}); - BettolaMath::Mat4 scale_matrix = BettolaMath::Mat4::scale(player.get_width(), - player.get_height(), 1.0f); + BettolaMath::Mat4 projection = BettolaMath::Mat4::perspective((45.0f * M_PI) / 180.0f, 800.0f/600.0f, 0.1f, 100.0f); - BettolaMath::Mat4 model= trans_matrix.multiply(scale_matrix); + GLint view_loc = glGetUniformLocation(_shader.get_id(), "view"); + GLint proj_loc = glGetUniformLocation(_shader.get_id(), "projection"); + GLint model_loc = glGetUniformLocation(_shader.get_id(), "model"); - unsigned int model_loc = glGetUniformLocation(_shader.get_id(), "model"); + glUniformMatrix4fv(view_loc, 1, GL_FALSE, view.get_ptr()); + GL_CHECK_ERROR(); + glUniformMatrix4fv(proj_loc, 1, GL_FALSE, projection.get_ptr()); + GL_CHECK_ERROR(); + + /* Draw the local player's cube. */ + BettolaMath::Mat4 model = BettolaMath::Mat4::translation(player.get_x(), 0.0f, player.get_y()); glUniformMatrix4fv(model_loc, 1, GL_FALSE, model.get_ptr()); + GL_CHECK_ERROR(); glBindVertexArray(_vao); - glDrawArrays(GL_TRIANGLES, 0, 6); - glBindVertexArray(0); + GL_CHECK_ERROR(); + glDrawArrays(GL_TRIANGLES, 0, 36); + GL_CHECK_ERROR(); - /* Render remote players. */ + /* Draw remote players' cube. */ for(const auto& remote_player : remote_players) { - BettolaMath::Mat4 remote_trans_matrix = - BettolaMath::Mat4::translation(remote_player.get_x(), - remote_player.get_y(), - 0.0f); - - BettolaMath::Mat4 remote_scale_matrix = - /* Assuming remote players have same size as local player. */ - BettolaMath::Mat4::scale(50.0f, 50.0f, 1.0f); - - BettolaMath::Mat4 remote_model = remote_trans_matrix.multiply(remote_scale_matrix); - + BettolaMath::Mat4 remote_model = BettolaMath::Mat4::translation(remote_player.get_x(), + 0.0f, remote_player.get_y()); glUniformMatrix4fv(model_loc, 1, GL_FALSE, remote_model.get_ptr()); - - glBindVertexArray(_vao); - glDrawArrays(GL_TRIANGLES, 0, 6); - glBindVertexArray(0); + glDrawArrays(GL_TRIANGLES, 0, 36); + GL_CHECK_ERROR(); } + + glBindVertexArray(0); } bool Renderer::_init_shaders(void) { @@ -86,31 +157,15 @@ bool Renderer::_init_shaders(void) { "assets/shaders/simple.frag")) { return false; } + + GLint success; + glGetProgramiv(_shader.get_id(), GL_LINK_STATUS, &success); + if(!success) { + char infoLog[512]; + glGetProgramInfoLog(_shader.get_id(), 512, NULL, infoLog); + fprintf(stderr, "Shader linking failed: %s\n", infoLog); + return false; + } return true; } -void Renderer::_setup_quad(void) { - float vertices[] = { - /* Should be a quad??!?? */ - -0.5f, -0.5f, 0.0f, - 0.5f, -0.5f, 0.0f, - 0.5f, 0.5f, 0.0f, - 0.5f, 0.5f, 0.0f, - -0.5f, 0.5f, 0.0f, - -0.5f, -0.5f, 0.0f, - }; - - glGenVertexArrays(1, &_vao); - glGenBuffers(1, &_vbo); - - glBindVertexArray(_vao); - - glBindBuffer(GL_ARRAY_BUFFER, _vbo); - glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); - - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float), (void*)0); - glEnableVertexAttribArray(0); - - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindVertexArray(0); -} diff --git a/src/graphics/renderer.h b/src/graphics/renderer.h index 2297266..b0135ce 100644 --- a/src/graphics/renderer.h +++ b/src/graphics/renderer.h @@ -16,7 +16,6 @@ public: private: bool _init_shaders(); - void _setup_quad(); Shader _shader; unsigned int _vao; diff --git a/srv/game/game.h b/srv/game/game.h index ba5edd7..f9f0859 100644 --- a/srv/game/game.h +++ b/srv/game/game.h @@ -4,7 +4,6 @@ #include #include "bettola/network/tcpsocket.h" #include "bettola/network/udpsocket.h" -#include "network/message.h" #include "player.h" class Game {