From e71c4d55cfffba50d92dbee4e837194039d09423 Mon Sep 17 00:00:00 2001 From: portersky <24420859+portersky@users.noreply.github.com> Date: Tue, 5 May 2026 22:27:19 +0200 Subject: [PATCH] refactor: split into static libraries and restructure scenes - Create cbt_opengl static library for OpenGL abstraction - Create cbt_scene static library for base scene class - Create scenes_cube static library for cube scene - Move scene base to cbt/ (utility), scenes to root scenes/ - Link libraries in dependency chain: cuber -> scenes -> cbt_scene -> cbt_opengl --- CMakeLists.txt | 34 ++++++++++- cbt/scene.cpp | 13 +++++ cbt/scene.hpp | 16 ++++++ cuber.cpp | 135 +++---------------------------------------- scenes/cube.cpp | 149 ++++++++++++++++++++++++++++++++++++++++++++++++ scenes/cube.hpp | 34 +++++++++++ 6 files changed, 252 insertions(+), 129 deletions(-) create mode 100644 cbt/scene.cpp create mode 100644 cbt/scene.hpp create mode 100644 scenes/cube.cpp create mode 100644 scenes/cube.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index d3571b8..261ed35 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,8 +18,8 @@ find_package(glad REQUIRED) find_package(asio REQUIRED) find_package(glm REQUIRED) -# Target setup -add_executable(cuber "cuber.cpp" +# OpenGL abstraction library +add_library(cbt_opengl STATIC "cbt/opengl/context.cpp" "cbt/opengl/buffer.cpp" "cbt/opengl/texture.cpp" @@ -27,8 +27,36 @@ add_executable(cuber "cuber.cpp" "cbt/opengl/shader.cpp" "cbt/opengl/vao.cpp" ) +target_include_directories(cbt_opengl PRIVATE ".") +target_compile_features(cbt_opengl PRIVATE cxx_std_23) +target_compile_options(cbt_opengl PRIVATE ${BASE_OPTIONS}) +target_compile_definitions(cbt_opengl PRIVATE ${BASE_DEFINITIONS}) +target_link_libraries(cbt_opengl PUBLIC fmt::fmt glfw::glfw glad::glad) + +# Scene base library +add_library(cbt_scene STATIC + "cbt/scene.cpp" +) +target_include_directories(cbt_scene PRIVATE ".") +target_compile_features(cbt_scene PRIVATE cxx_std_23) +target_compile_options(cbt_scene PRIVATE ${BASE_OPTIONS}) +target_compile_definitions(cbt_scene PRIVATE ${BASE_DEFINITIONS}) +target_link_libraries(cbt_scene PUBLIC cbt_opengl) + +# Application scenes +add_library(scenes_cube STATIC + "scenes/cube.cpp" +) +target_include_directories(scenes_cube PRIVATE ".") +target_compile_features(scenes_cube PRIVATE cxx_std_23) +target_compile_options(scenes_cube PRIVATE ${BASE_OPTIONS}) +target_compile_definitions(scenes_cube PRIVATE ${BASE_DEFINITIONS}) +target_link_libraries(scenes_cube PUBLIC cbt_scene glm::glm) + +# Main executable +add_executable(cuber "cuber.cpp") target_include_directories(cuber PRIVATE ".") target_compile_features(cuber PRIVATE cxx_std_23) target_compile_options(cuber PRIVATE ${BASE_OPTIONS}) target_compile_definitions(cuber PRIVATE ${BASE_DEFINITIONS}) -target_link_libraries(cuber PRIVATE fmt::fmt glfw::glfw glad::glad asio::asio glm::glm ${BASE_LIBRARIES}) +target_link_libraries(cuber PRIVATE cbt_scene scenes_cube asio::asio ${BASE_LIBRARIES}) diff --git a/cbt/scene.cpp b/cbt/scene.cpp new file mode 100644 index 0000000..0ffdd5d --- /dev/null +++ b/cbt/scene.cpp @@ -0,0 +1,13 @@ +#include "cbt/scene.hpp" + +namespace cbt { + +auto scene::init() -> bool { + return true; +} + +auto scene::update(float) -> void {} + +auto scene::render() -> void {} + +} diff --git a/cbt/scene.hpp b/cbt/scene.hpp new file mode 100644 index 0000000..d0629dd --- /dev/null +++ b/cbt/scene.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include + +namespace cbt { + +class scene { +public: + virtual ~scene() = default; + + virtual auto init() -> bool; + virtual auto update(float delta_time) -> void; + virtual auto render() -> void; +}; + +} diff --git a/cuber.cpp b/cuber.cpp index 17a4470..1aba07f 100644 --- a/cuber.cpp +++ b/cuber.cpp @@ -2,18 +2,11 @@ #include "GLFW/glfw3.h" #include "cbt/opengl/context.hpp" -#include "cbt/opengl/shader.hpp" -#include "cbt/opengl/buffer.hpp" -#include "cbt/opengl/vao.hpp" +#include "scenes/cube.hpp" -#include +#include #include -#include -#include -#include - -#include "glad/glad.h" auto main(int, char const*[]) -> int { auto ctx = cbt::opengl::context("cuber", 1280, 720); @@ -22,106 +15,11 @@ auto main(int, char const*[]) -> int { return 1; } - // compile shader - auto prog = cbt::opengl::shader(); - - char const* vert_src = R"glsl( - #version 410 core - layout(location = 0) in vec3 a_pos; - layout(location = 1) in vec3 a_color; - uniform mat4 u_model; - uniform mat4 u_view; - uniform mat4 u_proj; - out vec3 v_color; - void main() { - gl_Position = u_proj * u_view * u_model * vec4(a_pos, 1.0); - v_color = a_color; - } - )glsl"; - - char const* frag_src = R"glsl( - #version 410 core - in vec3 v_color; - out vec4 frag_color; - void main() { - frag_color = vec4(v_color, 1.0); - } - )glsl"; - - if (!prog.compile_vertex(vert_src) || !prog.compile_fragment(frag_src) || !prog.link()) { + auto scn = cbt::scenes::cube(); + if (!scn.init()) { return 1; } - // cube vertices: 6 faces, 2 triangles per face, 3 verts each = 36 vertices - // each vertex: x, y, z, r, g, b - std::array vertices = { - // front face (cyan) - 0.5f, -0.5f, 0.5f, 0.0f, 1.0f, 1.0f, - -0.5f, -0.5f, 0.5f, 0.0f, 1.0f, 1.0f, - -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 1.0f, - 0.5f, -0.5f, 0.5f, 0.0f, 1.0f, 1.0f, - -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 1.0f, - 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 1.0f, - - // back face (magenta) - 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 1.0f, - -0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 1.0f, - -0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 1.0f, - 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 1.0f, - -0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 1.0f, - 0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 1.0f, - - // top face (yellow) - 0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 0.0f, - -0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 0.0f, - -0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 0.0f, - 0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 0.0f, - -0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 0.0f, - 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 0.0f, - - // bottom face (white) - 0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 1.0f, - -0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 1.0f, - -0.5f, -0.5f, 0.5f, 1.0f, 1.0f, 1.0f, - 0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 1.0f, - -0.5f, -0.5f, 0.5f, 1.0f, 1.0f, 1.0f, - 0.5f, -0.5f, 0.5f, 1.0f, 1.0f, 1.0f, - - // right face (lime) - 0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 0.0f, - 0.5f, -0.5f, 0.5f, 0.0f, 1.0f, 0.0f, - 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, - 0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 0.0f, - 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, - 0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, - - // left face (orange) - -0.5f, -0.5f, -0.5f, 1.0f, 0.5f, 0.0f, - -0.5f, -0.5f, 0.5f, 1.0f, 0.5f, 0.0f, - -0.5f, 0.5f, 0.5f, 1.0f, 0.5f, 0.0f, - -0.5f, -0.5f, -0.5f, 1.0f, 0.5f, 0.0f, - -0.5f, 0.5f, 0.5f, 1.0f, 0.5f, 0.0f, - -0.5f, 0.5f, -0.5f, 1.0f, 0.5f, 0.0f, - }; - - auto vbo = cbt::opengl::buffer(cbt::opengl::buffer_type::vertex); - vbo.upload(vertices.data(), vertices.size() * sizeof(float)); - - // bind VAO and configure vertex attributes - auto vao = cbt::opengl::vao(); - vao.bind(); - vbo.bind(); - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), nullptr); - glEnableVertexAttribArray(1); - glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), - reinterpret_cast(3 * sizeof(float))); - vbo.unbind(); - vao.unbind(); - - // enable depth testing - glEnable(GL_DEPTH_TEST); - // signal handling asio::io_context io; asio::signal_set signals(io, SIGINT, SIGTERM); @@ -137,7 +35,7 @@ auto main(int, char const*[]) -> int { }; // render loop - auto start = std::chrono::steady_clock::now(); + auto prev = std::chrono::steady_clock::now(); while (!ctx.should_close()) { process_signals(); @@ -147,26 +45,11 @@ auto main(int, char const*[]) -> int { } auto now = std::chrono::steady_clock::now(); - auto elapsed = std::chrono::duration(now - start).count(); + auto dt = std::chrono::duration(now - prev).count(); + prev = now; - glClearColor(0.15f, 0.15f, 0.2f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - // compute MVP matrices - auto aspect = 1280.0f / 720.0f; - auto proj = glm::perspective(glm::radians(45.0f), aspect, 0.1f, 100.0f); - auto view = glm::translate(glm::mat4{1.0f}, glm::vec3{0.0f, 0.0f, -3.0f}); - auto model = glm::rotate(glm::mat4{1.0f}, elapsed, glm::vec3{1.0f, 0.5f, 0.3f}); - - prog.use(); - glUniformMatrix4fv(glGetUniformLocation(prog.id(), "u_proj"), 1, GL_FALSE, glm::value_ptr(proj)); - glUniformMatrix4fv(glGetUniformLocation(prog.id(), "u_view"), 1, GL_FALSE, glm::value_ptr(view)); - glUniformMatrix4fv(glGetUniformLocation(prog.id(), "u_model"), 1, GL_FALSE, glm::value_ptr(model)); - - vao.bind(); - glDrawArrays(GL_TRIANGLES, 0, 36); - vao.unbind(); - prog.unuse(); + scn.update(dt); + scn.render(); ctx.swap_buffers(); ctx.poll_events(); diff --git a/scenes/cube.cpp b/scenes/cube.cpp new file mode 100644 index 0000000..0867ce7 --- /dev/null +++ b/scenes/cube.cpp @@ -0,0 +1,149 @@ +#include "scenes/cube.hpp" + +#include + +#include +#include + +#include "glad/glad.h" + +namespace cbt::scenes { + +cube::cube() { + m_start = std::chrono::steady_clock::now(); +} + +auto cube::init() -> bool { + if (!build_shader()) { + return false; + } + build_mesh(); + glEnable(GL_DEPTH_TEST); + return true; +} + +auto cube::update(float) -> void {} + +auto cube::render() -> void { + auto now = std::chrono::steady_clock::now(); + auto elapsed = std::chrono::duration(now - m_start).count(); + + glClearColor(0.15f, 0.15f, 0.2f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + auto aspect = 1280.0f / 720.0f; + auto proj = glm::perspective(glm::radians(45.0f), aspect, 0.1f, 100.0f); + auto view = glm::translate(glm::mat4{1.0f}, glm::vec3{0.0f, 0.0f, -3.0f}); + auto model = glm::rotate(glm::mat4{1.0f}, elapsed, glm::vec3{1.0f, 0.5f, 0.3f}); + + m_prog.use(); + glUniformMatrix4fv(m_loc_proj, 1, GL_FALSE, glm::value_ptr(proj)); + glUniformMatrix4fv(m_loc_view, 1, GL_FALSE, glm::value_ptr(view)); + glUniformMatrix4fv(m_loc_model, 1, GL_FALSE, glm::value_ptr(model)); + + m_vao.bind(); + glDrawArrays(GL_TRIANGLES, 0, 36); + m_vao.unbind(); + m_prog.unuse(); +} + +auto cube::build_mesh() -> void { + std::array vertices = { + // front face (cyan) + 0.5f, -0.5f, 0.5f, 0.0f, 1.0f, 1.0f, + -0.5f, -0.5f, 0.5f, 0.0f, 1.0f, 1.0f, + -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 1.0f, + 0.5f, -0.5f, 0.5f, 0.0f, 1.0f, 1.0f, + -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 1.0f, + 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 1.0f, + + // back face (magenta) + 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 1.0f, + -0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 1.0f, + -0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 1.0f, + 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 1.0f, + -0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 1.0f, + 0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 1.0f, + + // top face (yellow) + 0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 0.0f, + -0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 0.0f, + -0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 0.0f, + 0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 0.0f, + -0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 0.0f, + 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 0.0f, + + // bottom face (white) + 0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 1.0f, + -0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 1.0f, + -0.5f, -0.5f, 0.5f, 1.0f, 1.0f, 1.0f, + 0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 1.0f, + -0.5f, -0.5f, 0.5f, 1.0f, 1.0f, 1.0f, + 0.5f, -0.5f, 0.5f, 1.0f, 1.0f, 1.0f, + + // right face (lime) + 0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 0.0f, + 0.5f, -0.5f, 0.5f, 0.0f, 1.0f, 0.0f, + 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, + 0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 0.0f, + 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, + 0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, + + // left face (orange) + -0.5f, -0.5f, -0.5f, 1.0f, 0.5f, 0.0f, + -0.5f, -0.5f, 0.5f, 1.0f, 0.5f, 0.0f, + -0.5f, 0.5f, 0.5f, 1.0f, 0.5f, 0.0f, + -0.5f, -0.5f, -0.5f, 1.0f, 0.5f, 0.0f, + -0.5f, 0.5f, 0.5f, 1.0f, 0.5f, 0.0f, + -0.5f, 0.5f, -0.5f, 1.0f, 0.5f, 0.0f, + }; + + m_vbo.upload(vertices.data(), vertices.size() * sizeof(float)); + + m_vao.bind(); + m_vbo.bind(); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), nullptr); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), + reinterpret_cast(3 * sizeof(float))); + m_vbo.unbind(); + m_vao.unbind(); +} + +auto cube::build_shader() -> bool { + char const* vert_src = R"glsl( + #version 410 core + layout(location = 0) in vec3 a_pos; + layout(location = 1) in vec3 a_color; + uniform mat4 u_model; + uniform mat4 u_view; + uniform mat4 u_proj; + out vec3 v_color; + void main() { + gl_Position = u_proj * u_view * u_model * vec4(a_pos, 1.0); + v_color = a_color; + } + )glsl"; + + char const* frag_src = R"glsl( + #version 410 core + in vec3 v_color; + out vec4 frag_color; + void main() { + frag_color = vec4(v_color, 1.0); + } + )glsl"; + + if (!m_prog.compile_vertex(vert_src) || !m_prog.compile_fragment(frag_src) || !m_prog.link()) { + return false; + } + + m_loc_proj = glGetUniformLocation(m_prog.id(), "u_proj"); + m_loc_view = glGetUniformLocation(m_prog.id(), "u_view"); + m_loc_model = glGetUniformLocation(m_prog.id(), "u_model"); + + return true; +} + +} diff --git a/scenes/cube.hpp b/scenes/cube.hpp new file mode 100644 index 0000000..8884087 --- /dev/null +++ b/scenes/cube.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include "cbt/scene.hpp" + +#include "cbt/opengl/shader.hpp" +#include "cbt/opengl/buffer.hpp" +#include "cbt/opengl/vao.hpp" + +#include + +namespace cbt::scenes { + +class cube final : public scene { + opengl::shader m_prog; + opengl::buffer m_vbo; + opengl::vao m_vao; + + GLint m_loc_proj = -1; + GLint m_loc_view = -1; + GLint m_loc_model = -1; + + std::chrono::steady_clock::time_point m_start; + + auto build_mesh() -> void; + auto build_shader() -> bool; + +public: + cube(); + auto init() -> bool override; + auto update(float delta_time) -> void override; + auto render() -> void override; +}; + +}