From d2b5fc5b3bc648afffa42375706429685ac63794 Mon Sep 17 00:00:00 2001 From: Mel Date: Mon, 12 Feb 2024 12:55:11 +0100 Subject: Split rendering into own thread and sync through render action lists --- src/Assets.cpp | 36 ++++++++-- src/Assets.hpp | 10 ++- src/Defines.hpp | 13 ++++ src/Entities/Player.cpp | 74 +++----------------- src/Entities/Player.hpp | 18 ++--- src/GFX/Actions.hpp | 39 +++++++++++ src/GFX/Resources.cpp | 26 +++++++ src/GFX/Resources.hpp | 38 ++++++++++ src/GFX/Shading/Program.cpp | 9 ++- src/GFX/Shading/Program.hpp | 4 +- src/GFX/Shading/Shader.hpp | 8 --- src/GFX/Window.cpp | 18 ++++- src/GFX/Window.hpp | 6 +- src/Game.cpp | 81 +++++++++++++++++++++ src/Game.hpp | 20 ++++++ src/Math/Rotation.hpp | 4 ++ src/Render.cpp | 102 +++++++++++++++++++++++++++ src/Render.hpp | 88 +++++++++++++++++++++++ src/Util/ImageViewer.cpp | 68 +++--------------- src/Util/ImageViewer.hpp | 12 ++-- src/World/Clouds.cpp | 113 +++++++----------------------- src/World/Clouds.hpp | 22 ++---- src/main.cpp | 167 +++++--------------------------------------- 23 files changed, 557 insertions(+), 419 deletions(-) create mode 100644 src/Defines.hpp create mode 100644 src/GFX/Actions.hpp create mode 100644 src/GFX/Resources.cpp create mode 100644 src/GFX/Resources.hpp create mode 100644 src/Game.cpp create mode 100644 src/Game.hpp create mode 100644 src/Render.cpp create mode 100644 src/Render.hpp (limited to 'src') diff --git a/src/Assets.cpp b/src/Assets.cpp index 66fdc88..dc2495e 100644 --- a/src/Assets.cpp +++ b/src/Assets.cpp @@ -2,16 +2,40 @@ namespace MC::Assets { -const Char* Shaders::fragment = -#include "../assets/generated/shaders/fragment.glsl.includable" +Char const* Shaders::terrain::vertex = +#include "../assets/generated/shaders/terrain.vert.glsl.include" ; -const Char* Shaders::vertex = -#include "../assets/generated/shaders/vertex.glsl.includable" +Char const* Shaders::terrain::fragment = +#include "../assets/generated/shaders/terrain.frag.glsl.include" ; -const Char* Images::atlas = -#include "../assets/generated/images/atlas.ppm.includable" +Char const* Shaders::clouds::vertex = +#include "../assets/generated/shaders/clouds.vert.glsl.include" +; + +Char const* Shaders::clouds::fragment = +#include "../assets/generated/shaders/clouds.frag.glsl.include" +; + +Char const* Shaders::image_viewer::vertex = +#include "../assets/generated/shaders/image_viewer.vert.glsl.include" +; + +Char const* Shaders::image_viewer::fragment = +#include "../assets/generated/shaders/image_viewer.frag.glsl.include" +; + +Char const* Shaders::block_outline::vertex = +#include "../assets/generated/shaders/block_outline.vert.glsl.include" +; + +Char const* Shaders::block_outline::fragment = +#include "../assets/generated/shaders/block_outline.frag.glsl.include" +; + +Char const* Images::atlas = +#include "../assets/generated/images/atlas.ppm.include" ; } \ No newline at end of file diff --git a/src/Assets.hpp b/src/Assets.hpp index 9ff5c42..f4b2f46 100644 --- a/src/Assets.hpp +++ b/src/Assets.hpp @@ -6,14 +6,18 @@ namespace MC::Assets { namespace Shaders { -extern const Char* vertex; -extern const Char* fragment; +#define MC_ASSETS_SHADER(name) namespace name { extern Char const* vertex; extern Char const* fragment; } + +MC_ASSETS_SHADER(terrain) +MC_ASSETS_SHADER(clouds) +MC_ASSETS_SHADER(block_outline) +MC_ASSETS_SHADER(image_viewer) } namespace Images { -extern const Char* atlas; +extern Char const* atlas; } diff --git a/src/Defines.hpp b/src/Defines.hpp new file mode 100644 index 0000000..1280e93 --- /dev/null +++ b/src/Defines.hpp @@ -0,0 +1,13 @@ +#pragma once + +#define APP_NAME "Meowcraft" + +#define WINDOW_WIDTH 800 +#define WINDOW_HEIGHT 600 +#define ASPECT (static_cast(WINDOW_WIDTH) / WINDOW_HEIGHT) + +#define FOV 90 + +// #DBDBDB +#define SKY_COLOR {0.85, 0.85, 0.85} +#define SUN_DIRECTION {1, -1, 0} diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index b7bee24..652f3fd 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -1,29 +1,8 @@ #include "Player.hpp" #include "../Common/Casts.hpp" -#include "../Math/MVP.hpp" #include namespace MC::Entities { -Player::Player(Position::World position, Real ascept, Real fov, Real near, Real far) - : m_transform(position), - m_outline_program( - {GFX::Shading::Shader::Type::Vertex, outline_vertex}, - {GFX::Shading::Shader::Type::Fragment, outline_fragment} - ), - m_outline_mesh(create_outline_cube_mesh()), - m_outline_model_uniform(), m_outline_view_uniform(), m_outline_projection_uniform() { - m_outline_program.bind(); - - m_outline_model_uniform = m_outline_program.uniform("model_matrix"); - m_outline_view_uniform = m_outline_program.uniform("view_matrix"); - m_outline_projection_uniform = m_outline_program.uniform("projection_matrix"); - - m_outline_model_uniform.set(Math::MVP::model({}, {}, {})); - m_outline_view_uniform.set(Math::MVP::view({}, {})); - m_outline_projection_uniform.set(Math::MVP::perspective_projection(ascept, fov, near, far)); - - m_outline_program.unbind(); -} void Player::update(const Time& time, GFX::Window& window, GFX::Camera& camera, World::World& world) { auto const input_direction = directional_input(window); @@ -51,24 +30,16 @@ void Player::update(const Time& time, GFX::Window& window, GFX::Camera& camera, actions(window, world); } -// TODO: Proof-of-concept, will all be moved to the rendering system. -void Player::render(const GFX::Camera& camera) { - // Render currently targeted block outline. - +void Player::render(GFX::Actions& actions) { if (m_targeted_block.has_value()) { - auto targeted_block = m_targeted_block.value(); - - m_outline_program.bind(); - - m_outline_view_uniform.set(Math::MVP::view(camera.position(), camera.angles())); - m_outline_model_uniform.set(Math::MVP::model(Vec3(targeted_block.position), Vec3::one(), {})); - - m_outline_mesh.bind(); - // TODO: These are very thin lines, do we have any better easy options? - glDrawElements(GL_LINES, m_outline_mesh.size(), GL_UNSIGNED_INT, nullptr); - m_outline_mesh.unbind(); - - m_outline_program.unbind(); + auto position = Vec3(m_targeted_block->position); + actions.add({ + .mesh = &m_outline_mesh, + .program = GFX::Resources::Program::BlockOutline, + .transform = Transform{position}, + // TODO: These are very thin lines, do we have any better easy options? + .draw_mode = GFX::DrawMode::Lines, + }); } } @@ -371,31 +342,4 @@ GFX::Mesh Player::create_outline_cube_mesh() { return builder.mesh(); } -const Char* Player::outline_vertex = R"v( -#version 330 core - -uniform mat4 model_matrix; -uniform mat4 view_matrix; -uniform mat4 projection_matrix; - -layout (location = 0) in vec3 position; - -void main() { - vec4 world_position = model_matrix * vec4(position, 1.0); - vec4 view_position = view_matrix * world_position; - vec4 clip_position = projection_matrix * view_position; - - gl_Position = clip_position; -} -)v"; - -const Char* Player::outline_fragment = R"f( -#version 330 core - -out vec4 color; - -void main() { - color = vec4(0.0, 0.0, 0.0, 1.0); -} -)f"; } diff --git a/src/Entities/Player.hpp b/src/Entities/Player.hpp index c829d1f..83ac091 100644 --- a/src/Entities/Player.hpp +++ b/src/Entities/Player.hpp @@ -2,10 +2,10 @@ #include "../Time.hpp" #include "../Transform.hpp" +#include "../GFX/Actions.hpp" #include "../GFX/Camera.hpp" #include "../World/World.hpp" #include "../GFX/Window.hpp" -#include "../GFX/Shading/Program.hpp" #include "../Math/AABB.hpp" #include "../Math/Rotation.hpp" #include "../World/Position.hpp" @@ -13,10 +13,13 @@ namespace MC::Entities { class Player { public: - explicit Player(Position::World position, Real ascept, Real fov, Real near, Real far); + explicit Player(Position::World position) + : m_transform(position), m_outline_mesh(create_outline_cube_mesh()) {} + + Position::World position() const { return m_transform.position(); } void update(const Time& time, GFX::Window& window, GFX::Camera& camera, World::World& world); - void render(const GFX::Camera& camera); + void render(GFX::Actions& actions); void move(Position::WorldOffset by); void move_to(Position::World to); @@ -83,16 +86,7 @@ private: Transform m_transform; static inline AABB s_bounds{{0.35, 1.8, 0.35}}; - // TODO: Put this into the rendering system - static const Char* outline_vertex; - static const Char* outline_fragment; - - GFX::Shading::Program m_outline_program; GFX::Mesh m_outline_mesh; - - GFX::Shading::Uniform m_outline_model_uniform; - GFX::Shading::Uniform m_outline_view_uniform; - GFX::Shading::Uniform m_outline_projection_uniform; }; } diff --git a/src/GFX/Actions.hpp b/src/GFX/Actions.hpp new file mode 100644 index 0000000..cc19169 --- /dev/null +++ b/src/GFX/Actions.hpp @@ -0,0 +1,39 @@ +#pragma once +#include "Mesh.hpp" +#include "Resources.hpp" +#include "../Transform.hpp" + +namespace MC::GFX { + +enum class DrawMode { + Triangles = GL_TRIANGLES, + Lines = GL_LINES, +}; + +struct Action { + Mesh* mesh; + Resources::Program program; + Transform transform; + + F32 alpha = 1.0f; + DrawMode draw_mode = DrawMode::Triangles; +}; + +class Actions { +public: + void add(Action const& action) { + m_actions.push_back(action); + } + + void clear() { + m_actions.clear(); + } + + const std::vector& actions() const { + return m_actions; + } +private: + std::vector m_actions; +}; + +} diff --git a/src/GFX/Resources.cpp b/src/GFX/Resources.cpp new file mode 100644 index 0000000..34ce32e --- /dev/null +++ b/src/GFX/Resources.cpp @@ -0,0 +1,26 @@ +#include "Resources.hpp" + +#include "../Common/Assert.hpp" + +namespace MC::GFX { + +void Resources::initialize() { + ASSERT(!m_initialized, "Resources already initialized"); + for (auto& metadata: m_program_metadata) { + m_programs[static_cast(metadata.id)] = create_program(metadata); + } + m_initialized = true; +} + +Shading::Program const& Resources::program(Program id) const { + ASSERT(m_initialized, "Resources not initialized"); + return m_programs[static_cast(id)]; +} + +Shading::Program Resources::create_program(ProgramMetadata const& metadata) { + Shading::Shader vertex(Shading::Shader::Type::Vertex, metadata.vertex); + Shading::Shader fragment(Shading::Shader::Type::Fragment, metadata.fragment); + return {vertex, fragment}; +} + +} diff --git a/src/GFX/Resources.hpp b/src/GFX/Resources.hpp new file mode 100644 index 0000000..4206e33 --- /dev/null +++ b/src/GFX/Resources.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include +#include "Shading/Program.hpp" + +namespace MC::GFX { +class Resources { +public: + enum class Program: USize { + Terrain, + Clouds, + ImageViewer, + BlockOutline, + }; + constexpr static USize ProgramCount = 4; + + void initialize(); + Shading::Program const& program(Program id) const; +private: + struct ProgramMetadata { + Program id; + Char const* vertex; + Char const* fragment; + }; + + static inline std::array const m_program_metadata = {{ + {Program::Terrain, Assets::Shaders::terrain::vertex, Assets::Shaders::terrain::fragment}, + {Program::Clouds, Assets::Shaders::clouds::vertex, Assets::Shaders::clouds::fragment}, + {Program::ImageViewer, Assets::Shaders::image_viewer::vertex, Assets::Shaders::image_viewer::fragment}, + {Program::BlockOutline, Assets::Shaders::block_outline::vertex, Assets::Shaders::block_outline::fragment}, + }}; + + Bool m_initialized = false; + std::array m_programs = {}; + + static Shading::Program create_program(ProgramMetadata const& metadata); +}; +} diff --git a/src/GFX/Shading/Program.cpp b/src/GFX/Shading/Program.cpp index ff10012..fe5d576 100644 --- a/src/GFX/Shading/Program.cpp +++ b/src/GFX/Shading/Program.cpp @@ -2,6 +2,8 @@ #include #include "Program.hpp" +#include "../../Common/Assert.hpp" + namespace MC::GFX::Shading { Program::Program(Shader vertex, Shader fragment) { @@ -26,6 +28,7 @@ Program::Program(Shader vertex, Shader fragment) { } void Program::bind() const { + ASSERT(m_program != 0, "Program is not initialized"); glUseProgram(m_program); } @@ -33,10 +36,12 @@ void Program::unbind() const { glUseProgram(0); } -Uniform Program::uniform(const std::string& name) const { +std::optional Program::uniform(const std::string& name) const { + ASSERT(m_program != 0, "Program is not initialized"); auto index = glGetUniformLocation(m_program, name.c_str()); - return {name, static_cast(index)}; + if (index == -1) return {}; + return {{name, static_cast(index)}}; } U32 Program::get() const { diff --git a/src/GFX/Shading/Program.hpp b/src/GFX/Shading/Program.hpp index 2f48698..67838dc 100644 --- a/src/GFX/Shading/Program.hpp +++ b/src/GFX/Shading/Program.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include "Shader.hpp" #include "Uniform.hpp" @@ -8,11 +9,12 @@ namespace MC::GFX::Shading { class Program { public: + Program() : m_program(0) {} Program(Shader vertex, Shader fragment); U32 get() const; - Uniform uniform(const std::string& name) const; + std::optional uniform(std::string const& name) const; void bind() const; void unbind() const; diff --git a/src/GFX/Shading/Shader.hpp b/src/GFX/Shading/Shader.hpp index 21fd899..8c6c5c8 100644 --- a/src/GFX/Shading/Shader.hpp +++ b/src/GFX/Shading/Shader.hpp @@ -19,14 +19,6 @@ public: return m_shader; } - static Shader create_vertex() { - return {Type::Vertex, Assets::Shaders::vertex}; - } - - static Shader create_fragment() { - return {Type::Fragment, Assets::Shaders::fragment}; - } - private: U32 m_shader; }; diff --git a/src/GFX/Window.cpp b/src/GFX/Window.cpp index bbe2ba7..c0c5b03 100644 --- a/src/GFX/Window.cpp +++ b/src/GFX/Window.cpp @@ -2,6 +2,8 @@ #include "../Common/Sizes.hpp" #include "Window.hpp" +#include "../Common/Assert.hpp" + namespace MC::GFX { Window::Window(const Char* title, U32 width, U32 height) { @@ -48,8 +50,11 @@ Bool Window::mouse(I32 key, I32 type) const { return glfwGetMouseButton(m_window, key) == type; } -void Window::start_frame() { +void Window::start_render() { glfwSwapBuffers(m_window); +} + +void Window::poll_events() { glfwPollEvents(); } @@ -57,4 +62,13 @@ void Window::on_size_change(void (callback)(GLFWwindow*, I32, I32)) { glfwSetFramebufferSizeCallback(m_window, callback); } -} \ No newline at end of file +void Window::attach() const { + glfwMakeContextCurrent(m_window); +} + +void Window::detach() const { + ASSERT(glfwGetCurrentContext() == m_window, "Cannot detach window that is not current"); + glfwMakeContextCurrent(nullptr); +} + +} diff --git a/src/GFX/Window.hpp b/src/GFX/Window.hpp index ac0efd2..c26b0fd 100644 --- a/src/GFX/Window.hpp +++ b/src/GFX/Window.hpp @@ -16,8 +16,12 @@ public: void on_size_change(void (* callback)(GLFWwindow*, I32, I32)); + void attach() const; + void detach() const; + void close(); - void start_frame(); + void start_render(); + void poll_events(); Vector<2> mouse_delta(); Bool key(I32 key, I32 type) const; diff --git a/src/Game.cpp b/src/Game.cpp new file mode 100644 index 0000000..117ba09 --- /dev/null +++ b/src/Game.cpp @@ -0,0 +1,81 @@ +#include "Game.hpp" + +#include +#include "Time.hpp" +#include "Common/Sizes.hpp" +#include "Entities/Player.hpp" +#include "GFX/Camera.hpp" +#include "GFX/Window.hpp" +#include "World/Clouds.hpp" +#include "World/World.hpp" + +namespace MC { + +void Game::run() const { + World::World world{}; + + GFX::Camera camera{}; + + World::Clouds clouds{}; + Entities::Player player{{0, World::Chunk::Height / 2.0, 0}}; + + Time time; + + while (!m_window.should_close()) { + m_window.poll_events(); + +#ifdef __APPLE__ + // Needs to happen on the main thread + fix_macos_render(); +#endif + + time.start_frame(); + + if (m_window.key(GLFW_KEY_ESCAPE, GLFW_PRESS)) { + m_window.close(); + } + + player.update(time, m_window, camera, world); + clouds.update(time); + + GFX::Actions actions; + + for (auto chunk : world.get_visible_chunks(time, camera.position())) { + auto position = chunk->chunk.value().position(); + actions.add({ + .program = GFX::Resources::Program::Terrain, + .mesh = &chunk->land_mesh.value(), + .transform = Transform(position), + }); + + actions.add({ + .program = GFX::Resources::Program::Terrain, + .mesh = &chunk->water_mesh.value(), + .transform = Transform(position - Vec3{0, 0.2, 0}), + .alpha = 0.4, + }); + } + + player.render(actions); + clouds.render(actions, player.position()); + + m_render_control->send_render_data({actions, camera}); + m_render_control->wait_for_render_finish(); + + time.end_frame(); + } +} + +void Game::fix_macos_render() const { + static Bool moved = false; + + if(!moved) { + I32 x, y; + glfwGetWindowPos(m_window.get(), &x, &y); + glfwSetWindowPos(m_window.get(), ++x, y); + + moved = true; + } +} + +} \ No newline at end of file diff --git a/src/Game.hpp b/src/Game.hpp new file mode 100644 index 0000000..ccb0013 --- /dev/null +++ b/src/Game.hpp @@ -0,0 +1,20 @@ +#pragma once +#include "Render.hpp" + +namespace MC { + +class Game { +public: + explicit Game(GFX::Window& window, std::shared_ptr control) + : m_window(window) + , m_render_control(std::move(control)) {} + + void run() const; +private: + void fix_macos_render() const; + + GFX::Window& m_window; + std::shared_ptr m_render_control; +}; + +} diff --git a/src/Math/Rotation.hpp b/src/Math/Rotation.hpp index d12ac29..d8dff72 100644 --- a/src/Math/Rotation.hpp +++ b/src/Math/Rotation.hpp @@ -15,6 +15,10 @@ struct Rotation { vector = wrap({pitch, yaw, roll }); } + static Rotation zero() { + return {0, 0, 0}; + } + Vector<3> radians() const { return vector.map([](auto a) { return Math::radians(a); }); } diff --git a/src/Render.cpp b/src/Render.cpp new file mode 100644 index 0000000..3955f1c --- /dev/null +++ b/src/Render.cpp @@ -0,0 +1,102 @@ +#include "Render.hpp" +#include "Assets.hpp" +#include "Defines.hpp" +#include "Time.hpp" +#include "Common/Assert.hpp" +#include "GFX/Image/PPMParser.hpp" +#include "GFX/Shading/Program.hpp" +#include "Math/MVP.hpp" + +namespace MC { + +void Render::run() { + m_window.attach(); + setup_gl(); + + m_resources.initialize(); + + glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); + m_window.on_size_change([](GLFWwindow* _, I32 w, I32 h) { + glViewport(0, 0, w, h); + }); + + auto image = GFX::Image::PPMParser(Assets::Images::atlas).parse(); + auto texture = GFX::Texture(image); + + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LEQUAL); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glEnable(GL_CULL_FACE); + glFrontFace(GL_CCW); + glCullFace(GL_BACK); + + while (!m_window.should_close()) { + Scene scene = m_control->wait_for_render_data(); + + m_window.start_render(); + render_scene(scene, texture); + + m_control->finish_render(); + } +} + +void Render::render_scene(Scene const& scene, GFX::Texture const& texture) const { + Vector<3, F32> sky_color = SKY_COLOR; + glClearColor(sky_color.x(), sky_color.y(), sky_color.z(), 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + auto view = Math::MVP::view(scene.camera.position(), scene.camera.angles()); + auto projection = Math::MVP::perspective_projection(ASPECT, FOV, 0.1f, 1000.0f); + + for (auto& action : scene.actions.actions()) { + auto& program = m_resources.program(action.program); + program.bind(); + + auto model_uniform = program.uniform("model_matrix"); + auto view_uniform = program.uniform("view_matrix"); + auto projection_uniform = program.uniform("projection_matrix"); + auto sun_direction_uniform = program.uniform("sun_direction"); + auto sky_color_uniform = program.uniform("sky_color"); + auto mesh_alpha_uniform = program.uniform("mesh_alpha"); + + ASSERT(model_uniform.has_value() && view_uniform.has_value() && projection_uniform.has_value(), + "Program does not have the necessary MVP uniforms"); + + auto transform = action.transform; + auto model = Math::MVP::model(transform.position(), transform.scale(), transform.rotation()); + + model_uniform->set(model); + view_uniform->set(view); + projection_uniform->set(projection); + + if (sun_direction_uniform.has_value()) + sun_direction_uniform->set(SUN_DIRECTION); + + if (sky_color_uniform.has_value()) + sky_color_uniform->set(SKY_COLOR); + + if (mesh_alpha_uniform.has_value()) + mesh_alpha_uniform->set(action.alpha); + + texture.bind(); + action.mesh->bind(); + glDrawElements(TO(GLenum, action.draw_mode), action.mesh->size(), GL_UNSIGNED_INT, nullptr); + action.mesh->unbind(); + texture.unbind(); + + program.unbind(); + } +} + +void Render::setup_gl() { + GLenum error = glewInit(); + if (error != GLEW_OK) { + std::string error_string(reinterpret_cast(glewGetErrorString(error))); + throw std::runtime_error("Failed to load GL functions: " + error_string); + } +} + +} diff --git a/src/Render.hpp b/src/Render.hpp new file mode 100644 index 0000000..6b7cb65 --- /dev/null +++ b/src/Render.hpp @@ -0,0 +1,88 @@ +#pragma once + +#include +#include "Defines.hpp" +#include "Common/Sizes.hpp" +#include "GFX/Actions.hpp" +#include "GFX/Camera.hpp" +#include "GFX/Texture.hpp" +#include "GFX/Window.hpp" + +namespace MC { + +class Render { +public: + struct Scene { + GFX::Actions actions; + GFX::Camera camera; + }; + + struct Control { + std::mutex mutex; + std::condition_variable cv; + Bool logic_done = false; + Bool render_done = false; + + Scene scene; + + void send_render_data(Scene new_scene) { + { + std::scoped_lock lock(mutex); + scene = std::move(new_scene); + logic_done = true; + } + cv.notify_one(); + } + + Scene wait_for_render_data() { + Scene new_scene; + { + std::unique_lock lock(mutex); + cv.wait(lock, [&]{ + return logic_done; + }); + new_scene = scene; + logic_done = false; + render_done = false; + } + cv.notify_one(); + return new_scene; + } + + void wait_for_render_finish() { + { + std::unique_lock lock(mutex); + cv.wait(lock, [&]{ + return render_done; + }); + logic_done = false; + render_done = false; + } + cv.notify_one(); + } + + void finish_render() { + { + std::scoped_lock lock(mutex); + render_done = true; + } + cv.notify_one(); + } + }; + + explicit Render(GFX::Window& window, std::shared_ptr control) + : m_window(window) + , m_control(std::move(control)) {} + + void run(); + +private: + void render_scene(Scene const& actions, GFX::Texture const& texture) const; + static void setup_gl(); + + GFX::Resources m_resources; + GFX::Window& m_window; + std::shared_ptr m_control; +}; + +} diff --git a/src/Util/ImageViewer.cpp b/src/Util/ImageViewer.cpp index 4c78750..05d316c 100644 --- a/src/Util/ImageViewer.cpp +++ b/src/Util/ImageViewer.cpp @@ -1,38 +1,15 @@ -#include #include "ImageViewer.hpp" -#include "../Math/MVP.hpp" namespace MC::Util { -ImageViewer::ImageViewer( - const GFX::Image::RawImage& image, - Real window_aspect -) : m_texture(image), - m_program( - {GFX::Shading::Shader::Type::Vertex, vertex}, - {GFX::Shading::Shader::Type::Fragment, fragment} - ), - m_mesh(create_mesh(window_aspect, image.width(), image.height())) { - m_program.bind(); - auto model_uniform = m_program.uniform("model_matrix"); - auto view_uniform = m_program.uniform("view_matrix"); - auto projection_uniform = m_program.uniform("projection_matrix"); - - model_uniform.set(Math::MVP::model({}, Vector<3>::one(), {})); - view_uniform.set(Math::MVP::view({}, {})); - projection_uniform.set(Math::MVP::orthographic_projection(view_size * window_aspect, view_size, 0.0f, 100.0f)); - - m_program.unbind(); -} - -void ImageViewer::render() { - m_program.bind(); - m_texture.bind(); - m_mesh.bind(); - glDrawElements(GL_TRIANGLES, m_mesh.size(), GL_UNSIGNED_INT, nullptr); - m_mesh.unbind(); - m_texture.unbind(); - m_program.unbind(); +void ImageViewer::render(GFX::Actions& actions) { + // TODO: Re-add texture support + // TODO: Add orthographic camera support + actions.add({ + .program = GFX::Resources::Program::ImageViewer, + .mesh = &m_mesh, + .transform = Transform(), + }); } GFX::Mesh ImageViewer::create_mesh(Real window_aspect, U32 image_width, U32 image_height) { @@ -60,33 +37,4 @@ GFX::Mesh ImageViewer::create_mesh(Real window_aspect, U32 image_width, U32 imag }, {0, 1, 2, 0, 2, 3}}; } -const Char* ImageViewer::vertex = R"v( -#version 330 core - -uniform mat4 model_matrix; -uniform mat4 view_matrix; -uniform mat4 projection_matrix; - -layout (location = 0) in vec3 position; -layout (location = 1) in vec2 tex_coord; - -out vec2 frag_tex_coord; - -void main() { - gl_Position = projection_matrix * view_matrix * model_matrix * vec4(position, 1.0); - frag_tex_coord = tex_coord; -})v"; - -const Char* ImageViewer::fragment = R"f( -#version 330 core - -uniform sampler2D image; - -in vec2 frag_tex_coord; -out vec4 color; - -void main() { - color = texture(image, frag_tex_coord); -})f"; - } \ No newline at end of file diff --git a/src/Util/ImageViewer.hpp b/src/Util/ImageViewer.hpp index aa48b1e..cfe9d18 100644 --- a/src/Util/ImageViewer.hpp +++ b/src/Util/ImageViewer.hpp @@ -1,27 +1,25 @@ #pragma once +#include "../GFX/Actions.hpp" #include "../GFX/Mesh.hpp" #include "../GFX/Image/RawImage.hpp" #include "../GFX/Texture.hpp" -#include "../GFX/Shading/Program.hpp" namespace MC::Util { class ImageViewer { public: - explicit ImageViewer(const GFX::Image::RawImage& image, Real window_aspect); + explicit ImageViewer(const GFX::Image::RawImage& image, Real window_aspect) + : m_mesh(create_mesh(window_aspect, image.width(), image.height())), + m_texture(image) {} - void render(); + void render(GFX::Actions& actions); private: static GFX::Mesh create_mesh(Real window_aspect, U32 image_width, U32 image_height); static constexpr Real view_size = 1000.0f; - static const Char* vertex; - static const Char* fragment; - GFX::Mesh m_mesh; - GFX::Shading::Program m_program; GFX::Texture m_texture; }; diff --git a/src/World/Clouds.cpp b/src/World/Clouds.cpp index b30c0ce..58b1919 100644 --- a/src/World/Clouds.cpp +++ b/src/World/Clouds.cpp @@ -5,65 +5,47 @@ #include "../Math/AABB.hpp" #include "../GFX/Util/Primitives.hpp" #include "../GFX/Util/MeshBuilder.hpp" -#include #include namespace MC::World { -Clouds::Clouds(Real ascept, Real fov, Real near, Real far, Vector<3, F32> sky_color, Vector<3, F32> sun_direction) - : m_program( - {GFX::Shading::Shader::Type::Vertex, vertex}, - {GFX::Shading::Shader::Type::Fragment, fragment} - ), - m_mesh(create_mesh(create_cloud_matrix())), - m_model_uniform(), m_view_uniform(), m_projection_uniform() { - m_program.bind(); - - m_model_uniform = m_program.uniform("model_matrix"); - m_view_uniform = m_program.uniform("view_matrix"); - m_projection_uniform = m_program.uniform("projection_matrix"); - auto sky_color_uniform = m_program.uniform("sky_color"); - auto sun_direction_uniform = m_program.uniform("sun_direction"); - - m_model_uniform.set(Math::MVP::model({}, {}, {})); - m_view_uniform.set(Math::MVP::view({}, {})); - m_projection_uniform.set(Math::MVP::perspective_projection(ascept, fov, near, far)); - sky_color_uniform.set(sky_color); - sun_direction_uniform.set(sun_direction); - - m_program.unbind(); -} - -void Clouds::update(const Time& time) { +void Clouds::update(Time const& time) { m_x_offset += 5.0 * time.delta(); } -void Clouds::render(const GFX::Camera& camera) { - auto position = camera.position(); - - I32 center_x = std::round((position.x() - m_x_offset) / TileSize); - I32 center_y = std::round(position.z() / TileSize); +void Clouds::render(GFX::Actions& actions, Position::World player_position) { + I32 center_x = std::round((player_position.x() - m_x_offset) / TileSize); + I32 center_y = std::round(player_position.z() / TileSize); for (Int x = -1; x <= 1; x++) { for (Int y = -1; y <= 1; y++) { - render_single_instance(camera, center_x + x, center_y + y); + render_single_instance(actions, center_x + x, center_y + y); } } } -void Clouds::render_single_instance(const GFX::Camera& camera, Int x, Int y) { +void Clouds::render_single_instance(GFX::Actions& actions, Int x, Int y) { Vector<3> position{TileSize * x + m_x_offset, Height, TileSize * y}; - m_program.bind(); - - m_view_uniform.set(Math::MVP::view(camera.position(), camera.angles())); - m_model_uniform.set(Math::MVP::model(position, Vector<3>{Scale}, {})); - - m_mesh.bind(); - glDrawElements(GL_TRIANGLES, m_mesh.size(), GL_UNSIGNED_INT, nullptr); - m_mesh.unbind(); - - m_program.unbind(); + // m_program.bind(); + // + // m_view_uniform.set(Math::MVP::view(camera.position(), camera.angles())); + // m_model_uniform.set(Math::MVP::model(position, Vector<3>{Scale}, {})); + // + // m_mesh.bind(); + // glDrawElements(GL_TRIANGLES, m_mesh.size(), GL_UNSIGNED_INT, nullptr); + // m_mesh.unbind(); + // + // m_program.unbind(); + actions.add({ + .mesh = &m_mesh, + .program = GFX::Resources::Program::Clouds, + .transform = Transform( + position, + Rotation::zero(), + Vec3(Scale) + ), + }); } Clouds::CloudMatrix Clouds::create_cloud_matrix() { @@ -80,7 +62,7 @@ Clouds::CloudMatrix Clouds::create_cloud_matrix() { return clouds; } -GFX::Mesh Clouds::create_mesh(const CloudMatrix& cloud_matrix) { +GFX::Mesh Clouds::create_mesh(CloudMatrix const& cloud_matrix) { GFX::Util::MeshBuilder> builder{}; for (Int x = 0; x < CloudMatrixSize; x++) { @@ -112,47 +94,4 @@ GFX::Mesh Clouds::create_mesh(const CloudMatrix& cloud_matrix) { return builder.mesh(); } -const Char* Clouds::vertex = R"v( -#version 330 core - -uniform mat4 model_matrix; -uniform mat4 view_matrix; -uniform mat4 projection_matrix; - -layout (location = 0) in vec3 position; -layout (location = 1) in vec3 normal; - -out vec3 surface_normal; -out float depth; - -void main() { - vec4 world_position = model_matrix * vec4(position, 1.0); - vec4 view_position = view_matrix * world_position; - vec4 clip_position = projection_matrix * view_position; - - gl_Position = clip_position; - surface_normal = (model_matrix * vec4(normal, 0.0)).xyz; - depth = clamp((length(view_position) - 200) / 400, 0.0, 1.0); -} -)v"; - -const Char* Clouds::fragment = R"f( -#version 330 core - -uniform vec3 sky_color; -uniform vec3 sun_direction; - -in vec3 surface_normal; -in float depth; -out vec4 color; - -void main() { - float brightness = dot(normalize(surface_normal), normalize(-sun_direction)); - vec3 diffuse = vec3(1 - clamp(brightness, -0.3, 0.2)); - vec3 base = vec3(1.0, 1.0, 1.0) * diffuse; - - color = vec4(mix(sky_color, base, 1 - depth), 0.3); -} -)f"; - } diff --git a/src/World/Clouds.hpp b/src/World/Clouds.hpp index 4bd0637..7246e7b 100644 --- a/src/World/Clouds.hpp +++ b/src/World/Clouds.hpp @@ -1,8 +1,7 @@ #pragma once #include "../Time.hpp" -#include "../GFX/Shading/Program.hpp" -#include "../GFX/Shading/Uniform.hpp" +#include "../GFX/Actions.hpp" #include "../GFX/Camera.hpp" #include "../GFX/Mesh.hpp" @@ -10,10 +9,10 @@ namespace MC::World { class Clouds { public: - Clouds(Real ascept, Real fov, Real near, Real far, Vector<3, F32> sky_color, Vector<3, F32> sun_direction); + Clouds() : m_mesh(create_mesh(create_cloud_matrix())) {} - void update(const Time& time); - void render(const GFX::Camera& camera); + void update(Time const& time); + void render(GFX::Actions& actions, Position::World player_position); private: constexpr static U32 CloudMatrixSize = 128; constexpr static Int Height = 200; @@ -22,22 +21,13 @@ private: using CloudMatrix = Matrix; - void render_single_instance(const GFX::Camera& camera, Int x, Int y); + void render_single_instance(GFX::Actions& actions, Int x, Int y); static CloudMatrix create_cloud_matrix(); - static GFX::Mesh create_mesh(const CloudMatrix& cloud_matrix); - - static const Char* vertex; - static const Char* fragment; + static GFX::Mesh create_mesh(CloudMatrix const& cloud_matrix); Real m_x_offset = 0.0; - - GFX::Shading::Program m_program; GFX::Mesh m_mesh; - - GFX::Shading::Uniform m_model_uniform; - GFX::Shading::Uniform m_view_uniform; - GFX::Shading::Uniform m_projection_uniform; }; } diff --git a/src/main.cpp b/src/main.cpp index 9dd42c9..9153530 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2,36 +2,30 @@ #include #include -#include "Time.hpp" -#include "Common/Sizes.hpp" -#include "GFX/Window.hpp" -#include "GFX/Camera.hpp" -#include "Math/MVP.hpp" -#include "GFX/Shading/Program.hpp" -#include "GFX/Texture.hpp" -#include "GFX/Image/PPMParser.hpp" -#include "World/Clouds.hpp" +#include "Game.hpp" +#include "Render.hpp" #include "World/World.hpp" -#include "Entities/Player.hpp" -#define APP_NAME "Meowcraft" - -#define WINDOW_WIDTH 800 -#define WINDOW_HEIGHT 600 -#define ASPECT (static_cast(WINDOW_WIDTH) / WINDOW_HEIGHT) +int main() { + if (!glfwInit()) { + std::cout << "Failed to initialize GLFW" << std::endl; + return 1; + } -#define FOV 90 + try { + MC::GFX::Window window(APP_NAME, WINDOW_WIDTH, WINDOW_HEIGHT); + window.detach(); -void run(); -void render(MC::GFX::Mesh&, MC::GFX::Texture&); -void setup_gl(); -void fix_macos_render(const MC::GFX::Window&); + auto render_control = std::make_shared(); -int main() { - glfwInit(); + std::thread render_thread{[&window, render_control] { + MC::Render render{window, render_control}; + render.run(); + }}; + render_thread.detach(); - try { - run(); + MC::Game game{window, render_control}; + game.run(); } catch (std::runtime_error& error) { std::cout << "An error occurred: " << error.what() << std::endl; glfwTerminate(); @@ -41,128 +35,3 @@ int main() { glfwTerminate(); return 0; } - -void run() { - MC::GFX::Window window(APP_NAME, WINDOW_WIDTH, WINDOW_HEIGHT); - setup_gl(); - - glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); - window.on_size_change([](GLFWwindow* _, I32 w, I32 h) { - glViewport(0, 0, w, h); - }); - - auto image = MC::GFX::Image::PPMParser(MC::Assets::Images::atlas).parse(); - auto texture = MC::GFX::Texture(image); - - MC::World::World world; - - MC::GFX::Camera camera{}; - - MC::GFX::Shading::Program program( - MC::GFX::Shading::Shader::create_vertex(), - MC::GFX::Shading::Shader::create_fragment() - ); - - auto model_uniform = program.uniform("model_matrix"); - auto view_uniform = program.uniform("view_matrix"); - auto projection_uniform = program.uniform("projection_matrix"); - auto sun_direction_uniform = program.uniform("sun_direction"); - auto sky_color_uniform = program.uniform("sky_color"); - auto mesh_alpha_uniform = program.uniform("mesh_alpha"); - - program.bind(); - auto projection = Math::MVP::perspective_projection(ASPECT, FOV, 0.1f, 1000.0f); - projection_uniform.set(projection); - - Vector<3, F32> sun_direction{1, -1, 0}; - sun_direction_uniform.set(sun_direction); - - Vector<3, F32> sky_color{0.85, 0.85, 0.85}; // #DBDBDB - sky_color_uniform.set(sky_color); - - MC::World::Clouds clouds{ASPECT, FOV, 0.1f, 1000.0f, sky_color, sun_direction}; - - MC::Entities::Player player{{0, MC::World::Chunk::Height / 2.0, 0}}; - - glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_LEQUAL); - - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - glEnable(GL_CULL_FACE); - glFrontFace(GL_CCW); - glCullFace(GL_BACK); - - MC::Time time; - - while (!window.should_close()) { - time.start_frame(); - window.start_frame(); - -#ifdef __APPLE__ - fix_macos_render(window); -#endif - - if (window.key(GLFW_KEY_ESCAPE, GLFW_PRESS)) { - window.close(); - } - - player.update(time, window, camera, world); - clouds.update(time); - - glClearColor(sky_color.x(), sky_color.y(), sky_color.z(), 1.0f); // #DBDBDB - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - program.bind(); - - auto view = Math::MVP::view(camera.position(), camera.angles()); - view_uniform.set(view); - - for (auto chunk : world.get_visible_chunks(camera.position())) { - mesh_alpha_uniform.set(1.0); - auto land_model = Math::MVP::model(chunk->chunk.value().position(), Vector<3>::one(), {}); - model_uniform.set(land_model); - render(chunk->land_mesh.value(), texture); - - mesh_alpha_uniform.set(0.4); - auto water_model = Math::MVP::model(chunk->chunk.value().position() - Vector<3>{0, 0.2, 0}, Vector<3>::one(), {}); - model_uniform.set(water_model); - render(chunk->water_mesh.value(), texture); - } - - program.unbind(); - - clouds.render(camera); - - time.end_frame(); - } -} - -void render(MC::GFX::Mesh& mesh, MC::GFX::Texture& texture) { - texture.bind(); - mesh.bind(); - glDrawElements(GL_TRIANGLES, mesh.size(), GL_UNSIGNED_INT, nullptr); - mesh.unbind(); - texture.unbind(); -} - -void setup_gl() { - GLenum error; - if ((error = glewInit()) != GLEW_OK) { - std::string error_string(reinterpret_cast(glewGetErrorString(error))); - throw std::runtime_error("Failed to load GL functions: " + error_string); - } -} - -void fix_macos_render(const MC::GFX::Window& window) { - static Bool moved = false; - - if(!moved) { - I32 x, y; - glfwGetWindowPos(window.get(), &x, &y); - glfwSetWindowPos(window.get(), ++x, y); - - moved = true; - } -} \ No newline at end of file -- cgit 1.4.1