From 8d2d451543b587bbef7d5c42a127c90c511b5def Mon Sep 17 00:00:00 2001 From: WillisMedwell Date: Sun, 28 Jan 2024 15:02:20 +1100 Subject: [PATCH] Added: Static Model loading and Basic App tests (spinning square) --- .github/workflows/clang.yml | 0 .github/workflows/emscripten.yml | 0 code/Demos/CMakeLists.txt | 2 +- code/Demos/src/Main.cpp | 127 ++++++++++---- code/Engine/CMakeLists.txt | 4 +- code/Engine/include/App.hpp | 31 +++- code/Engine/include/AppRenderer.hpp | 2 + .../include/{Models => Model}/Animated.hpp | 0 .../include/{Models => Model}/Dynamic.hpp | 0 code/Engine/include/Model/Model.hpp | 6 + .../include/{Models => Model}/Static.hpp | 8 +- .../include/{Models => Model}/Types.hpp | 11 +- code/Engine/include/Models/Models.hpp | 6 - .../include/Renderer/DefaultFrameBuffer.hpp | 8 - code/Engine/include/Renderer/FrameBuffer.hpp | 13 ++ code/Engine/include/Renderer/IndexBuffer.hpp | 16 +- .../Engine/include/Renderer/OpenglContext.hpp | 1 + code/Engine/include/Renderer/Renderer.hpp | 1 - code/Engine/include/Renderer/Shader.hpp | 2 +- code/Engine/include/Renderer/Texture.hpp | 2 +- code/Engine/include/Renderer/VertexArray.hpp | 2 +- code/Engine/include/Renderer/VertexBuffer.hpp | 16 +- code/Engine/src/{Models => Model}/Static.cpp | 27 +-- code/Engine/src/Renderer/Framebuffer.cpp | 32 +++- code/Engine/src/Renderer/OpenglContext.cpp | 36 ++++ code/Engine/src/Renderer/Shader.cpp | 2 +- code/Test/assets/cube.obj | 54 ++++++ code/Test/src/Integration/BasicApps.cpp | 164 ++++++++++++++---- code/Test/src/Unit/UnitModelStatic.cpp | 26 +++ code/build-web.bat | 2 +- code/index.html | 2 +- 31 files changed, 491 insertions(+), 112 deletions(-) create mode 100644 .github/workflows/clang.yml create mode 100644 .github/workflows/emscripten.yml rename code/Engine/include/{Models => Model}/Animated.hpp (100%) rename code/Engine/include/{Models => Model}/Dynamic.hpp (100%) create mode 100644 code/Engine/include/Model/Model.hpp rename code/Engine/include/{Models => Model}/Static.hpp (75%) rename code/Engine/include/{Models => Model}/Types.hpp (54%) delete mode 100644 code/Engine/include/Models/Models.hpp delete mode 100644 code/Engine/include/Renderer/DefaultFrameBuffer.hpp rename code/Engine/src/{Models => Model}/Static.cpp (94%) create mode 100644 code/Test/assets/cube.obj create mode 100644 code/Test/src/Unit/UnitModelStatic.cpp diff --git a/.github/workflows/clang.yml b/.github/workflows/clang.yml new file mode 100644 index 0000000..e69de29 diff --git a/.github/workflows/emscripten.yml b/.github/workflows/emscripten.yml new file mode 100644 index 0000000..e69de29 diff --git a/code/Demos/CMakeLists.txt b/code/Demos/CMakeLists.txt index f0e5e84..bfdd1a4 100644 --- a/code/Demos/CMakeLists.txt +++ b/code/Demos/CMakeLists.txt @@ -23,7 +23,7 @@ if(DEFINED EMSCRIPTEN) endforeach() message(STATUS "PRELOAD FILES: \"${PRELOAD_FILES}\"") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${PRELOAD_FILES}") - target_compile_options(Demos PRIVATE -msimd128 -mrelaxed-simd -msse -msse2 -mavx) + target_compile_options(Demos PRIVATE -msimd128 -mrelaxed-simd -msse -msse2 -mavx -O0 -g -sDEMANGLE_SUPPORT=2 -sASSERTIONS=1 -sSAFE_HEAP=1 -sSTACK_OVERFLOW_CHECK=2 -sNO_DISABLE_EXCEPTION_CATCHING -Wno-unused-command-line-argument -sEXCEPTION_DEBUG=1 -v) else() file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/assets DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) target_compile_options(Demos PRIVATE -march=native) diff --git a/code/Demos/src/Main.cpp b/code/Demos/src/Main.cpp index 1cd9308..c3b00da 100644 --- a/code/Demos/src/Main.cpp +++ b/code/Demos/src/Main.cpp @@ -1,6 +1,6 @@ #include "App.hpp" #include "Config.hpp" -#include "Models/Models.hpp" +#include "Model/Model.hpp" #include #include @@ -9,48 +9,117 @@ #include -// auto print_then_quit = [](auto& error) { -// std::cerr -// << error.what() -// << std::endl; -// exit(1); -// }; - -// auto print_num_faces = [](Model::Staging::Model& model) { -// std::println("Triangle Count: {}", model.index_data.size() / 3); -// }; - -// void timeLoadModel(const std::string& file_path) { -// auto start = std::chrono::high_resolution_clock::now(); -// Models::Staging::loadModel(file_path).on_error(print_then_quit).on_value(print_num_faces); -// auto end = std::chrono::high_resolution_clock::now(); -// std::chrono::duration duration = end - start; -// std::cout << "Loading " << file_path << " took " << duration.count() << " milliseconds." << std::endl; -// } +using namespace std::literals; + +void* operator new(std::size_t size) { + std::cout << "Custom new called, size = " << size << std::endl; + void* memory = std::malloc(size); + return memory; +} + +void* operator new[](std::size_t size) { + std::cout << "Custom new[] called, size = " << size << std::endl; + void* memory = std::malloc(size); + return memory; +} struct Data { - Cameras::StationaryPerspective camera { - glm::vec3(0, 0, 0), - glm::vec3(1, 0, 0), - 90.0f - }; + std::chrono::steady_clock::time_point start_time; + constexpr static auto TRIANGLE_VERTICES = std::to_array({ 0.5f, 0.5f, 0.0f, 0.5f, -0.5f, 0.0f, -0.5f, -0.5f, 0.0f, -0.5f, 0.5f, 0.0f }); + constexpr static auto TRIANGLE_INDICES = std::to_array({ 0, 1, 3, 1, 2, 3 }); + + AppRenderer::ShaderId s_id; + AppRenderer::IndexBufferId ib_id; + AppRenderer::VertexBufferId vb_id; + AppRenderer::VertexArrayId va_id; + + Cameras::StationaryPerspective camera { glm::vec3(0, 0, -1), glm::vec3(0, 0, 1) }; + + constexpr static glm::vec4 BACKGROUND_COLOUR = { 0, 0, 0, 1 }; + + constexpr static glm::vec3 ROTATION_AXIS = { 0, 0, 1.0f }; + double angle = 0.0f; + constexpr static double ROTATIONS_PER_SECOND = 1; }; struct Logic { void init(AppRenderer& renderer, Data& data) { + data.start_time = std::chrono::high_resolution_clock::now(); + constexpr auto vert = + "precision highp float; " + "uniform mat4 u_mvp;" + "layout(location = 0) in vec3 aPos;" + "void main() {" + " gl_Position = u_mvp * vec4(aPos, 1.0);" + "}"sv; + + constexpr auto frag = + "precision highp float; " + "out vec4 FragColor; " + " void main()" + " {" + " FragColor = vec4(1.0, 0.5, 0.2, 1.0); " + " }"sv; + + renderer.add_shader(vert, frag) + .on_error(Utily::ErrorHandler::print_then_quit) + .on_value([&](auto& id) { + data.s_id = id; + }); + renderer.add_vertex_buffer() + .on_error(Utily::ErrorHandler::print_then_quit) + .on_value([&](auto& id) { + data.vb_id = id; + }); + renderer.add_index_buffer() + .on_error(Utily::ErrorHandler::print_then_quit) + .on_value([&](auto& id) { + data.ib_id = id; + }); + + renderer.add_vertex_array(Renderer::VertexBufferLayout {}, data.vb_id) + .on_error(Utily::ErrorHandler::print_then_quit) + .on_value([&](auto& id) { + data.va_id = id; + }); } - void update(float dt, const AppInput& input, AppState& state, Data& data) { - state.should_close = true; + void update(double dt, AppInput& input, AppState& state, Data& data) { + + auto duration = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - data.start_time); + data.angle = data.angle + data.ROTATIONS_PER_SECOND * 360.0 * dt; } + void draw(AppRenderer& renderer, Data& data) { - const auto proj_mat = data.camera.projection_matrix(renderer.window_width, renderer.window_height); - const auto view_mat = data.camera.view_matrix(); + renderer.screen_frame_buffer.bind(); + renderer.screen_frame_buffer.clear(data.BACKGROUND_COLOUR); + renderer.screen_frame_buffer.resize(renderer.window_width, renderer.window_height); + + Renderer::IndexBuffer& ib = renderer.index_buffers[data.ib_id.id]; + Renderer::VertexBuffer& vb = renderer.vertex_buffers[data.vb_id.id]; + Renderer::VertexArray& va = renderer.vertex_arrays[data.va_id.id]; + Renderer::Shader& s = renderer.shaders[data.s_id.id]; + + auto pm = data.camera.projection_matrix(renderer.window_width, renderer.window_height); + auto vm = data.camera.view_matrix(); + auto mm = glm::rotate(glm::mat4(1.0f), static_cast(glm::radians(data.angle)), data.ROTATION_AXIS); + + auto mvp = pm * vm * mm; + + s.bind(); + s.set_uniform("u_mvp", mvp); + va.bind(); + ib.bind(); + vb.bind(); + ib.load_indices(data.TRIANGLE_INDICES); + vb.load_vertices(data.TRIANGLE_VERTICES); + glDrawElements(GL_TRIANGLES, ib.get_count(), GL_UNSIGNED_INT, (void*)0); } + void stop() { } }; int main() { - autoRunApp(); + auto_run_app("Demo", 1000, 500); return 0; } \ No newline at end of file diff --git a/code/Engine/CMakeLists.txt b/code/Engine/CMakeLists.txt index fb1e720..9407164 100644 --- a/code/Engine/CMakeLists.txt +++ b/code/Engine/CMakeLists.txt @@ -37,10 +37,10 @@ if(DEFINED EMSCRIPTEN) ) target_include_directories(Engine PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include ${Stb_INCLUDE_DIR}) target_compile_options(Engine PUBLIC - "$<$:-O0;-g;-sDEMANGLE_SUPPORT=1;-sFORCE_FILESYSTEM=1;-sASSERTIONS=1;-sSAFE_HEAP=1;-sSTACK_OVERFLOW_CHECK=2;-sNO_DISABLE_EXCEPTION_CATCHING;-Wno-unused-command-line-argument>" + "$<$:-O0;-g3;-sDEMANGLE_SUPPORT=1;-sFORCE_FILESYSTEM=1;-sASSERTIONS=1;-sSAFE_HEAP=1;-sSTACK_OVERFLOW_CHECK=2;-sNO_DISABLE_EXCEPTION_CATCHING;-Wno-unused-command-line-argument>" "$<$:-Oz;-sGL_FFP_ONLY;-msimd128;-Wno-unused-command-line-argument>" ) - target_link_options(Engine PUBLIC -sUSE_WEBGL2=1 -sUSE_GLFW=3 -sFULL_ES3=1 -sFULL_ES2=1 -Wno-unused-command-line-argument) + target_link_options(Engine PUBLIC -sUSE_WEBGL2=1 -sUSE_GLFW=3 -sFULL_ES3=1 -sFULL_ES2=1 -Wno-unused-command-line-argument -sALLOW_MEMORY_GROWTH) else() find_package(OpenGL REQUIRED) find_package(OpenAL CONFIG REQUIRED) diff --git a/code/Engine/include/App.hpp b/code/Engine/include/App.hpp index d7dc5f5..63b6530 100644 --- a/code/Engine/include/App.hpp +++ b/code/Engine/include/App.hpp @@ -15,12 +15,15 @@ #include "AppInput.hpp" #include "AppRenderer.hpp" +#include +#include + struct AppState { bool should_close = false; }; template -concept HasValidAppLogic = requires(T t, float dt, AppState& state, AppData& data, AppRenderer& renderer, AppInput& input) { +concept HasValidAppLogic = requires(T t, double dt, AppState& state, AppData& data, AppRenderer& renderer, AppInput& input) { { t.init(renderer, data) } -> std::same_as; @@ -63,7 +66,7 @@ class App _context.stop(); } auto update() -> void { - float dt = std::chrono::duration { std::chrono::high_resolution_clock::now() - _last_update }.count(); + double dt = std::chrono::duration { std::chrono::high_resolution_clock::now() - _last_update }.count(); _logic.update(dt, _input, _state, _data); _last_update = std::chrono::high_resolution_clock::now(); } @@ -71,6 +74,7 @@ class App _renderer.window_width = _context.window_width; _renderer.window_height = _context.window_height; _logic.draw(_renderer, _data); + _context.swap_buffers(); } auto poll_events() -> void { @@ -86,13 +90,28 @@ class App }; template -void autoRunApp(std::string_view app_name = "Auto Running App") { - App app; - app.init(app_name, 400, 400); +void auto_run_app(std::string_view app_name = "Auto Running App", uint16_t width = 400, uint16_t height = 400) { + static App app; + app.init(app_name, width, height); + +#if defined(CONFIG_TARGET_NATIVE) while (app.is_running()) { app.poll_events(); app.update(); app.render(); } app.stop(); -} \ No newline at end of file +#elif defined(CONFIG_TARGET_WEB) + emscripten_set_main_loop( + []() { + if (!app.is_running()) { + emscripten_cancel_main_loop(); + } + app.poll_events(); + app.update(); + app.render(); + }, + 0, + 0); +#endif +} diff --git a/code/Engine/include/AppRenderer.hpp b/code/Engine/include/AppRenderer.hpp index 2c329f2..2f02e5d 100644 --- a/code/Engine/include/AppRenderer.hpp +++ b/code/Engine/include/AppRenderer.hpp @@ -25,6 +25,8 @@ class AppRenderer Utily::StaticVector vertex_buffers; Utily::StaticVector index_buffers; Utily::StaticVector vertex_arrays; + + Renderer::ScreenFrameBuffer screen_frame_buffer; float window_width; float window_height; diff --git a/code/Engine/include/Models/Animated.hpp b/code/Engine/include/Model/Animated.hpp similarity index 100% rename from code/Engine/include/Models/Animated.hpp rename to code/Engine/include/Model/Animated.hpp diff --git a/code/Engine/include/Models/Dynamic.hpp b/code/Engine/include/Model/Dynamic.hpp similarity index 100% rename from code/Engine/include/Models/Dynamic.hpp rename to code/Engine/include/Model/Dynamic.hpp diff --git a/code/Engine/include/Model/Model.hpp b/code/Engine/include/Model/Model.hpp new file mode 100644 index 0000000..4d8343e --- /dev/null +++ b/code/Engine/include/Model/Model.hpp @@ -0,0 +1,6 @@ +#pragma once + +#include "Model/Types.hpp" +#include "Model/Static.hpp" +#include "Model/Animated.hpp" +#include "Model/Dynamic.hpp" diff --git a/code/Engine/include/Models/Static.hpp b/code/Engine/include/Model/Static.hpp similarity index 75% rename from code/Engine/include/Models/Static.hpp rename to code/Engine/include/Model/Static.hpp index 1afbb17..da3c2e6 100644 --- a/code/Engine/include/Models/Static.hpp +++ b/code/Engine/include/Model/Static.hpp @@ -1,10 +1,10 @@ #pragma once -#include "Models/Types.hpp" +#include "Model/Types.hpp" #include "Utily/Utily.hpp" -namespace Models { - +namespace Model { + // Contiguous vertices and indices. struct Static { std::array axis_align_bounding_box; std::unique_ptr data; @@ -13,7 +13,7 @@ namespace Models { }; auto decode_as_static_model( - std::span file_data, + std::span file_data, Utily::StaticVector file_extension) -> Utily::Result; } \ No newline at end of file diff --git a/code/Engine/include/Models/Types.hpp b/code/Engine/include/Model/Types.hpp similarity index 54% rename from code/Engine/include/Models/Types.hpp rename to code/Engine/include/Model/Types.hpp index ebcc217..a631bf2 100644 --- a/code/Engine/include/Models/Types.hpp +++ b/code/Engine/include/Model/Types.hpp @@ -1,11 +1,12 @@ #pragma once #include +#include #include #include #include -namespace Models { +namespace Model { using Vec2 = glm::vec2; using Vec3 = glm::vec3; using Quat = glm::quat; @@ -15,6 +16,14 @@ namespace Models { Vec3 position; Vec3 normal; Vec2 uv_coord; + + friend auto operator<<(std::ostream& stream, const Vertex& vertex) -> std::ostream& { + stream << "v(" << vertex.position.x << ',' << vertex.position.y << ',' << vertex.position.z << ") \t" + << "n(" << vertex.normal.x << ',' << vertex.normal.y << ',' << vertex.normal.z << ") \t" + << "uv(" << vertex.uv_coord.x << ',' << vertex.uv_coord.y << ")"; + + return stream; + } }; using Index = uint32_t; diff --git a/code/Engine/include/Models/Models.hpp b/code/Engine/include/Models/Models.hpp deleted file mode 100644 index 693e36b..0000000 --- a/code/Engine/include/Models/Models.hpp +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -#include "Models/Types.hpp" -#include "Models/Static.hpp" -#include "Models/Animated.hpp" -#include "Models/Dynamic.hpp" diff --git a/code/Engine/include/Renderer/DefaultFrameBuffer.hpp b/code/Engine/include/Renderer/DefaultFrameBuffer.hpp deleted file mode 100644 index 45243fb..0000000 --- a/code/Engine/include/Renderer/DefaultFrameBuffer.hpp +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -namespace Renderer { - class DefaultFrameBuffer - { - - }; -} \ No newline at end of file diff --git a/code/Engine/include/Renderer/FrameBuffer.hpp b/code/Engine/include/Renderer/FrameBuffer.hpp index d77c1ef..fa6a15f 100644 --- a/code/Engine/include/Renderer/FrameBuffer.hpp +++ b/code/Engine/include/Renderer/FrameBuffer.hpp @@ -3,6 +3,7 @@ #include #include "Config.hpp" +#include #include namespace Renderer { @@ -26,4 +27,16 @@ namespace Renderer { std::optional _colour_attachment_index = std::nullopt; uint32_t _width { 0 }, _height { 0 }; }; + + class ScreenFrameBuffer + { + public: + static void clear(glm::vec4 colour = { 0, 0, 0, 1.0f }) noexcept; + static void bind() noexcept; + static void resize(uint32_t screen_width, uint32_t screen_height) noexcept; + + private: + constexpr static uint32_t SCREEN_ID = 0; + static uint32_t width, height; + }; } \ No newline at end of file diff --git a/code/Engine/include/Renderer/IndexBuffer.hpp b/code/Engine/include/Renderer/IndexBuffer.hpp index 860de18..d0bc069 100644 --- a/code/Engine/include/Renderer/IndexBuffer.hpp +++ b/code/Engine/include/Renderer/IndexBuffer.hpp @@ -12,12 +12,26 @@ namespace Renderer { IndexBuffer(const IndexBuffer&) = delete; IndexBuffer(IndexBuffer&& other) noexcept; - auto init() noexcept -> Utily::Result; + [[nodiscard]] auto init() noexcept -> Utily::Result; void stop() noexcept; void bind() noexcept; void unbind() noexcept; + template + requires std::ranges::range + && std::contiguous_iterator> + && std::same_as, uint32_t> + && std::ranges::sized_range + void load_indices(const Range& indices) noexcept { + this->bind(); + size_t size_in_bytes = indices.size() * sizeof(uint32_t); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, size_in_bytes, &(*indices.begin()), GL_DYNAMIC_DRAW); + _count = indices.size(); + } + + size_t get_count() const noexcept { return _count; } + private: std::optional _id = std::nullopt; size_t _count; diff --git a/code/Engine/include/Renderer/OpenglContext.hpp b/code/Engine/include/Renderer/OpenglContext.hpp index 698c68d..1dbc543 100644 --- a/code/Engine/include/Renderer/OpenglContext.hpp +++ b/code/Engine/include/Renderer/OpenglContext.hpp @@ -18,6 +18,7 @@ namespace Renderer { [[nodiscard]] auto init(std::string_view app_name, uint_fast16_t width, uint_fast16_t height) -> Utily::Result; void poll_events(); void stop(); + void swap_buffers() noexcept; [[nodiscard]] auto should_close() -> bool; uint_fast16_t window_width, window_height; diff --git a/code/Engine/include/Renderer/Renderer.hpp b/code/Engine/include/Renderer/Renderer.hpp index e5a0c02..3699f33 100644 --- a/code/Engine/include/Renderer/Renderer.hpp +++ b/code/Engine/include/Renderer/Renderer.hpp @@ -7,7 +7,6 @@ #include "Renderer/VertexArray.hpp" #include "Renderer/Shader.hpp" #include "Renderer/FrameBuffer.hpp" -#include "Renderer/DefaultFrameBuffer.hpp" #include "Renderer/Texture.hpp" namespace Renderer { diff --git a/code/Engine/include/Renderer/Shader.hpp b/code/Engine/include/Renderer/Shader.hpp index 179dea7..cd4eca9 100644 --- a/code/Engine/include/Renderer/Shader.hpp +++ b/code/Engine/include/Renderer/Shader.hpp @@ -18,7 +18,7 @@ namespace Renderer { Shader(const Shader&) = delete; Shader(Shader&&); - auto init(const std::string_view& vert, const std::string_view& frag) -> Utily::Result; + [[nodiscard]] auto init(const std::string_view& vert, const std::string_view& frag) -> Utily::Result; void stop(); void bind() noexcept; void unbind() noexcept; diff --git a/code/Engine/include/Renderer/Texture.hpp b/code/Engine/include/Renderer/Texture.hpp index e5710df..88b720d 100644 --- a/code/Engine/include/Renderer/Texture.hpp +++ b/code/Engine/include/Renderer/Texture.hpp @@ -20,7 +20,7 @@ namespace Renderer { Texture(const Texture&) = delete; Texture(Texture&&); - auto init() noexcept -> Utily::Result; + [[nodiscard]] auto init() noexcept -> Utily::Result; auto upload_image(Media::Image& image, Filter filter = Filter::smooth, bool offload_image_on_success = false) noexcept -> Utily::Result; // Once texture unit is aquired, it cannot be taken away unless unbinded() or just bind(false). diff --git a/code/Engine/include/Renderer/VertexArray.hpp b/code/Engine/include/Renderer/VertexArray.hpp index c149931..3d37e3d 100644 --- a/code/Engine/include/Renderer/VertexArray.hpp +++ b/code/Engine/include/Renderer/VertexArray.hpp @@ -15,7 +15,7 @@ namespace Renderer { VertexArray(const VertexArray&) = delete; VertexArray(VertexArray&& other); - auto init() noexcept -> Utily::Result; + [[nodiscard]] auto init() noexcept -> Utily::Result; void stop() noexcept; void bind() noexcept; diff --git a/code/Engine/include/Renderer/VertexBuffer.hpp b/code/Engine/include/Renderer/VertexBuffer.hpp index 744080f..0d09427 100644 --- a/code/Engine/include/Renderer/VertexBuffer.hpp +++ b/code/Engine/include/Renderer/VertexBuffer.hpp @@ -2,6 +2,8 @@ #include #include +#include +#include #include @@ -15,8 +17,7 @@ namespace Renderer { VertexBuffer(const VertexBuffer&) = delete; VertexBuffer(VertexBuffer&&) noexcept; - - auto init() noexcept -> Utily::Result; + [[nodiscard]] auto init() noexcept -> Utily::Result; void stop() noexcept; void bind() noexcept; @@ -24,6 +25,17 @@ namespace Renderer { ~VertexBuffer(); + template + requires std::ranges::range + && std::contiguous_iterator> + && std::same_as, float> + && std::ranges::sized_range + void load_vertices(const Range& vertices) noexcept { + this->bind(); + size_t size_in_bytes = vertices.size() * sizeof(float); + glBufferData(GL_ARRAY_BUFFER, size_in_bytes, &(*vertices.begin()), GL_DYNAMIC_DRAW); + } + private: std::optional _id = std::nullopt; }; diff --git a/code/Engine/src/Models/Static.cpp b/code/Engine/src/Model/Static.cpp similarity index 94% rename from code/Engine/src/Models/Static.cpp rename to code/Engine/src/Model/Static.cpp index 42de3dc..6e5973a 100644 --- a/code/Engine/src/Models/Static.cpp +++ b/code/Engine/src/Model/Static.cpp @@ -1,4 +1,4 @@ -#include "Models/Static.hpp" +#include "Model/Static.hpp" #include #include @@ -15,13 +15,13 @@ #include #include -namespace Models { - auto decode_as_static_model(std::span file_data, Utily::StaticVector file_extension) +namespace Model { + auto decode_as_static_model(std::span file_data, Utily::StaticVector file_extension) -> Utily::Result { constexpr auto assimp_process_flags = aiProcess_CalcTangentSpace | aiProcess_Triangulate - | aiProcess_JoinIdenticalVertices + | aiProcess_JoinIdenticalVertices // maybe bad. | aiProcess_SortByPType | aiProcess_OptimizeGraph | aiProcess_OptimizeMeshes @@ -59,32 +59,35 @@ namespace Models { if (!assimp_mesh->HasFaces() || !assimp_mesh->HasNormals() || !assimp_mesh->HasPositions()) { return Utily::Error("There was either no: faces || normals || positions"); } - + auto faces = std::span { assimp_mesh->mFaces, assimp_mesh->mNumFaces }; auto positions = std::span { assimp_mesh->mVertices, assimp_mesh->mNumVertices }; auto normals = std::span { assimp_mesh->mNormals, assimp_mesh->mNumVertices }; auto uvs = std::span { assimp_mesh->mTextureCoords[0], assimp_mesh->mNumVertices }; - + auto get_formatted_aabb = [&] { return std::array { Vec3 { static_cast(assimp_mesh->mAABB.mMin.x), static_cast(assimp_mesh->mAABB.mMin.y), static_cast(assimp_mesh->mAABB.mMin.z) }, Vec3 { static_cast(assimp_mesh->mAABB.mMax.x), static_cast(assimp_mesh->mAABB.mMax.y), static_cast(assimp_mesh->mAABB.mMax.z) }, }; }; - auto to_vert = [](auto& p, auto& n, auto& u) -> Vertex { + auto to_span = [](const aiFace& face) -> std::span { + return { face.mIndices, face.mNumIndices }; + }; + + auto to_vert = [](auto&& pnu) -> Vertex { + auto& [p, n, u] = pnu; return { .position = { p.x, p.y, p.z }, .normal = { n.x, n.y, n.z }, .uv_coord = { u.x, u.y } }; }; - auto to_span = [](const aiFace& face) -> std::span { - return { face.mIndices, face.mNumIndices }; - }; - auto vertices_view = std::views::zip_transform(to_vert, positions, normals, uvs); + auto vertices_view = std::views::zip(positions, normals, uvs) | std::views::transform(to_vert); + auto indices_view = faces | std::views::transform(to_span) | std::views::join; + auto [ptr, vert, ind] = Utily::InlineArrays::alloc_copy(std::move(vertices_view), std::move(indices_view)); - auto [ptr, vert, ind] = Utily::InlineArrays::alloc_copy(vertices_view, std::move(indices_view)); loaded_model.axis_align_bounding_box = get_formatted_aabb(); loaded_model.data = std::move(ptr); loaded_model.vertices = vert; diff --git a/code/Engine/src/Renderer/Framebuffer.cpp b/code/Engine/src/Renderer/Framebuffer.cpp index 213f92f..8ef959e 100644 --- a/code/Engine/src/Renderer/Framebuffer.cpp +++ b/code/Engine/src/Renderer/Framebuffer.cpp @@ -15,7 +15,7 @@ namespace Renderer { static Utily::StaticVector colour_attachments; - auto getUsableColourAttachment() -> Utily::Result, Utily::Error> { + auto get_usable_colour_attachment() -> Utily::Result, Utily::Error> { if (!colour_attachments.size()) { int32_t max_colour_attachments = 0; glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &max_colour_attachments); @@ -55,17 +55,17 @@ namespace Renderer { } if (!_colour_attachment_index) { - auto result = getUsableColourAttachment(); + auto result = get_usable_colour_attachment(); if (result.has_error()) { return result.error(); } _colour_attachment_index = std::get(result.value()); } - auto& [colour_rb, in_use] = colour_attachments[*_colour_attachment_index]; + auto& [colour_rb, in_use] = colour_attachments[*_colour_attachment_index]; bind(); - if(!colour_rb) { + if (!colour_rb) { colour_rb = 0; glGenRenderbuffers(1, &colour_rb.value()); } @@ -126,4 +126,28 @@ namespace Renderer { last_bound = nullptr; } } + + uint32_t ScreenFrameBuffer::width = 0; + uint32_t ScreenFrameBuffer::height = 0; + + void ScreenFrameBuffer::clear(glm::vec4 colour) noexcept { + ScreenFrameBuffer::bind(); + glClearColor(colour.x, colour.y, colour.z, colour.w); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + } + void ScreenFrameBuffer::bind() noexcept { + if (last_bound != nullptr) { + glBindBuffer(GL_FRAMEBUFFER, 0); + last_bound = nullptr; + } + } + void ScreenFrameBuffer::resize(uint32_t screen_width, uint32_t screen_height) noexcept { + if (screen_width != ScreenFrameBuffer::width || screen_height != ScreenFrameBuffer::height) { + ScreenFrameBuffer::bind(); + glViewport(0, 0, screen_width, screen_height); + ScreenFrameBuffer::width = screen_width; + ScreenFrameBuffer::height = screen_height; + } + } + } \ No newline at end of file diff --git a/code/Engine/src/Renderer/OpenglContext.cpp b/code/Engine/src/Renderer/OpenglContext.cpp index 45448cb..6be71fd 100644 --- a/code/Engine/src/Renderer/OpenglContext.cpp +++ b/code/Engine/src/Renderer/OpenglContext.cpp @@ -116,8 +116,34 @@ namespace Renderer { } } } +#if defined(CONFIG_TARGET_WEB) + static std::optional g_window = std::nullopt; +#endif auto OpenglContext::init(std::string_view app_name, uint_fast16_t width, uint_fast16_t height) -> Utily::Result { +#if defined(CONFIG_TARGET_NATIVE) + if (glfwInit() == GLFW_FALSE) { + return Utily::Error("GLFW3 failed to be initialised"); + } + + if (_window.has_value()) { + return {}; + } + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); + glfwWindowHint(GLFW_DEPTH_BITS, 24); + + _window = glfwCreateWindow(width, height, app_name.data(), NULL, NULL); + if (!_window) { + return Utily::Error("GLFW3 failed to create a window"); + } + window_width = width; + window_height = height; + glfwMakeContextCurrent(*_window); +#elif defined(CONFIG_TARGET_WEB) + if (g_window) { + _window = g_window; + return {}; + } if (glfwInit() == GLFW_FALSE) { return Utily::Error("GLFW3 failed to be initialised"); } @@ -135,6 +161,9 @@ namespace Renderer { window_height = height; glfwMakeContextCurrent(*_window); + g_window = _window; +#endif + #ifdef CONFIG_TARGET_NATIVE glewExperimental = GL_TRUE; if (glewInit() != GLEW_OK) { @@ -167,10 +196,17 @@ namespace Renderer { } void OpenglContext::stop() { +#if defined(CONFIG_TARGET_NATIVE) if (_window) { glfwDestroyWindow(_window.value()); } glfwTerminate(); +#endif + } + + void OpenglContext::swap_buffers() noexcept { + validate_window(); + glfwSwapBuffers(*_window); } void OpenglContext::poll_events() { diff --git a/code/Engine/src/Renderer/Shader.cpp b/code/Engine/src/Renderer/Shader.cpp index 0c5784b..52b7a02 100644 --- a/code/Engine/src/Renderer/Shader.cpp +++ b/code/Engine/src/Renderer/Shader.cpp @@ -113,9 +113,9 @@ namespace Renderer { } } if (last_bound != this) { - glUseProgram(_program_id.value()); last_bound = this; } + glUseProgram(_program_id.value()); } void Shader::unbind() noexcept { if constexpr (Config::SKIP_UNBINDING) { diff --git a/code/Test/assets/cube.obj b/code/Test/assets/cube.obj new file mode 100644 index 0000000..8f9df73 --- /dev/null +++ b/code/Test/assets/cube.obj @@ -0,0 +1,54 @@ +# Blender 3.6.4 +# www.blender.org +o Cube +v 1.000000 2.000000 -1.000000 +v 1.000000 0.000000 -1.000000 +v 1.000000 2.000000 1.000000 +v 1.000000 0.000000 1.000000 +v -1.000000 2.000000 -1.000000 +v -1.000000 0.000000 -1.000000 +v -1.000000 2.000000 1.000000 +v -1.000000 0.000000 1.000000 +vn -0.0000 1.0000 -0.0000 +vn -0.0000 -0.0000 1.0000 +vn -1.0000 -0.0000 -0.0000 +vn -0.0000 -1.0000 -0.0000 +vn 1.0000 -0.0000 -0.0000 +vn -0.0000 -0.0000 -1.0000 +vt 0.003906 0.329427 +vt 0.329427 0.003906 +vt 0.329427 0.329427 +vt 0.003906 0.337240 +vt 0.329427 0.662760 +vt 0.003906 0.662760 +vt 0.337240 0.329427 +vt 0.662760 0.003906 +vt 0.662760 0.329427 +vt 0.662760 0.662760 +vt 0.337240 0.337240 +vt 0.662760 0.337240 +vt 0.003906 0.670573 +vt 0.329427 0.996094 +vt 0.003906 0.996094 +vt 0.337240 0.670573 +vt 0.662760 0.996094 +vt 0.337240 0.996094 +vt 0.003906 0.003906 +vt 0.329427 0.337240 +vt 0.337240 0.003906 +vt 0.337240 0.662760 +vt 0.329427 0.670573 +vt 0.662760 0.670573 +s 0 +f 5/1/1 3/2/1 1/3/1 +f 3/4/2 8/5/2 4/6/2 +f 7/7/3 6/8/3 8/9/3 +f 2/10/4 8/11/4 6/12/4 +f 1/13/5 4/14/5 2/15/5 +f 5/16/6 2/17/6 6/18/6 +f 5/1/1 7/19/1 3/2/1 +f 3/4/2 7/20/2 8/5/2 +f 7/7/3 5/21/3 6/8/3 +f 2/10/4 4/22/4 8/11/4 +f 1/13/5 3/23/5 4/14/5 +f 5/16/6 1/24/6 2/17/6 diff --git a/code/Test/src/Integration/BasicApps.cpp b/code/Test/src/Integration/BasicApps.cpp index de6c03e..40372b7 100644 --- a/code/Test/src/Integration/BasicApps.cpp +++ b/code/Test/src/Integration/BasicApps.cpp @@ -7,11 +7,12 @@ using namespace std::literals; #include "AppAnalytics.hpp" -struct OneSecondAppData { +struct BackgroundColourData { std::chrono::steady_clock::time_point start_time; + glm::vec4 background_colour = { 0, 0, 0, 1.0f }; }; -struct OneSecondAppLogic { - void init(AppRenderer& renderer, OneSecondAppData& data) { +struct BackgroundColourLogic { + void init(AppRenderer& renderer, BackgroundColourData& data) { data.start_time = std::chrono::high_resolution_clock::now(); constexpr auto vert = @@ -35,74 +36,179 @@ struct OneSecondAppLogic { EXPECT_FALSE(renderer.add_index_buffer().has_error()); EXPECT_FALSE(renderer.add_vertex_array(VBL {}, renderer.add_vertex_buffer().value()).has_error()); } - void update(float dt, AppInput& input, AppState& state, OneSecondAppData& data) { + void update(float dt, AppInput& input, AppState& state, BackgroundColourData& data) { auto duration = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - data.start_time); if (duration > std::chrono::seconds(1)) { state.should_close = true; } + + float increment = dt * 5.0f; + + if (data.background_colour.r < 1.0f) { + data.background_colour.r += increment; + } else if (data.background_colour.g < 1.0f) { + data.background_colour.g += increment; + } else if (data.background_colour.b < 1.0f) { + data.background_colour.b += increment; + } else { + data.background_colour = { 0, 0, 0, 1.0f }; + } } - void draw(AppRenderer& renderer, OneSecondAppData& data) { + void draw(AppRenderer& renderer, BackgroundColourData& data) { + renderer.screen_frame_buffer.clear(data.background_colour); } void stop() { } }; -TEST(BasicApps, one_second_app) { - autoRunApp("Test App: One Second "); -} - -struct TriangleAppData { +struct SpinningSquareData { std::chrono::steady_clock::time_point start_time; - constexpr static auto TRIANGLE_VERTICES = std::to_array({ -0.5f, -0.5f, 0.5f, -0.5f, 0.0f, 0.5f }); - constexpr static auto TRIANGLE_INDICES = std::to_array({ 0, 1, 2 }); + constexpr static auto TRIANGLE_VERTICES = std::to_array({ 0.5f, 0.5f, 0.0f, 0.5f, -0.5f, 0.0f, -0.5f, -0.5f, 0.0f, -0.5f, 0.5f, 0.0f }); + constexpr static auto TRIANGLE_INDICES = std::to_array({ 0, 1, 3, 1, 2, 3 }); + + AppRenderer::ShaderId s_id; + AppRenderer::IndexBufferId ib_id; + AppRenderer::VertexBufferId vb_id; + AppRenderer::VertexArrayId va_id; + + Cameras::StationaryPerspective camera { glm::vec3(0, 0, -1), glm::vec3(0, 0, 1) }; + + constexpr static glm::vec4 BACKGROUND_COLOUR = { 0, 0, 0, 1 }; + constexpr static glm::vec3 ROTATION_AXIS = { 0, 0, 1.0f }; + constexpr static double ROTATIONS_PER_SECOND = 1; + double angle = 0.0f; }; -struct TriangleAppLogic { - void init(AppRenderer& renderer, TriangleAppData& data) { + +struct SpinningSquareLogic { + void init(AppRenderer& renderer, SpinningSquareData& data) { data.start_time = std::chrono::high_resolution_clock::now(); constexpr auto vert = - "precision mediump float; " + "precision highp float; " + "uniform mat4 u_mvp;" "layout(location = 0) in vec3 aPos;" "void main() {" - " gl_Position = vec4(aPos, 1.0);" + " gl_Position = u_mvp * vec4(aPos, 1.0);" "}"sv; constexpr auto frag = - "precision mediump float; " + "precision highp float; " "out vec4 FragColor; " " void main()" " {" " FragColor = vec4(1.0, 0.5, 0.2, 1.0); " " }"sv; - using VBL = Renderer::VertexBufferLayout; - - auto r = renderer.add_shader(vert, frag); - - Renderer::FrameBuffer fb1; - Renderer::FrameBuffer fb2; - EXPECT_FALSE(fb1.init(100, 100).has_error()); - EXPECT_FALSE(fb2.init(100, 100).has_error()); + renderer.add_shader(vert, frag) + .on_error(Utily::ErrorHandler::print_then_quit) + .on_value([&](auto& id) { + data.s_id = id; + }); + renderer.add_vertex_buffer() + .on_error(Utily::ErrorHandler::print_then_quit) + .on_value([&](auto& id) { + data.vb_id = id; + }); + renderer.add_index_buffer() + .on_error(Utily::ErrorHandler::print_then_quit) + .on_value([&](auto& id) { + data.ib_id = id; + }); + + renderer.add_vertex_array(Renderer::VertexBufferLayout {}, data.vb_id) + .on_error(Utily::ErrorHandler::print_then_quit) + .on_value([&](auto& id) { + data.va_id = id; + }); } - void update(float dt, AppInput& input, AppState& state, TriangleAppData& data) { + void update(double dt, AppInput& input, AppState& state, SpinningSquareData& data) { auto duration = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - data.start_time); + data.angle = data.angle + data.ROTATIONS_PER_SECOND * 360.0 * dt; if (duration > std::chrono::seconds(1)) { state.should_close = true; } } - void draw(AppRenderer& renderer, TriangleAppData& data) { + void draw(AppRenderer& renderer, SpinningSquareData& data) { + renderer.screen_frame_buffer.bind(); + renderer.screen_frame_buffer.clear(data.BACKGROUND_COLOUR); + renderer.screen_frame_buffer.resize(renderer.window_width, renderer.window_height); + + Renderer::IndexBuffer& ib = renderer.index_buffers[data.ib_id.id]; + Renderer::VertexBuffer& vb = renderer.vertex_buffers[data.vb_id.id]; + Renderer::VertexArray& va = renderer.vertex_arrays[data.va_id.id]; + Renderer::Shader& s = renderer.shaders[data.s_id.id]; + + auto pm = data.camera.projection_matrix(renderer.window_width, renderer.window_height); + auto vm = data.camera.view_matrix(); + auto mm = glm::rotate(glm::mat4(1.0f), static_cast(glm::radians(data.angle)), data.ROTATION_AXIS); + + auto mvp = pm * vm * mm; + + s.bind(); + s.set_uniform("u_mvp", mvp); + va.bind(); + ib.bind(); + vb.bind(); + ib.load_indices(data.TRIANGLE_INDICES); + vb.load_vertices(data.TRIANGLE_VERTICES); + glDrawElements(GL_TRIANGLES, ib.get_count(), GL_UNSIGNED_INT, (void*)0); } void stop() { } }; -TEST(BasicApps, triangle_app) { - autoRunApp("Test App: Triangle App"); +#if defined(CONFIG_TARGET_NATIVE) +TEST(BasicApps, background_colour) { + auto_run_app("Test App: Background Colour "); +} +TEST(BasicApps, spinning_square) { + auto_run_app("Test App: Spinning Square"); +} + +#elif defined(CONFIG_TARGET_WEB) + +void run_spinning_square() { + static App ss_app; + ss_app.init("spinner", 400, 400); + emscripten_set_main_loop( + []() { + if (!ss_app.is_running()) { + emscripten_cancel_main_loop(); + } + ss_app.poll_events(); + ss_app.update(); + ss_app.render(); + }, + 0, + 1); } + +void run_background_colour() { + static App b_app; + b_app.init("colour", 400, 400); + emscripten_set_main_loop( + []() { + if (!b_app.is_running()) { + emscripten_cancel_main_loop(); + run_spinning_square(); + } + b_app.poll_events(); + b_app.update(); + b_app.render(); + }, + 0, + 1); +} + +TEST(BasicApps, apps) { + run_background_colour(); +} + +#endif \ No newline at end of file diff --git a/code/Test/src/Unit/UnitModelStatic.cpp b/code/Test/src/Unit/UnitModelStatic.cpp new file mode 100644 index 0000000..8ea8d0a --- /dev/null +++ b/code/Test/src/Unit/UnitModelStatic.cpp @@ -0,0 +1,26 @@ +#include "Model/Model.hpp" +#include "Utily/Utily.hpp" +#include + +#include +#include + +using namespace std::literals; + +TEST(Unit, Model_static) { + const std::filesystem::path cube_path = "./assets/cube.obj"; + EXPECT_TRUE(std::filesystem::exists(cube_path)); + + auto maybe_data = Utily::FileReader::load_entire_file(cube_path); + + EXPECT_FALSE(maybe_data.has_error()); + + auto maybe_cube = Model::decode_as_static_model( + std::span { + maybe_data.value().begin(), + maybe_data.value().end() }, + { '.', 'o', 'b', 'j' }); + + EXPECT_FALSE(maybe_cube.has_error()); + //EXPECT_EQ(maybe_cube.value().vertices.size(), 36); +} diff --git a/code/build-web.bat b/code/build-web.bat index d0c1ed9..c1f8034 100644 --- a/code/build-web.bat +++ b/code/build-web.bat @@ -8,7 +8,7 @@ if not exist "build-web\" ( ) cd build-web -call emcmake cmake .. -DCMAKE_TOOLCHAIN_FILE=%VCPKG_PATH%/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=wasm32-emscripten -DEMSCRIPTEN=1 -DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=%EMSCRIPTEN%cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=%BUILD_TYPE% +@REM call emcmake cmake .. -DCMAKE_TOOLCHAIN_FILE=%VCPKG_PATH%/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=wasm32-emscripten -DEMSCRIPTEN=1 -DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=%EMSCRIPTEN%cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=%BUILD_TYPE% call cmake --build . --target Engine --config %BUILD_TYPE% call cmake --build . --target Demos --config %BUILD_TYPE% call cmake --build . --target Test --config %BUILD_TYPE% diff --git a/code/index.html b/code/index.html index f8e0299..a6f3bf5 100644 --- a/code/index.html +++ b/code/index.html @@ -6,7 +6,7 @@ -

Graphics


+