diff --git a/.clang-tidy b/.clang-tidy new file mode 100755 index 0000000..de3935e --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,148 @@ +# Generated from CLion Inspection settings +--- +Checks: '-*, +bugprone-argument-comment, +bugprone-assert-side-effect, +bugprone-bad-signal-to-kill-thread, +bugprone-branch-clone, +bugprone-copy-constructor-init, +bugprone-dangling-handle, +bugprone-dynamic-static-initializers, +bugprone-fold-init-type, +bugprone-forward-declaration-namespace, +bugprone-forwarding-reference-overload, +bugprone-inaccurate-erase, +bugprone-incorrect-roundings, +bugprone-integer-division, +bugprone-lambda-function-name, +bugprone-macro-parentheses, +bugprone-macro-repeated-side-effects, +bugprone-misplaced-operator-in-strlen-in-alloc, +bugprone-misplaced-pointer-arithmetic-in-alloc, +bugprone-misplaced-widening-cast, +bugprone-move-forwarding-reference, +bugprone-multiple-statement-macro, +bugprone-no-escape, +bugprone-not-null-terminated-result, +bugprone-parent-virtual-call, +bugprone-posix-return, +bugprone-reserved-identifier, +bugprone-sizeof-container, +bugprone-sizeof-expression, +bugprone-spuriously-wake-up-functions, +bugprone-string-constructor, +bugprone-string-integer-assignment, +bugprone-string-literal-with-embedded-nul, +bugprone-suspicious-enum-usage, +bugprone-suspicious-include, +bugprone-suspicious-memset-usage, +bugprone-suspicious-missing-comma, +bugprone-suspicious-semicolon, +bugprone-suspicious-string-compare, +bugprone-suspicious-memory-comparison, +bugprone-suspicious-realloc-usage, +bugprone-swapped-arguments, +bugprone-terminating-continue, +bugprone-throw-keyword-missing, +bugprone-too-small-loop-variable, +bugprone-undefined-memory-manipulation, +bugprone-undelegated-constructor, +bugprone-unhandled-self-assignment, +bugprone-unused-raii, +bugprone-unused-return-value, +bugprone-use-after-move, +bugprone-virtual-near-miss, +cert-dcl21-cpp, +cert-dcl58-cpp, +cert-err34-c, +cert-err52-cpp, +cert-err60-cpp, +cert-flp30-c, +cert-msc50-cpp, +cert-msc51-cpp, +cert-str34-c, +cppcoreguidelines-interfaces-global-init, +cppcoreguidelines-narrowing-conversions, +cppcoreguidelines-pro-type-member-init, +cppcoreguidelines-pro-type-static-cast-downcast, +cppcoreguidelines-slicing, +google-default-arguments, +google-explicit-constructor, +google-runtime-operator, +hicpp-exception-baseclass, +hicpp-multiway-paths-covered, +misc-misplaced-const, +misc-new-delete-overloads, +misc-no-recursion, +misc-non-copyable-objects, +misc-throw-by-value-catch-by-reference, +misc-unconventional-assign-operator, +misc-uniqueptr-reset-release, +modernize-avoid-bind, +modernize-concat-nested-namespaces, +modernize-deprecated-headers, +modernize-deprecated-ios-base-aliases, +modernize-loop-convert, +modernize-make-shared, +modernize-make-unique, +modernize-pass-by-value, +modernize-raw-string-literal, +modernize-redundant-void-arg, +modernize-replace-auto-ptr, +modernize-replace-disallow-copy-and-assign-macro, +modernize-replace-random-shuffle, +modernize-return-braced-init-list, +modernize-shrink-to-fit, +modernize-unary-static-assert, +modernize-use-auto, +modernize-use-bool-literals, +modernize-use-emplace, +modernize-use-equals-default, +modernize-use-equals-delete, +modernize-use-nodiscard, +modernize-use-noexcept, +modernize-use-nullptr, +modernize-use-override, +modernize-use-transparent-functors, +modernize-use-uncaught-exceptions, +mpi-buffer-deref, +mpi-type-mismatch, +openmp-use-default-none, +performance-faster-string-find, +performance-for-range-copy, +performance-implicit-conversion-in-loop, +performance-inefficient-algorithm, +performance-inefficient-string-concatenation, +performance-inefficient-vector-operation, +performance-move-const-arg, +performance-move-constructor-init, +performance-no-automatic-move, +performance-noexcept-move-constructor, +performance-trivially-destructible, +performance-type-promotion-in-math-fn, +performance-unnecessary-copy-initialization, +performance-unnecessary-value-param, +portability-simd-intrinsics, +readability-avoid-const-params-in-decls, +readability-const-return-type, +readability-container-size-empty, +readability-convert-member-functions-to-static, +readability-delete-null-pointer, +readability-deleted-default, +readability-inconsistent-declaration-parameter-name, +readability-make-member-function-const, +readability-misleading-indentation, +readability-misplaced-array-index, +readability-non-const-parameter, +readability-redundant-control-flow, +readability-redundant-declaration, +readability-redundant-function-ptr-dereference, +readability-redundant-smartptr-get, +readability-redundant-string-cstr, +readability-redundant-string-init, +readability-simplify-subscript-expr, +readability-static-accessed-through-handle, +readability-static-definition-in-anonymous-namespace, +readability-string-compare, +readability-uniqueptr-delete-release, +readability-use-anyofallof' \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..2dff2f0 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +cmake-build-debug diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100755 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..77ce906 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,158 @@ + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100755 index 0000000..79ee123 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100755 index 0000000..0310f9d --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,13 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..206a77e --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/point_cloud_viewer.iml b/.idea/point_cloud_viewer.iml new file mode 100644 index 0000000..f08604b --- /dev/null +++ b/.idea/point_cloud_viewer.iml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100755 index 0000000..f74a0f8 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100755 index 0000000..46d0684 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,196 @@ +cmake_minimum_required(VERSION 3.27) +project(z3d) + +set(CMAKE_CXX_STANDARD 23) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Ofast -pedantic -Wall -Werror ") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fmacro-prefix-map=${CMAKE_CURRENT_SOURCE_DIR}/=") + +add_executable(z3d main.cpp + include/assets/components/material_components.hpp + include/assets/components/point_cloud_vertex_components.hpp + include/assets/components/mesh_vertex_components.hpp + include/assets/dynamic_read_buffers/dynamic_material_buffer.hpp + include/assets/dynamic_read_buffers/dynamic_mesh_buffer.hpp + include/assets/dynamic_read_buffers/dynamic_point_cloud_buffer.hpp + include/assets/data_loaders/kitti_loader.hpp + include/assets/data_loaders/obj_loader.hpp + include/assets/data_loaders/stl_loader.hpp + include/assets/data_loaders/uos_loader.hpp + include/assets/components/mesh_vertex_components.hpp + include/scene/camera.hpp + include/scene/flying_camera.hpp + include/opengl/handles/mesh_handle.hpp + include/opengl/handles/point_cloud_handle.hpp + include/opengl/handles/shader_program_handle.hpp + include/opengl/handles/texture_handle.hpp + include/opengl/data/mesh_data.hpp + include/opengl/data/point_cloud_data.hpp + include/opengl/data/shader_program_data.hpp + include/opengl/data/texture_data.hpp + include/scene/lighting_setup.hpp + include/rendering/batch_renderers/mesh_batch_renderer.hpp + include/opengl/error.hpp + include/rendering/batches/mesh_batch.hpp + include/rendering/batches/point_cloud_batch.hpp + include/util/arx.hpp + include/util/extra_arx_parsers.hpp + include/util/for_each.hpp + include/util/function.hpp + include/util/image.hpp + include/util/logger.hpp + include/util/string_indexer.hpp + include/util/string_literal.hpp + include/util/uix.hpp + source/assets/data_loaders/stl_loader.cpp + source/assets/data_loaders/obj_loader.cpp + source/scene/flying_camera.cpp + source/opengl/data/point_cloud_data.cpp + source/opengl/handles/shader_program_handle.ipp + source/opengl/data/texture_data.ipp + source/opengl/data/mesh_data.cpp + include/opengl/error.hpp + source/opengl/data/shader_program_data.cpp + source/opengl/handles/mesh_handle.ipp + source/opengl/handles/point_cloud_handle.ipp + source/opengl/handles/texture_handle.ipp + include/assets/data/surface_properties.hpp + include/util/unroll_bool_template.hpp + include/rendering/batch_renderers/point_cloud_batch_renderer.hpp + source/rendering/batches/mesh_batch.ipp + source/rendering/batches/point_cloud_batch.ipp + include/opengl/shader_program_variable.hpp + include/opengl/type_utils.hpp + include/util/binary_ifstream.hpp + source/assets/data_loaders/kitti_loader.cpp + include/assets/data_loaders/generic/generic_3dtk_loader.hpp + include/assets/data_loaders/uosr_loader.hpp + include/assets/data_loaders/uos_normal_loader.hpp + include/assets/data_loaders/uos_rgb_loader.hpp + include/assets/dynamic_data_loaders/dynamic_mesh_loader.hpp + source/assets/dynamic_data_loaders/dynamic_mesh_loader.cpp + source/assets/dynamic_data_loaders/dynamic_mesh_loader.cpp + include/assets/dynamic_data_loaders/dynamic_point_cloud_loader.hpp + include/assets/dynamic_data_loaders/generic/base_dynamic_loader.hpp + source/assets/dynamic_data_loaders/generic/base_dynamic_loader.ipp + source/assets/dynamic_data_loaders/dynamic_point_cloud_loader.cpp + include/opengl/handles/matrix_handles.hpp + include/viewer/instance.hpp + include/opengl/data/material_data.hpp + include/opengl/handles/material_handle.hpp + include/opengl/handles/surface_properties_handle.hpp + include/opengl/handles/alpha_handle.hpp + source/opengl/data/material_data.ipp + include/util/specialised_lambda.hpp + include/viewer/asset_types.hpp + source/viewer/instance.cpp + include/viewer/asset_loader.hpp + source/viewer/asset_loader.cpp + include/assets/data_loaders/glsl_loader.hpp + include/assets/dynamic_read_buffers/dynamic_shader_buffer.hpp + source/assets/data_loaders/glsl_loader.cpp + include/viewer/settings.hpp + include/opengl/data/shader_data.hpp + source/opengl/data/shader_data.ipp + source/opengl/data/point_cloud_data.ipp + source/opengl/data/shader_data.cpp + include/opengl/handles/shader_handle.hpp + include/util/string_lookup.hpp + include/viewer/dynamic_shader_program_loading.hpp + source/viewer/dynamic_shader_program_loading.cpp + source/opengl/handles/shader_program_handle.cpp + include/opengl/shader_program_lookup.hpp + source/opengl/shader_program_lookup.cpp + include/shader_program/attributes/mesh_attributes.hpp + include/shader_program/attributes/point_cloud_attributes.hpp + include/shader_program/uniforms/mesh_uniforms.hpp + include/shader_program/uniforms/point_cloud_uniforms.hpp + include/shader_program/capabilities/mesh_capabilities.hpp + include/shader_program/capabilities/point_cloud_capabilities.hpp + include/rendering/requirements/mesh_requirements.hpp + include/rendering/requirements/point_cloud_requirements.hpp + include/rendering/modes/mesh_modes.hpp + include/rendering/modes/point_cloud_modes.hpp + source/rendering/batch_renderers/mesh_batch_renderer.cpp + source/rendering/batch_renderers/point_cloud_batch_renderer.cpp + include/rendering/shader_program_lookups/mesh_lookup.hpp + include/rendering/shader_program_lookups/mesh_lookup.hpp + source/rendering/shader_program_lookups/mesh_lookup.cpp + source/rendering/shader_program_lookups/point_cloud_lookup.cpp + include/rendering/shader_program_lookups/point_cloud_lookup.hpp + include/geometry/aabb.hpp + source/geometry/normal_estimation.cpp + include/geometry/normal_estimation.hpp + include/assets/components/texture_components.hpp + include/util/enum_operators.hpp + include/assets/dynamic_read_buffers/dynamic_texture_buffer.hpp + source/assets/dynamic_read_buffers/dynamic_mesh_buffer.ipp + source/assets/dynamic_read_buffers/dynamic_material_buffer.ipp + include/assets/dynamic_read_buffers/dynamic_vertex_buffer.hpp + source/assets/dynamic_read_buffers/dynamic_model_buffer.ipp + source/assets/dynamic_read_buffers/dynamic_texture_buffer.ipp + source/assets/dynamic_read_buffers/dynamic_point_cloud_buffer.ipp + include/assets/data_loaders/mtl_loader.hpp + include/assets/dynamic_data_loaders/dynamic_material_loader.hpp + include/util/id_type.hpp + include/assets/dynamic_data_stores/generic/generic_dynamic_store.hpp + source/assets/dynamic_data_stores/generic/generic_dynamic_store.ipp + include/assets/dynamic_data_stores/dynamic_material_store.hpp + include/assets/dynamic_data_stores/dynamic_mesh_store.hpp + include/assets/dynamic_data_stores/dynamic_point_cloud_store.hpp + include/util/result.hpp + source/assets/data_loaders/mtl_loader.cpp + include/util/line_parser.hpp + include/assets/dynamic_data_loaders/dynamic_texture_loader.hpp + source/assets/dynamic_data_loaders/dynamic_texture_loader.cpp + include/assets/dynamic_data_stores/dynamic_texture_store.hpp + include/opengl/data_uploaders/texture_data_uploader.hpp + include/assets/dynamic_data_stores/dynamic_vertex_store.hpp + include/assets/prefetch_lookups/mesh_prefetch_lookup.hpp + include/assets/prefetch_lookups/point_cloud_prefetch_lookup.hpp + include/assets/prefetch_lookups/material_prefetch_lookup.hpp + include/assets/prefetch_lookups/texture_prefetch_lookup.hpp + include/assets/prefetch_lookups/pose_prefetch_lookup.hpp + include/assets/dynamic_data_stores/dynamic_pose_store.hpp + include/assets/data_loaders/kitti_pose_loader.hpp + include/assets/data_loaders/threedtk_pose_loader.hpp + source/assets/data_loaders/threedtk_pose_loader.cpp + include/assets/dynamic_read_buffers/dynamic_pose_buffer.hpp + source/assets/data_loaders/kitti_pose_loader.cpp + include/assets/prefetch_queue.hpp + include/util/string_list.hpp + include/assets/dynamic_data_stores/dynamic_material_library_store.hpp + include/assets/dynamic_data_stores/generic/generic_dynamic_component_store.hpp + source/assets/dynamic_data_stores/generic/generic_dynamic_component_store.ipp + source/assets/dynamic_data_stores/dynamic_material_store.cpp + include/assets/dynamic_data_stores/generic/generic_dynamic_component_array_store.hpp + source/assets/dynamic_data_stores/generic/generic_dynamic_component_array_store.ipp + source/assets/dynamic_data_stores/dynamic_mesh_store.cpp + include/assets/dynamic_data_stores/generic/generic_dynamic_indexed_component_array_store.hpp + source/assets/dynamic_data_stores/dynamic_point_cloud_store.cpp + include/assets/dynamic_read_buffers/dynamic_material_library_buffer.hpp + include/assets/dynamic_data_loaders/dynamic_material_library_loader.hpp + include/assets/prefetch_lookups/material_library_prefetch_lookup.hpp + include/assets/prefetch_lookups/shader_prefetch_lookup.hpp + include/assets/dynamic_data_stores/dynamic_shader_store.hpp + include/assets/data_loaders/generic/generic_3dtk_loader.hpp + source/assets/data_loaders/generic/generic_3dtk_loader.ipp + include/assets/prefetch_lookup.hpp + source/assets/prefetch_lookups/pose_prefetch_lookup.cpp + include/assets/dynamic_data_store.hpp +) + +target_include_directories(z3d PRIVATE include) +target_include_directories(z3d PRIVATE source) # for ipp headers +target_include_directories(z3d PRIVATE libraries/include/glm) +target_include_directories(z3d PRIVATE libraries/include/stb) + +find_package(GLEW REQUIRED) +find_package(OpenGL REQUIRED) +find_package(SFML REQUIRED COMPONENTS graphics system) +find_package(Eigen3 3.4 REQUIRED NO_MODULE) + +include_directories(${SFML_INCLUDE_DIR}) +include_directories(SYSTEM ${eigen_INCLUDE_DIR}) +target_link_libraries(z3d Eigen3::Eigen sfml-graphics sfml-system sfml-window ${OPENGL_LIBRARIES} ${GLEW_LIBRARIES}) diff --git a/data/fonts/JetBrainsMono_Medium.ttf b/data/fonts/JetBrainsMono_Medium.ttf new file mode 100755 index 0000000..a6ba552 Binary files /dev/null and b/data/fonts/JetBrainsMono_Medium.ttf differ diff --git a/include/assets/components/material_components.hpp b/include/assets/components/material_components.hpp new file mode 100644 index 0000000..e66dfbe --- /dev/null +++ b/include/assets/components/material_components.hpp @@ -0,0 +1,59 @@ +#pragma once + +#include +#include "../dynamic_read_buffers" +#include "assets/data/surface_properties.hpp" +#include "util/enum_operators.hpp" + +namespace components::material +{ +using surface_properties = ::surface_properties; +using transparency = float; +using ambient_color_texture = ::dynamic_texture_data; +using diffuse_color_texture = ::dynamic_texture_data; +using specular_color_texture = ::dynamic_texture_data; +using shininess_texture = ::dynamic_texture_data; +using alpha_texture = ::dynamic_texture_data; +using bump_texture = ::dynamic_texture_data; + +namespace indices +{ +using type = std::size_t; +inline constexpr type surface_properties = 0; +inline constexpr type transparency = 1; +inline constexpr type ambient_color_texture = 2; +inline constexpr type diffuse_color_texture = 3; +inline constexpr type specular_color_texture = 4; +inline constexpr type shininess_texture = 5; +inline constexpr type alpha_texture = 6; +inline constexpr type bump_texture = 7; +} + +enum class flags : std::uint8_t +{ + none = 0, + surface_properties = 1 << indices::surface_properties, + transparency = 1 << indices::transparency, + ambient_filter_texture = 1 << indices::ambient_color_texture, + diffuse_filter_texture = 1 << indices::diffuse_color_texture, + specular_filter_texture = 1 << indices::specular_color_texture, + shininess_texture = 1 << indices::shininess_texture, + alpha_texture = 1 << indices::alpha_texture, + bump_texture = 1 << indices::bump_texture +}; + +using all = std::tuple< + surface_properties, + transparency, + ambient_color_texture, + diffuse_color_texture, + specular_color_texture, + shininess_texture, + alpha_texture, + bump_texture +>; +constexpr inline auto count = std::tuple_size_v; + +} // namespace material_component + +DEFINE_ENUM_FLAG_OPERATORS(components::material::flags) diff --git a/include/assets/components/mesh_vertex_components.hpp b/include/assets/components/mesh_vertex_components.hpp new file mode 100755 index 0000000..ff578b8 --- /dev/null +++ b/include/assets/components/mesh_vertex_components.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include +#include +#include "util/enum_operators.hpp" + +namespace components::mesh_vertex { + +using position = std::array; +using normal = std::array; +using tex_coord = std::array; +using color = std::array; +using reflectance = std::array; + +namespace indices +{ +using type = std::size_t; +inline constexpr type position = 0; +inline constexpr type normal = 1; +inline constexpr type tex_coord = 2; +inline constexpr type color = 3; +inline constexpr type reflectance = 4; +} + +enum class flags : std::uint8_t { + none = 0, + position = 1 << indices::position, + normal = 1 << indices::normal, + tex_coord = 1 << indices::tex_coord, + color = 1 << indices::color, + reflectance = 1 << indices::reflectance +}; + +using all = std::tuple; +constexpr inline auto count = std::tuple_size_v; + +} // namespace components::mesh_vertex + +DEFINE_ENUM_FLAG_OPERATORS(components::mesh_vertex::flags) diff --git a/include/assets/components/point_cloud_vertex_components.hpp b/include/assets/components/point_cloud_vertex_components.hpp new file mode 100755 index 0000000..98e3ca1 --- /dev/null +++ b/include/assets/components/point_cloud_vertex_components.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include "util/enum_operators.hpp" + +namespace components::point_cloud_vertex { + +using position = std::array; +using normal = std::array; +using color = std::array; +using reflectance = std::array; + +namespace indices +{ +using type = std::size_t; +inline constexpr type position = 0; +inline constexpr type normal = 1; +inline constexpr type color = 2; +inline constexpr type reflectance = 3; +} // namespace indices + +enum class flags : std::uint8_t { + none = 0, + position = 1 << indices::position, + normal = 1 << indices::normal, + color = 1 << indices::color, + reflectance = 1 << indices::reflectance +}; + +using all = std::tuple; +constexpr inline auto count = std::tuple_size_v; + +} // namespace components::point_cloud_vertex + +DEFINE_ENUM_FLAG_OPERATORS(components::point_cloud_vertex::flags) diff --git a/include/assets/components/texture_components.hpp b/include/assets/components/texture_components.hpp new file mode 100644 index 0000000..f560d31 --- /dev/null +++ b/include/assets/components/texture_components.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include +#include +#include "util/enum_operators.hpp" + +namespace components::texture { + +using red = std::uint8_t; +using green = std::uint8_t; +using blue = std::uint8_t; +using luminance = std::uint8_t; + +enum class flags : std::uint8_t { + none = 0, + luminance = 1 << 1, + red = 1 << 2, + green = 1 << 3, + blue = 1 << 4, + alpha = 1 << 5 +}; + +using all = std::tuple; +constexpr inline auto count = std::tuple_size_v; + +} // namespace components::texture + +DEFINE_ENUM_FLAG_OPERATORS(components::texture::flags) \ No newline at end of file diff --git a/include/assets/data/surface_properties.hpp b/include/assets/data/surface_properties.hpp new file mode 100644 index 0000000..dce0c1e --- /dev/null +++ b/include/assets/data/surface_properties.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include + +struct surface_properties +{ + std::array ambient_filter{ 0.7f, 0.7f, 0.7f }; + std::array diffuse_filter{ 0.466f, 0.466f, 0.7922f }; + std::array specular_filter{ 0.5974f, 0.2084f, 0.2084f }; + float shininess{ 100.2237f }; +}; \ No newline at end of file diff --git a/include/assets/data_loaders/glsl_loader.hpp b/include/assets/data_loaders/glsl_loader.hpp new file mode 100644 index 0000000..d585c09 --- /dev/null +++ b/include/assets/data_loaders/glsl_loader.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include +#include +#include + +#include "assets/dynamic_data_store.hpp" +#include "assets/prefetch_lookup.hpp" +#include "assets/prefetch_queue.hpp" +#include "assets/prefetch_lookups/mesh_prefetch_lookup.hpp" +#include "assets/dynamic_read_buffers/dynamic_shader_buffer.hpp" +#include "util/string_list.hpp" + +struct glsl_loader +{ + static constexpr auto name = std::string_view("glsl"); + + [[nodiscard]] static std::error_code prefetch( + const file_dir_list& paths, + prefetch_queue& queue + ); + + [[nodiscard]] static std::error_code load( + dynamic_shader_buffer& buffer, + const file_dir_list& paths, + prefetch_lookup& id_lookup, + dynamic_data_store& store, + bool pedantic = false + ); +}; \ No newline at end of file diff --git a/include/assets/data_loaders/kitti_loader.hpp b/include/assets/data_loaders/kitti_loader.hpp new file mode 100644 index 0000000..3948380 --- /dev/null +++ b/include/assets/data_loaders/kitti_loader.hpp @@ -0,0 +1,53 @@ +#pragma once + +#include +#include +#include + +#include "assets/dynamic_data_store.hpp" +#include "assets/components/point_cloud_vertex_components.hpp" +#include "assets/dynamic_read_buffers/dynamic_point_cloud_buffer.hpp" +#include "assets/dynamic_data_stores/dynamic_point_cloud_store.hpp" +#include "assets/prefetch_lookup.hpp" +#include "assets/prefetch_queue.hpp" +#include "glm/mat4x4.hpp" +#include "util/result.hpp" + +struct kitti_loader +{ + static constexpr auto name = std::string_view("kitti"); + + [[nodiscard]] static std::error_code prefetch( + const file_dir_list& paths, + prefetch_queue& queue + ); + + [[nodiscard]] static std::error_code load( + dynamic_point_cloud_buffer& buffer, + const file_dir_list& paths, + prefetch_lookup& id_lookup, + dynamic_data_store& store, + bool pedantic = false + ); + +private: + inline static constexpr auto frame_folder = "frames"; + + [[nodiscard]] static ztu::result frame_id_from_filename( + std::string_view filename + ); + + [[nodiscard]] static std::error_code load_point_file( + const std::filesystem::path& filename, + dynamic_point_cloud_buffer& point_cloud + ); + + static void transform_point_cloud( + std::span points, + const glm::mat4& pose + ); + + [[nodiscard]] static ztu::result parent_directory(std::string_view path); + + +}; diff --git a/include/assets/data_loaders/kitti_pose_loader.hpp b/include/assets/data_loaders/kitti_pose_loader.hpp new file mode 100644 index 0000000..ce553c1 --- /dev/null +++ b/include/assets/data_loaders/kitti_pose_loader.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include + +#include "assets/dynamic_data_store.hpp" +#include "assets/prefetch_lookup.hpp" +#include "assets/prefetch_queue.hpp" +#include "util/string_list.hpp" +#include "assets/dynamic_data_stores/dynamic_pose_store.hpp" +#include "assets/dynamic_read_buffers/dynamic_pose_buffer.hpp" +#include "assets/prefetch_lookups/pose_prefetch_lookup.hpp" + +struct kitti_pose_loader +{ + static constexpr auto name = std::string_view("kitti_pose"); + + [[nodiscard]] static std::error_code prefetch( + const file_dir_list& paths, + prefetch_queue& queue + ); + + [[nodiscard]] static std::error_code load( + dynamic_pose_buffer& buffer, + const file_dir_list& paths, + prefetch_lookup& id_lookup, + dynamic_data_store& store, + bool pedantic = false + ); + +private: + static constexpr auto pose_filename = std::string_view{ "pose.txt" }; + + static std::error_code parse_pose( + std::ifstream& in, + dynamic_pose_buffer& pose + ); +}; \ No newline at end of file diff --git a/include/assets/data_loaders/mtl_loader.hpp b/include/assets/data_loaders/mtl_loader.hpp new file mode 100644 index 0000000..dd2df5f --- /dev/null +++ b/include/assets/data_loaders/mtl_loader.hpp @@ -0,0 +1,56 @@ +#pragma once + +#include +#include + +#include "assets/dynamic_data_store.hpp" +#include "assets/prefetch_lookup.hpp" +#include "assets/prefetch_queue.hpp" +#include "assets/dynamic_read_buffers/dynamic_material_buffer.hpp" +#include "assets/dynamic_data_stores/dynamic_material_library_store.hpp" +#include "assets/dynamic_data_stores/dynamic_material_store.hpp" +#include "util/string_lookup.hpp" +#include "util/result.hpp" + +namespace mtl_loader_error { + +enum class codes { + ok = 0, + mtl_cannot_open_file, + mtl_cannot_open_texture, + mtl_malformed_ambient_color, + mtl_malformed_diffuse_color, + mtl_malformed_specular_color, + mtl_malformed_specular_exponent, + mtl_malformed_dissolve, + mlt_unknown_line_begin +}; + +} // namespace mtl_loader_error + +class mtl_loader { +public: + static constexpr auto name = std::string_view("mtl"); + + std::optional find_id(std::string_view name); + + void clear_name_lookup(); + + [[nodiscard]] static std::error_code prefetch( + const file_dir_list& paths, + prefetch_queue& queue + ); + + // THis is not very elegant, but right now I do not see a better solution... + [[nodiscard]] static std::error_code load( + dynamic_material_library_buffer& material_library_buffer, + const file_dir_list& paths, + prefetch_lookup& id_lookup, + dynamic_data_store& store, + bool pedantic = false + ); + + +private: + ztu::string_lookup m_id_lookup; +}; diff --git a/include/assets/data_loaders/obj_loader.hpp b/include/assets/data_loaders/obj_loader.hpp new file mode 100755 index 0000000..7bd95e6 --- /dev/null +++ b/include/assets/data_loaders/obj_loader.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include +#include +#include +#include "assets/dynamic_data_loaders/dynamic_material_loader.hpp" +#include "assets/dynamic_data_stores/dynamic_mesh_store.hpp" +#include "assets/prefetch_lookup.hpp" + +namespace obj_loader_error { + +enum class codes { + ok = 0, + obj_cannot_open_file, + obj_malformed_vertex, + obj_malformed_texture_coordinate, + obj_malformed_normal, + obj_malformed_face, + obj_face_index_out_of_range, + obj_unknown_line_begin +}; + +} // namespace obj_loader_error + +struct obj_loader { + + static constexpr auto name = std::string_view("obj"); + + [[nodiscard]] static std::error_code prefetch( + const file_dir_list& paths, + prefetch_queue& queue + ); + + [[nodiscard]] static std::error_code load( + components::mesh_vertex::flags enabled_components, + dynamic_mesh_buffer& buffer, + const file_dir_list& paths, + prefetch_lookup& id_lookup, + dynamic_data_store& store, + bool pedantic = false + ); +}; diff --git a/include/assets/data_loaders/stl_loader.hpp b/include/assets/data_loaders/stl_loader.hpp new file mode 100644 index 0000000..4c0b1f1 --- /dev/null +++ b/include/assets/data_loaders/stl_loader.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include +#include +#include + +#include "assets/dynamic_data_store.hpp" +#include "assets/dynamic_read_buffers/dynamic_mesh_buffer.hpp" +#include "assets/dynamic_data_stores/dynamic_mesh_store.hpp" +#include "assets/prefetch_lookup.hpp" +#include "assets/prefetch_queue.hpp" + +struct stl_loader { + + static constexpr auto name = std::string_view("stl"); + + [[nodiscard]] static std::error_code prefetch( + const file_dir_list& paths, + prefetch_queue& queue + ); + + [[nodiscard]] static std::error_code load( + // space stuff that has to persist + dynamic_mesh_buffer& buffer, + const file_dir_list& paths, + prefetch_lookup& id_lookup, + dynamic_data_store& store, + bool pedantic = false + ); +}; diff --git a/include/assets/data_loaders/threedtk_pose_loader.hpp b/include/assets/data_loaders/threedtk_pose_loader.hpp new file mode 100644 index 0000000..7365857 --- /dev/null +++ b/include/assets/data_loaders/threedtk_pose_loader.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include + +#include "assets/dynamic_data_store.hpp" +#include "assets/prefetch_lookup.hpp" +#include "assets/prefetch_queue.hpp" +#include "assets/dynamic_read_buffers/dynamic_pose_buffer.hpp" +#include "util/string_list.hpp" +#include "util/result.hpp" +#include "assets/prefetch_lookups/pose_prefetch_lookup.hpp" + +struct threedtk_pose_loader +{ + static constexpr auto name = std::string_view("3dtk_pose"); + + [[nodiscard]] static std::error_code prefetch( + const file_dir_list& paths, + prefetch_queue& queue + ); + + [[nodiscard]] static std::error_code load( + dynamic_pose_buffer& buffer, + const file_dir_list& paths, + prefetch_lookup& id_lookup, + dynamic_data_store& store, + bool pedantic = false + ); + +protected: + static std::error_code parse_transform_info( + std::ifstream& in, + std::string& line, + std::array& transform_info + ); + + static ztu::result parse_index( + std::string_view filename + ); +}; diff --git a/include/assets/data_loaders/uos_loader.hpp b/include/assets/data_loaders/uos_loader.hpp new file mode 100644 index 0000000..7d1e6ae --- /dev/null +++ b/include/assets/data_loaders/uos_loader.hpp @@ -0,0 +1,9 @@ +#pragma once + +#include "generic/generic_3dtk_loader.hpp" + +struct uos_loader : generic_3dtk_loader +{ + static constexpr auto name = std::string_view("uos"); +}; + diff --git a/include/assets/data_loaders/uos_normal_loader.hpp b/include/assets/data_loaders/uos_normal_loader.hpp new file mode 100644 index 0000000..5c95b04 --- /dev/null +++ b/include/assets/data_loaders/uos_normal_loader.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include "generic/generic_3dtk_loader.hpp" + +struct uos_normal_loader : generic_3dtk_loader +{ + static constexpr auto name = std::string_view("uos_normal"); +}; diff --git a/include/assets/data_loaders/uos_rgb_loader.hpp b/include/assets/data_loaders/uos_rgb_loader.hpp new file mode 100644 index 0000000..f49b79e --- /dev/null +++ b/include/assets/data_loaders/uos_rgb_loader.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include "generic/generic_3dtk_loader.hpp" + +struct uos_rgb_loader : generic_3dtk_loader +{ + static constexpr auto name = std::string_view("uos_rgb"); +}; diff --git a/include/assets/data_loaders/uosr_loader.hpp b/include/assets/data_loaders/uosr_loader.hpp new file mode 100644 index 0000000..c59f6e5 --- /dev/null +++ b/include/assets/data_loaders/uosr_loader.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include "generic/generic_3dtk_loader.hpp" + +struct uosr_loader : generic_3dtk_loader +{ + static constexpr auto name = std::string_view("uosr"); +}; diff --git a/include/assets/dynamic_data_loaders/dynamic_material_library_loader.hpp b/include/assets/dynamic_data_loaders/dynamic_material_library_loader.hpp new file mode 100644 index 0000000..8c508ab --- /dev/null +++ b/include/assets/dynamic_data_loaders/dynamic_material_library_loader.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include "assets/prefetch_queue.hpp" +#include "assets/components/material_components.hpp" +#include "assets/dynamic_read_buffers/dynamic_material_library_buffer.hpp" +#include "assets/dynamic_data_stores/dynamic_material_library_store.hpp" +#include "assets/prefetch_lookups/material_library_prefetch_lookup.hpp" +#include "generic/base_dynamic_loader.hpp" + +#include "assets/data_loaders/mtl_loader.hpp" +#include "util/string_list.hpp" + +class dynamic_material_library_loader : public base_dynamic_loader< + components::material::flags, + mtl_loader +> { +public: + + [[nodiscard]] std::error_code prefetch( + loader_id_type loader_id, + const ztu::string_list& directories, + prefetch_queue& queue + ); + + [[nodiscard]] std::error_code load( + loader_id_type loader_id, + const ztu::string_list& directories, + dynamic_material_library_store& store, + dynamic_material_store& material_store, + material_library_prefetch_lookup& id_lookup, + bool pedantic = false + ); + +private: + dynamic_material_library_buffer m_buffer{}; +}; diff --git a/include/assets/dynamic_data_loaders/dynamic_material_loader.hpp b/include/assets/dynamic_data_loaders/dynamic_material_loader.hpp new file mode 100644 index 0000000..934f87f --- /dev/null +++ b/include/assets/dynamic_data_loaders/dynamic_material_loader.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include "assets/prefetch_queue.hpp" +#include "assets/components/material_components.hpp" +#include "generic/base_dynamic_loader.hpp" + +#include "assets/data_loaders/mtl_loader.hpp" +#include "assets/dynamic_data_stores/dynamic_material_store.hpp" +#include "assets/prefetch_lookups/material_prefetch_lookup.hpp" +#include "util/string_list.hpp" + +class dynamic_material_loader : public base_dynamic_loader< + components::material::flags + // TODO no loaders +> { +public: + + [[nodiscard]] std::error_code prefetch( + loader_id_type loader_id, + const ztu::string_list& directories, + prefetch_queue& queue + ); + + [[nodiscard]] std::error_code load( + loader_id_type loader_id, + const ztu::string_list& directories, + dynamic_material_store& store, + material_prefetch_lookup& id_lookup, + bool pedantic = false + ); + +private: + dynamic_material_buffer m_buffer{}; +}; diff --git a/include/assets/dynamic_data_loaders/dynamic_mesh_loader.hpp b/include/assets/dynamic_data_loaders/dynamic_mesh_loader.hpp new file mode 100644 index 0000000..3d02ed4 --- /dev/null +++ b/include/assets/dynamic_data_loaders/dynamic_mesh_loader.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include "generic/base_dynamic_loader.hpp" +#include + +#include "assets/dynamic_read_buffers/dynamic_mesh_buffer.hpp" +#include "assets/dynamic_data_stores/dynamic_mesh_store.hpp" +#include "assets/data_loaders/obj_loader.hpp" +#include "assets/data_loaders/stl_loader.hpp" +#include "assets/prefetch_lookups/mesh_prefetch_lookup.hpp" + + +class dynamic_mesh_loader : public base_dynamic_loader< + components::mesh_vertex::flags, + obj_loader, + stl_loader +> { +public: + [[nodiscard]] std::error_code prefetch( + loader_id_type loader_id, + const ztu::string_list& directories, + prefetch_queue& queue + ); + + [[nodiscard]] std::error_code load( + loader_id_type loader_id, + const ztu::string_list& directories, + dynamic_mesh_store& store, + mesh_prefetch_lookup& id_lookup, + bool pedantic = false + ); + +private: + dynamic_mesh_buffer m_buffer{}; +}; diff --git a/include/assets/dynamic_data_loaders/dynamic_point_cloud_loader.hpp b/include/assets/dynamic_data_loaders/dynamic_point_cloud_loader.hpp new file mode 100644 index 0000000..7c7b601 --- /dev/null +++ b/include/assets/dynamic_data_loaders/dynamic_point_cloud_loader.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include +#include + +#include "assets/prefetch_queue.hpp" +#include "generic/base_dynamic_loader.hpp" +#include "assets/dynamic_read_buffers/dynamic_point_cloud_buffer.hpp" +#include "assets/dynamic_data_stores/dynamic_point_cloud_store.hpp" + +#include "assets/data_loaders/kitti_loader.hpp" +#include "assets/data_loaders/uos_loader.hpp" +#include "assets/data_loaders/uos_normal_loader.hpp" +#include "assets/data_loaders/uos_rgb_loader.hpp" +#include "assets/data_loaders/uosr_loader.hpp" +#include "assets/prefetch_lookups/point_cloud_prefetch_lookup.hpp" + +#include "util/string_list.hpp" + +class dynamic_point_cloud_loader : public base_dynamic_loader< + components::point_cloud_vertex::flags, + kitti_loader, + uos_loader, + uos_normal_loader, + uos_rgb_loader, + uos_loader, + uosr_loader +> { +public: + [[nodiscard]] std::error_code prefetch( + loader_id_type loader_id, + const ztu::string_list& directories, + prefetch_queue& queue + ); + + [[nodiscard]] std::error_code load( + loader_id_type loader_id, + const ztu::string_list& directories, + dynamic_point_cloud_store& store, + point_cloud_prefetch_lookup& id_lookup, + bool pedantic = false + ); + +private: + dynamic_point_cloud_buffer m_buffer{}; +}; diff --git a/include/assets/dynamic_data_loaders/dynamic_texture_loader.hpp b/include/assets/dynamic_data_loaders/dynamic_texture_loader.hpp new file mode 100644 index 0000000..a703253 --- /dev/null +++ b/include/assets/dynamic_data_loaders/dynamic_texture_loader.hpp @@ -0,0 +1,60 @@ +#pragma once + +#include +#include +#include "util/uix.hpp" +#include "util/id_type.hpp" +#include "util/string_lookup.hpp" +#include "util/result.hpp" + +#include "assets/components/texture_components.hpp" +#include "assets/dynamic_read_buffers/dynamic_texture_buffer.hpp" +#include "assets/dynamic_data_stores/dynamic_texture_store.hpp" +#include "assets/dynamic_data_loader_ctx.hpp" +#include "assets/prefetch_queue.hpp" +#include "assets/prefetch_lookups/texture_prefetch_lookup.hpp" +#include "util/string_list.hpp" + +/* +* [[nodiscard]] std::error_code prefetch( +loader_id_type loader_id, +const ztu::string_list& directories, +prefetch_queue& queue +); + +[[nodiscard]] std::error_code load( +loader_id_type loader_id, +const ztu::string_list& directories, +dynamic_point_cloud_store& store, +point_cloud_prefetch_lookup& id_lookup, +bool pedantic = false +); +*/ + +class dynamic_texture_loader +{ +public: + using loader_id_type = ztu::id_type_for; + + explicit dynamic_texture_loader(components::texture::flags enabled_components); + + [[nodiscard]] std::optional find_loader(const std::string_view& name); + + [[nodiscard]] std::error_code prefetch( + loader_id_type loader_id, + const ztu::string_list& directories, + prefetch_queue& queue + ); + + [[nodiscard]] std::error_code load( + loader_id_type loader_id, + const ztu::string_list& directories, + dynamic_texture_store& store, + texture_prefetch_lookup& id_lookup, + bool pedantic = false + ); + +private: + ztu::string_lookup m_loader_id_lookup{}; + components::texture::flags m_enabled_components{ components::texture::flags::none }; +}; diff --git a/include/assets/dynamic_data_loaders/generic/base_dynamic_loader.hpp b/include/assets/dynamic_data_loaders/generic/base_dynamic_loader.hpp new file mode 100644 index 0000000..bf0d819 --- /dev/null +++ b/include/assets/dynamic_data_loaders/generic/base_dynamic_loader.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include +#include + +#include "assets/dynamic_data_stores/dynamic_point_cloud_store.hpp" +#include "util/uix.hpp" +#include "util/string_lookup.hpp" +#include "util/id_type.hpp" +#include "util/result.hpp" + +template +class base_dynamic_loader +{ +public: + using loader_id_type = ztu::id_type_for; + + explicit base_dynamic_loader(C enabled_components); + + [[nodiscard]] std::optional find_loader(std::string_view name); + + [[nodiscard]] static consteval std::optional find_loader_static(std::string_view name); + + template + auto& get_loader(); + +protected: + + template + ztu::result invoke_with_matching_loader(loader_id_type loader_id, F&& f); + + std::tuple m_loaders{}; + ztu::string_lookup m_loader_id_lookup{}; + C m_enabled_components{ 0 }; +}; + +#define INCLUDE_BASE_DYNAMIC_LOADER_IMPLEMENTATION +#include "assets/dynamic_data_loaders/generic/base_dynamic_loader.ipp" +#undef INCLUDE_BASE_DYNAMIC_LOADER_IMPLEMENTATION diff --git a/include/assets/dynamic_data_stores/dynamic_material_library_store.hpp b/include/assets/dynamic_data_stores/dynamic_material_library_store.hpp new file mode 100644 index 0000000..a180507 --- /dev/null +++ b/include/assets/dynamic_data_stores/dynamic_material_library_store.hpp @@ -0,0 +1,6 @@ +#pragma once + +#include "generic/generic_dynamic_store.hpp" +#include "assets/dynamic_read_buffers/dynamic_material_library_buffer.hpp" + +using dynamic_material_library_store = generic_dynamic_store; diff --git a/include/assets/dynamic_data_stores/dynamic_material_store.hpp b/include/assets/dynamic_data_stores/dynamic_material_store.hpp new file mode 100644 index 0000000..cfb849f --- /dev/null +++ b/include/assets/dynamic_data_stores/dynamic_material_store.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include "dynamic_texture_store.hpp" +#include "generic/generic_dynamic_component_store.hpp" +#include "assets/dynamic_read_buffers/dynamic_material_buffer.hpp" + +class dynamic_material_store { + using store_type = generic_dynamic_component_store< + components::material::flags, + components::material::surface_properties, + components::material::transparency, + dynamic_texture_store::id_type, + dynamic_texture_store::id_type, + dynamic_texture_store::id_type, + dynamic_texture_store::id_type, + dynamic_texture_store::id_type, + dynamic_texture_store::id_type + >; + +public: + using id_type = store_type::id_type; + using iterator_type = store_type::iterator_type; + using const_iterator = store_type::const_iterator; + + id_type add(const dynamic_material_buffer& data); + + [[nodiscard]] std::pair find(id_type id); + + [[nodiscard]] std::pair find(id_type id) const; + + void remove(const iterator_type& it); + + void clear(); + +private: + store_type m_store; +}; diff --git a/include/assets/dynamic_data_stores/dynamic_mesh_store.hpp b/include/assets/dynamic_data_stores/dynamic_mesh_store.hpp new file mode 100644 index 0000000..2268d97 --- /dev/null +++ b/include/assets/dynamic_data_stores/dynamic_mesh_store.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include "assets/dynamic_data_stores/generic/generic_dynamic_indexed_component_array_store.hpp" + +#include "assets/components/mesh_vertex_components.hpp" +#include "assets/dynamic_read_buffers/dynamic_mesh_buffer.hpp" + +class dynamic_mesh_store +{ + using store_type = generic_dynamic_indexed_component_array_store< + components::mesh_vertex::flags, + ztu::u32, + components::mesh_vertex::position, + components::mesh_vertex::normal, + components::mesh_vertex::tex_coord, + components::mesh_vertex::color, + components::mesh_vertex::reflectance + >; + +public: + using id_type = store_type::id_type; + using iterator_type = store_type::iterator_type; + using const_iterator = store_type::const_iterator; + + id_type add(const dynamic_mesh_buffer& mesh_buffer); + + [[nodiscard]] std::pair find(id_type id); + + [[nodiscard]] std::pair find(id_type id) const; + + void remove(const iterator_type& it); + + void clear(); + +private: + store_type m_store; +}; \ No newline at end of file diff --git a/include/assets/dynamic_data_stores/dynamic_point_cloud_store.hpp b/include/assets/dynamic_data_stores/dynamic_point_cloud_store.hpp new file mode 100644 index 0000000..f86b9c2 --- /dev/null +++ b/include/assets/dynamic_data_stores/dynamic_point_cloud_store.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include "assets/dynamic_data_stores/generic/generic_dynamic_component_array_store.hpp" + +#include "assets/components/point_cloud_vertex_components.hpp" +#include "assets/dynamic_read_buffers/dynamic_point_cloud_buffer.hpp" + + +class dynamic_point_cloud_store +{ + using store_type = generic_dynamic_component_array_store< + components::point_cloud_vertex::flags, + components::point_cloud_vertex::position, + components::point_cloud_vertex::normal, + components::point_cloud_vertex::color, + components::point_cloud_vertex::reflectance + >; + +public: + using id_type = store_type::id_type; + using iterator_type = store_type::iterator_type; + using const_iterator = store_type::const_iterator; + + id_type add(const dynamic_point_cloud_buffer& point_cloud_buffer); + + [[nodiscard]] std::pair find(id_type id); + + [[nodiscard]] std::pair find(id_type id) const; + + void remove(const iterator_type& it); + + void clear(); + +private: + store_type m_store; +}; diff --git a/include/assets/dynamic_data_stores/dynamic_pose_store.hpp b/include/assets/dynamic_data_stores/dynamic_pose_store.hpp new file mode 100644 index 0000000..9c59d68 --- /dev/null +++ b/include/assets/dynamic_data_stores/dynamic_pose_store.hpp @@ -0,0 +1,6 @@ +#pragma once + +#include "generic/generic_dynamic_store.hpp" +#include "glm/mat4x4.hpp" + +using dynamic_pose_store = generic_dynamic_store; diff --git a/include/assets/dynamic_data_stores/dynamic_shader_store.hpp b/include/assets/dynamic_data_stores/dynamic_shader_store.hpp new file mode 100644 index 0000000..1b88e2b --- /dev/null +++ b/include/assets/dynamic_data_stores/dynamic_shader_store.hpp @@ -0,0 +1,9 @@ +#pragma once + +#include "generic/generic_dynamic_store.hpp" +#include "glm/mat4x4.hpp" + +class dynamic_shader_store { + + +}; \ No newline at end of file diff --git a/include/assets/dynamic_data_stores/dynamic_texture_store.hpp b/include/assets/dynamic_data_stores/dynamic_texture_store.hpp new file mode 100644 index 0000000..52437c3 --- /dev/null +++ b/include/assets/dynamic_data_stores/dynamic_texture_store.hpp @@ -0,0 +1,6 @@ +#pragma once + +#include "generic/generic_dynamic_store.hpp" +#include "assets/dynamic_read_buffers/dynamic_texture_buffer.hpp" + +using dynamic_texture_store = generic_dynamic_store; diff --git a/include/assets/dynamic_data_stores/dynamic_vertex_store.hpp b/include/assets/dynamic_data_stores/dynamic_vertex_store.hpp new file mode 100644 index 0000000..77f0e5c --- /dev/null +++ b/include/assets/dynamic_data_stores/dynamic_vertex_store.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include +#include +#include +#include "util/id_type.hpp" +#include "util/uix.hpp" + +#include "GL/glew.h" + +template +class dynamic_vertex_store { +public: + using id_type = ztu::id_type_for; + + void add( + C component_flags, + std::span... components + ); + + void build_vertex_buffer( + std::vector& vertex_buffer, + std::size_t& component_count, + std::array& component_types, + std::array& component_lengths, + GLsizei& stride + ) const; + +protected: + std::tuple...> m_component_buffers{}; + std::vector vertex_counts; + std::vector m_components{ 0 }; +}; + +#define INCLUDE_DYNAMIC_MODEL_DATA_IMPLEMENTATION +#include "assets/dynamic_read_buffers/dynamic_model_buffer.ipp" +#undef INCLUDE_DYNAMIC_MODEL_DATA_IMPLEMENTATION diff --git a/include/assets/dynamic_data_stores/generic/generic_dynamic_component_array_store.hpp b/include/assets/dynamic_data_stores/generic/generic_dynamic_component_array_store.hpp new file mode 100644 index 0000000..94dec73 --- /dev/null +++ b/include/assets/dynamic_data_stores/generic/generic_dynamic_component_array_store.hpp @@ -0,0 +1,149 @@ +#pragma once + +#include +#include + +#include "util/uix.hpp" +#include "util/id_type.hpp" + +#include +#include +#include +#include +#include +#include +#include + +template +class generic_dynamic_component_array_store; + +template +class component_array_iterator { +public: + using value_type = std::tuple...>; + using size_type = std::size_t; + using count_type = ztu::u32; + using flag_count_type = std::pair; + using component_array_pointer_type = std::tuple...>; + using flag_count_pointer_type = const flag_count_type*; + using offsets_type = std::array; + using difference_type = std::ptrdiff_t; + using pointer = value_type*; + using reference = value_type; + using iterator_category = std::random_access_iterator_tag; + +private: + friend generic_dynamic_component_array_store; + + component_array_iterator( + component_array_pointer_type components, + flag_count_pointer_type flags, + std::size_t index, + const offsets_type& offsets + ); + +public: + constexpr component_array_iterator() noexcept = default; + + constexpr component_array_iterator(const component_array_iterator&) noexcept = default; + constexpr component_array_iterator(component_array_iterator&&) noexcept = default; + + constexpr component_array_iterator& operator=(const component_array_iterator&) noexcept = default; + constexpr component_array_iterator& operator=(component_array_iterator&&) noexcept = default; + + reference operator*() const; + + component_array_iterator& operator++(); + component_array_iterator operator++(int); + component_array_iterator& operator--(); + component_array_iterator operator--(int); + + component_array_iterator& operator+=(difference_type n); + component_array_iterator& operator-=(difference_type n); + component_array_iterator operator+(difference_type n) const; + component_array_iterator operator-(difference_type n) const; + difference_type operator-(const component_array_iterator& other) const; + + reference operator[](difference_type n) const; + + bool operator==(const component_array_iterator& other) const; + bool operator!=(const component_array_iterator& other) const; + bool operator<(const component_array_iterator& other) const; + bool operator<=(const component_array_iterator& other) const; + bool operator>(const component_array_iterator& other) const; + bool operator>=(const component_array_iterator& other) const; + +protected: + template + static bool is_component_enabled(C flag); + + template + void calc_offsets(std::index_sequence, difference_type n); + + template + reference dereference(std::index_sequence) const; + + template + std::tuple_element_t get_span() const; + +private: + value_type m_components{}; + const flag_count_type* m_flag_counts{}; + size_type m_index{}; + offsets_type m_offsets{}; +}; + +template +class generic_dynamic_component_array_store +{ +public: + using id_type = ztu::id_type_for; + using count_type = ztu::u32; + using iterator_type = component_array_iterator; + using const_iterator = component_array_iterator...>; + using view_type = std::ranges::subrange; + using const_view_type = std::ranges::subrange; + + id_type add(const std::tuple...>& component_arrays); + + [[nodiscard]] std::pair find(id_type id); + + [[nodiscard]] std::pair find(id_type id) const; + + void remove(const iterator_type& it); + + void clear(); + + iterator_type begin(); + + iterator_type end(); + + const_iterator begin() const; + + const_iterator end() const; + + const_iterator cbegin() const; + + const_iterator cend() const; + + view_type view(); + + const_view_type view() const; + +protected: + std::tuple...> data_ptrs(); + + std::tuple>...> data_ptrs() const; + + std::array data_counts() const; + +private: + std::tuple...> m_component_arrays; + std::vector> m_component_flag_counts; + std::vector m_ids; + id_type m_next_data_id{ 1 }; +}; + +#define INCLUDE_GENERIC_DYNAMIC_COMPONENT_ARRAY_STORE_IMPLEMENTATION +#include "assets/dynamic_data_stores/generic/generic_dynamic_component_array_store.ipp" +#undef INCLUDE_GENERIC_DYNAMIC_COMPONENT_ARRAY_STORE_IMPLEMENTATION diff --git a/include/assets/dynamic_data_stores/generic/generic_dynamic_component_store.hpp b/include/assets/dynamic_data_stores/generic/generic_dynamic_component_store.hpp new file mode 100644 index 0000000..0dbac6f --- /dev/null +++ b/include/assets/dynamic_data_stores/generic/generic_dynamic_component_store.hpp @@ -0,0 +1,147 @@ +#pragma once + +#include +#include + +#include "util/uix.hpp" +#include "util/id_type.hpp" + +#include +#include +#include +#include +#include +#include +#include + +template +class generic_dynamic_component_store; + +template +class component_iterator { +public: + + + using value_type = std::tuple...>; + using size_type = std::size_t; + using offsets_type = std::array; + using difference_type = std::ptrdiff_t; + using pointer = value_type*; + using reference = value_type; + using iterator_category = std::random_access_iterator_tag; + +private: + friend generic_dynamic_component_store; + + component_iterator( + value_type components, + const C* flags, + std::size_t index, + const offsets_type& offsets + ); + +public: + + constexpr component_iterator() noexcept = default; + + constexpr component_iterator(const component_iterator&) noexcept = default; + constexpr component_iterator(component_iterator&&) noexcept = default; + + constexpr component_iterator& operator=(const component_iterator&) noexcept = default; + constexpr component_iterator& operator=(component_iterator&&) noexcept = default; + + reference operator*() const; + + component_iterator& operator++(); + component_iterator operator++(int); + component_iterator& operator--(); + component_iterator operator--(int); + + component_iterator& operator+=(difference_type n); + component_iterator& operator-=(difference_type n); + component_iterator operator+(difference_type n) const; + component_iterator operator-(difference_type n) const; + difference_type operator-(const component_iterator& other) const; + + reference operator[](difference_type n) const; + + bool operator==(const component_iterator& other) const; + bool operator!=(const component_iterator& other) const; + bool operator<(const component_iterator& other) const; + bool operator<=(const component_iterator& other) const; + bool operator>(const component_iterator& other) const; + bool operator>=(const component_iterator& other) const; + +protected: + template + static bool is_component_enabled(C flag); + + template + void calc_offsets(std::index_sequence, difference_type n); + + template + reference dereference(std::index_sequence) const; + + template + std::tuple_element_t get_pointer() const; + +private: + value_type m_components{}; + const C* m_flags{}; + size_type m_index{}; + offsets_type m_offsets{}; +}; + +template +class generic_dynamic_component_store +{ +public: + using id_type = ztu::id_type_for; + using iterator_type = component_iterator; + using const_iterator = component_iterator...>; + using view_type = std::ranges::subrange; + using const_view_type = std::ranges::subrange; + + id_type add(const std::tuple...>& data); + + [[nodiscard]] std::pair find(id_type id); + + [[nodiscard]] std::pair find(id_type id) const; + + void remove(const iterator_type& it); + + void clear(); + + iterator_type begin(); + + iterator_type end(); + + const_iterator begin() const; + + const_iterator end() const; + + const_iterator cbegin() const; + + const_iterator cend() const; + + view_type view(); + + const_view_type view() const; + +protected: + std::tuple...> data_ptrs(); + + std::tuple>...> data_ptrs() const; + + std::array data_counts() const; + +private: + std::tuple...> m_components; + std::vector m_component_flags; + std::vector m_ids; + id_type m_next_data_id{ 1 }; +}; + +#define INCLUDE_GENERIC_DYNAMIC_COMPONENT_STORE_IMPLEMENTATION +#include "assets/dynamic_data_stores/generic_dynamic_component_store.ipp" +#undef INCLUDE_GENERIC_DYNAMIC_COMPONENT_STORE_IMPLEMENTATION diff --git a/include/assets/dynamic_data_stores/generic/generic_dynamic_indexed_component_array_store.hpp b/include/assets/dynamic_data_stores/generic/generic_dynamic_indexed_component_array_store.hpp new file mode 100644 index 0000000..8c47f0d --- /dev/null +++ b/include/assets/dynamic_data_stores/generic/generic_dynamic_indexed_component_array_store.hpp @@ -0,0 +1,157 @@ +#pragma once + +#include +#include + +#include "util/uix.hpp" +#include "util/id_type.hpp" + +#include +#include +#include +#include +#include +#include +#include + +template +class generic_dynamic_indexed_component_array_store; + +template +class indexed_component_array_iterator { +public: + using index_type = I; + using value_type = std::tuple, std::span...>; + using size_type = std::size_t; + using count_type = ztu::u32; + using flag_count_type = std::tuple; + using index_array_pointer_type = const index_type*; + using component_array_pointer_type = std::tuple...>; + using flag_count_pointer_type = const flag_count_type*; + + using offsets_type = std::array; + using difference_type = std::ptrdiff_t; + using pointer = value_type*; + using reference = value_type; + using iterator_category = std::random_access_iterator_tag; + +private: + friend generic_dynamic_indexed_component_array_store; + + indexed_component_array_iterator( + index_array_pointer_type indices, + const component_array_pointer_type& components, + flag_count_pointer_type flag_counts, + std::size_t index, + const offsets_type& offsets + ); + +public: + constexpr indexed_component_array_iterator() noexcept = default; + + constexpr indexed_component_array_iterator(const indexed_component_array_iterator&) noexcept = default; + constexpr indexed_component_array_iterator(indexed_component_array_iterator&&) noexcept = default; + + constexpr indexed_component_array_iterator& operator=(const indexed_component_array_iterator&) noexcept = default; + constexpr indexed_component_array_iterator& operator=(indexed_component_array_iterator&&) noexcept = default; + + reference operator*() const; + + indexed_component_array_iterator& operator++(); + indexed_component_array_iterator operator++(int); + indexed_component_array_iterator& operator--(); + indexed_component_array_iterator operator--(int); + + indexed_component_array_iterator& operator+=(difference_type n); + indexed_component_array_iterator& operator-=(difference_type n); + indexed_component_array_iterator operator+(difference_type n) const; + indexed_component_array_iterator operator-(difference_type n) const; + difference_type operator-(const indexed_component_array_iterator& other) const; + + reference operator[](difference_type n) const; + + bool operator==(const indexed_component_array_iterator& other) const; + bool operator!=(const indexed_component_array_iterator& other) const; + bool operator<(const indexed_component_array_iterator& other) const; + bool operator<=(const indexed_component_array_iterator& other) const; + bool operator>(const indexed_component_array_iterator& other) const; + bool operator>=(const indexed_component_array_iterator& other) const; + +protected: + template + static bool is_component_enabled(C flag); + + template + void calc_offsets(std::index_sequence, difference_type n); + + template + reference dereference(std::index_sequence) const; + +private: + index_array_pointer_type m_indices{}; + component_array_pointer_type m_components{}; + flag_count_pointer_type m_flag_counts{}; + size_type m_index{}; + offsets_type m_offsets{}; +}; + +template +class generic_dynamic_indexed_component_array_store +{ +public: + using id_type = ztu::id_type_for; + using count_type = ztu::u32; + using iterator_type = indexed_component_array_iterator; + using const_iterator = indexed_component_array_iterator...>; + using view_type = std::ranges::subrange; + using const_view_type = std::ranges::subrange; + + id_type add( + std::span indices, + const std::tuple...>& component_arrays + ); + + [[nodiscard]] std::pair find(id_type id); + + [[nodiscard]] std::pair find(id_type id) const; + + void remove(const iterator_type& it); + + void clear(); + + iterator_type begin(); + + iterator_type end(); + + const_iterator begin() const; + + const_iterator end() const; + + const_iterator cbegin() const; + + const_iterator cend() const; + + view_type view(); + + const_view_type view() const; + +protected: + + + std::tuple...> component_array_ptrs(); + + std::tuple>...> component_array_ptrs() const; + + std::array array_counts() const; + +private: + std::vector m_indices; + std::tuple...> m_component_arrays; + std::vector> m_component_flag_counts; + std::vector m_ids; + id_type m_next_data_id{ 1 }; +}; + +#define INCLUDE_GENERIC_DYNAMIC_INDEXED_COMPONENT_ARRAY_STORE_IMPLEMENTATION +#include "assets/dynamic_data_stores/generic/generic_dynamic_indexed_component_array_store.ipp" +#undef INCLUDE_GENERIC_DYNAMIC_INDEXED_COMPONENT_ARRAY_STORE_IMPLEMENTATION diff --git a/include/assets/dynamic_data_stores/generic/generic_dynamic_store.hpp b/include/assets/dynamic_data_stores/generic/generic_dynamic_store.hpp new file mode 100644 index 0000000..ebb6f4f --- /dev/null +++ b/include/assets/dynamic_data_stores/generic/generic_dynamic_store.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include +#include + +#include "util/uix.hpp" +#include "util/id_type.hpp" + +template +class generic_dynamic_store +{ +public: + using id_type = ztu::id_type_for; + using container_type = std::vector; + using iterator_type = typename container_type::iterator; + using const_iterator = typename container_type::const_iterator; + + id_type add(const T& data); + + [[nodiscard]] std::pair find(id_type id); + + [[nodiscard]] std::pair find(id_type id) const; + + [[nodiscard]] std::span data(); + + [[nodiscard]] std::span data() const; + + void remove(iterator_type it); + + void clear(); + +private: + std::vector m_data; + std::vector m_ids; + id_type m_next_data_id{ 1 }; +}; + +#define INCLUDE_GENERIC_DYNAMIC_STORE_IMPLEMENTATION +#include "assets/dynamic_data_stores/generic_dynamic_store.ipp" +#undef INCLUDE_GENERIC_DYNAMIC_STORE_IMPLEMENTATION diff --git a/include/assets/dynamic_read_buffers/dynamic_material_buffer.hpp b/include/assets/dynamic_read_buffers/dynamic_material_buffer.hpp new file mode 100755 index 0000000..d52e5b6 --- /dev/null +++ b/include/assets/dynamic_read_buffers/dynamic_material_buffer.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include + +#include "assets/components/material_components.hpp" +#include "assets/dynamic_data_stores/dynamic_texture_store.hpp" + +struct dynamic_material_buffer { + + dynamic_material_buffer() = default; + + components::material::surface_properties& initialized_surface_properties(); + + [[nodiscard]] std::optional& surface_properties(); + [[nodiscard]] std::optional& transparency(); + [[nodiscard]] std::optional& ambient_color_texture_id(); + [[nodiscard]] std::optional& diffuse_color_texture_id(); + [[nodiscard]] std::optional& specular_color_texture_id(); + [[nodiscard]] std::optional& shininess_texture_id(); + [[nodiscard]] std::optional& alpha_texture_id(); + [[nodiscard]] std::optional& bump_texture_id(); + + [[nodiscard]] const std::optional& surface_properties() const; + [[nodiscard]] const std::optional& transparency() const; + [[nodiscard]] const std::optional& ambient_color_texture_id() const; + [[nodiscard]] const std::optional& diffuse_color_texture_id() const; + [[nodiscard]] const std::optional& specular_color_texture_id() const; + [[nodiscard]] const std::optional& shininess_texture_id() const; + [[nodiscard]] const std::optional& alpha_texture_id() const; + [[nodiscard]] const std::optional& bump_texture_id() const; + + std::tuple< + std::optional, + std::optional, + std::optional, + std::optional, + std::optional, + std::optional, + std::optional, + std::optional + > data{ + std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt + }; +}; + +#define INCLUDE_DYNAMIC_MATERIAL_DATA_IMPLEMENTATION +#include "assets/dynamic_read_buffers/dynamic_material_buffer.ipp" +#undef INCLUDE_DYNAMIC_MATERIAL_DATA_IMPLEMENTATION diff --git a/include/assets/dynamic_read_buffers/dynamic_material_library_buffer.hpp b/include/assets/dynamic_read_buffers/dynamic_material_library_buffer.hpp new file mode 100644 index 0000000..13b03a6 --- /dev/null +++ b/include/assets/dynamic_read_buffers/dynamic_material_library_buffer.hpp @@ -0,0 +1,6 @@ +#pragma once + +#include "util/string_lookup.hpp" +#include "assets/dynamic_data_stores/dynamic_material_store.hpp" + +using dynamic_material_library_buffer = ztu::string_lookup; diff --git a/include/assets/dynamic_read_buffers/dynamic_mesh_buffer.hpp b/include/assets/dynamic_read_buffers/dynamic_mesh_buffer.hpp new file mode 100644 index 0000000..b8491aa --- /dev/null +++ b/include/assets/dynamic_read_buffers/dynamic_mesh_buffer.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include +#include + +#include "util/uix.hpp" +#include "assets/components/mesh_vertex_components.hpp" +#include "assets/dynamic_read_buffers/dynamic_vertex_buffer.hpp" +#include "assets/dynamic_data_stores/dynamic_material_store.hpp" + +class dynamic_mesh_buffer : public dynamic_vertex_buffer< + components::mesh_vertex::flags, + components::mesh_vertex::position, + components::mesh_vertex::normal, + components::mesh_vertex::tex_coord, + components::mesh_vertex::color, + components::mesh_vertex::reflectance +> { +public: + using index_type = ztu::u32; + using triangle_type = std::array; + + [[nodiscard]] std::vector& positions(); + [[nodiscard]] std::vector& normals(); + [[nodiscard]] std::vector& tex_coords(); + [[nodiscard]] std::vector& colors(); + [[nodiscard]] std::vector& reflectances(); + [[nodiscard]] std::vector& triangles(); + [[nodiscard]] auto& material_id(); + + [[nodiscard]] const std::vector& positions() const; + [[nodiscard]] const std::vector& normals() const; + [[nodiscard]] const std::vector& tex_coords() const; + [[nodiscard]] const std::vector& colors() const; + [[nodiscard]] const std::vector& reflectances() const; + [[nodiscard]] const std::vector& triangles() const; + [[nodiscard]] const auto& material_id() const; + +private: + std::vector m_triangles{}; + dynamic_material_store::id_type m_material_id{}; +}; + +#define INCLUDE_DYNAMIC_MESH_DATA_IMPLEMENTATION +#include "assets/dynamic_read_buffers/dynamic_mesh_buffer.ipp" +#undef INCLUDE_DYNAMIC_MESH_DATA_IMPLEMENTATION diff --git a/include/assets/dynamic_read_buffers/dynamic_point_cloud_buffer.hpp b/include/assets/dynamic_read_buffers/dynamic_point_cloud_buffer.hpp new file mode 100644 index 0000000..3ff27b5 --- /dev/null +++ b/include/assets/dynamic_read_buffers/dynamic_point_cloud_buffer.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include "assets/components/point_cloud_vertex_components.hpp" + +#include +#include +#include "assets/dynamic_read_buffers/dynamic_vertex_buffer.hpp" + +class dynamic_point_cloud_buffer : public dynamic_vertex_buffer< + components::point_cloud_vertex::flags, + components::point_cloud_vertex::position, + components::point_cloud_vertex::normal, + components::point_cloud_vertex::color, + components::point_cloud_vertex::reflectance +> { +public: + [[nodiscard]] std::vector& positions(); + [[nodiscard]] std::vector& normals(); + [[nodiscard]] std::vector& colors(); + [[nodiscard]] std::vector& reflectances(); + + [[nodiscard]] const std::vector& positions() const; + [[nodiscard]] const std::vector& normals() const; + [[nodiscard]] const std::vector& colors() const; + [[nodiscard]] const std::vector& reflectances() const; +}; + +#define INCLUDE_DYNAMIC_TEXTURE_DATA_IMPLEMENTATION +#include "assets/dynamic_read_buffers/dynamic_point_cloud_buffer.ipp" +#undef INCLUDE_DYNAMIC_TEXTURE_DATA_IMPLEMENTATION \ No newline at end of file diff --git a/include/assets/dynamic_read_buffers/dynamic_pose_buffer.hpp b/include/assets/dynamic_read_buffers/dynamic_pose_buffer.hpp new file mode 100644 index 0000000..50181de --- /dev/null +++ b/include/assets/dynamic_read_buffers/dynamic_pose_buffer.hpp @@ -0,0 +1,5 @@ +#pragma once + +#include "glm/mat4x4.hpp" + +using dynamic_pose_buffer = glm::mat4; diff --git a/include/assets/dynamic_read_buffers/dynamic_shader_buffer.hpp b/include/assets/dynamic_read_buffers/dynamic_shader_buffer.hpp new file mode 100644 index 0000000..5fb00d6 --- /dev/null +++ b/include/assets/dynamic_read_buffers/dynamic_shader_buffer.hpp @@ -0,0 +1,10 @@ +#pragma once + +#include +#include "GL/glew.h" + +struct dynamic_shader_buffer +{ + std::vector source{}; + GLenum type{ GL_INVALID_ENUM }; +}; diff --git a/include/assets/dynamic_read_buffers/dynamic_texture_buffer.hpp b/include/assets/dynamic_read_buffers/dynamic_texture_buffer.hpp new file mode 100755 index 0000000..267b97f --- /dev/null +++ b/include/assets/dynamic_read_buffers/dynamic_texture_buffer.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include +#include +#include +#include +#include "assets/components/texture_components.hpp" + +class dynamic_texture_buffer { +public: + using value_type = std::uint8_t; + using dim_type = std::int32_t; + using size_type = std::make_signed_t; + using difference_type = size_type; + using reference = value_type&; + using const_reference = const value_type&; + using pointer = std::uint8_t*; + using const_pointer = const std::uint8_t*; + using iterator = pointer; + using const_iterator = const_pointer; + + dynamic_texture_buffer() = default; + + dynamic_texture_buffer( + std::unique_ptr&& data, + dim_type width, + dim_type height, + components::texture::flags components + ); + + dynamic_texture_buffer(const dynamic_texture_buffer&); + + dynamic_texture_buffer(dynamic_texture_buffer&&) noexcept; + + [[nodiscard]] dynamic_texture_buffer& operator=(const dynamic_texture_buffer&); + + [[nodiscard]] dynamic_texture_buffer& operator=(dynamic_texture_buffer&&) noexcept; + + [[nodiscard]] components::texture::flags components() const; + + [[nodiscard]] dim_type width() const; + + [[nodiscard]] dim_type height() const; + + [[nodiscard]] std::pair dimensions() const; + + [[nodiscard]] size_type pixel_count() const; + + [[nodiscard]] size_type component_count() const; + + [[nodiscard]] size_type size() const; + + [[nodiscard]] const_iterator begin() const; + + [[nodiscard]] iterator begin(); + + [[nodiscard]] const_iterator end() const; + + [[nodiscard]] iterator end(); + + [[nodiscard]] const_iterator cbegin() const; + + [[nodiscard]] const_iterator cend() const; + + [[nodiscard]] const_pointer data() const; + + [[nodiscard]] pointer data(); + +private: + std::unique_ptr m_data{ nullptr }; + dim_type m_width{ 0 }, m_height{ 0 }; + components::texture::flags m_components{ components::texture::flags::none }; +}; + +#define INCLUDE_DYNAMIC_TEXTURE_DATA_IMPLEMENTATION +#include "assets/dynamic_read_buffers/dynamic_texture_buffer.ipp" +#undef INCLUDE_DYNAMIC_TEXTURE_DATA_IMPLEMENTATION diff --git a/include/assets/dynamic_read_buffers/dynamic_vertex_buffer.hpp b/include/assets/dynamic_read_buffers/dynamic_vertex_buffer.hpp new file mode 100644 index 0000000..31dd353 --- /dev/null +++ b/include/assets/dynamic_read_buffers/dynamic_vertex_buffer.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include "util/uix.hpp" +#include +#include +#include "GL/glew.h" + +template +struct dynamic_vertex_buffer { + std::tuple...> vertices{}; +}; + +#define INCLUDE_DYNAMIC_MODEL_DATA_IMPLEMENTATION +#include "assets/dynamic_read_buffers/dynamic_texture_buffer.ipp" +#undef INCLUDE_DYNAMIC_MODEL_DATA_IMPLEMENTATION diff --git a/include/assets/prefetch_lookup.hpp b/include/assets/prefetch_lookup.hpp new file mode 100644 index 0000000..33c034c --- /dev/null +++ b/include/assets/prefetch_lookup.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include "prefetch_lookups/material_library_prefetch_lookup.hpp" +#include "prefetch_lookups/material_prefetch_lookup.hpp" +#include "prefetch_lookups/mesh_prefetch_lookup.hpp" +#include "prefetch_lookups/point_cloud_prefetch_lookup.hpp" +#include "prefetch_lookups/pose_prefetch_lookup.hpp" +#include "prefetch_lookups/shader_prefetch_lookup.hpp" +#include "prefetch_lookups/texture_prefetch_lookup.hpp" + +struct prefetch_lookup +{ + texture_prefetch_lookup textures; + material_library_prefetch_lookup material_libraries; + material_prefetch_lookup materials; + mesh_prefetch_lookup meshes; + pose_prefetch_lookup poses; + point_cloud_prefetch_lookup point_clouds; + shader_prefetch_lookup shaders; +}; \ No newline at end of file diff --git a/include/assets/prefetch_lookups/material_library_prefetch_lookup.hpp b/include/assets/prefetch_lookups/material_library_prefetch_lookup.hpp new file mode 100644 index 0000000..e4b1a71 --- /dev/null +++ b/include/assets/prefetch_lookups/material_library_prefetch_lookup.hpp @@ -0,0 +1,7 @@ +#pragma once + +#include +#include +#include "assets/dynamic_data_stores/dynamic_material_library_store.hpp" + +using material_library_prefetch_lookup = std::unordered_map; diff --git a/include/assets/prefetch_lookups/material_prefetch_lookup.hpp b/include/assets/prefetch_lookups/material_prefetch_lookup.hpp new file mode 100644 index 0000000..efe5bdd --- /dev/null +++ b/include/assets/prefetch_lookups/material_prefetch_lookup.hpp @@ -0,0 +1,7 @@ +#pragma once + +#include +#include +#include "assets/dynamic_data_stores/dynamic_material_store.hpp" + +using material_prefetch_lookup = std::unordered_map; diff --git a/include/assets/prefetch_lookups/mesh_prefetch_lookup.hpp b/include/assets/prefetch_lookups/mesh_prefetch_lookup.hpp new file mode 100644 index 0000000..3e60670 --- /dev/null +++ b/include/assets/prefetch_lookups/mesh_prefetch_lookup.hpp @@ -0,0 +1,7 @@ +#pragma once + +#include +#include +#include "assets/dynamic_data_stores/dynamic_mesh_store.hpp" + +using mesh_prefetch_lookup = std::unordered_map; diff --git a/include/assets/prefetch_lookups/point_cloud_prefetch_lookup.hpp b/include/assets/prefetch_lookups/point_cloud_prefetch_lookup.hpp new file mode 100644 index 0000000..1eef821 --- /dev/null +++ b/include/assets/prefetch_lookups/point_cloud_prefetch_lookup.hpp @@ -0,0 +1,7 @@ +#pragma once + +#include +#include +#include "assets/dynamic_data_stores/dynamic_point_cloud_store.hpp" + +using point_cloud_prefetch_lookup = std::unordered_map; diff --git a/include/assets/prefetch_lookups/pose_prefetch_lookup.hpp b/include/assets/prefetch_lookups/pose_prefetch_lookup.hpp new file mode 100644 index 0000000..5204764 --- /dev/null +++ b/include/assets/prefetch_lookups/pose_prefetch_lookup.hpp @@ -0,0 +1,58 @@ +#pragma once + +#include +#include + +#include "assets/dynamic_data_stores/dynamic_pose_store.hpp" +#include "util/uix.hpp" + +class pose_prefetch_lookup { +public: + using index_type = ztu::u32; + using directory_lookup = std::unordered_map; + using directory_iterator = std::pair; + using index_lookup = std::vector; + using index_iterator = index_lookup::iterator; + using lookup_type = std::vector; + using iterator = lookup_type::iterator; + + void emplace( + const std::filesystem::path& directory, + index_type index, + dynamic_pose_store::id_type id + ); + + void emplace_hint_dir( + directory_iterator directory_it, + const std::filesystem::path& directory, + index_type index, + dynamic_pose_store::id_type id + ); + + void emplace_hint_dir_index( + directory_iterator directory_it, + index_iterator index_it, + index_type index, + dynamic_pose_store::id_type id + ); + + std::pair find_directory( + const std::filesystem::path& directory + ); + + std::pair find_index( + directory_iterator directory_it, + index_type index + ); + +protected: + directory_iterator emplace_dir( + directory_iterator directory_it, + const std::filesystem::path& directory + ); + +private: + directory_lookup m_directory_lookup; + index_lookup m_directory_indices; // count before indices, indices sorted per dir + lookup_type m_pose_ids; // offset by 1 +}; diff --git a/include/assets/prefetch_lookups/shader_prefetch_lookup.hpp b/include/assets/prefetch_lookups/shader_prefetch_lookup.hpp new file mode 100644 index 0000000..7cdd980 --- /dev/null +++ b/include/assets/prefetch_lookups/shader_prefetch_lookup.hpp @@ -0,0 +1,7 @@ +#pragma once + +#include +#include +#include "assets/dynamic_data_stores/dynamic_shader_st + +using texture_prefetch_lookup = std::unordered_map; diff --git a/include/assets/prefetch_lookups/texture_prefetch_lookup.hpp b/include/assets/prefetch_lookups/texture_prefetch_lookup.hpp new file mode 100644 index 0000000..c345f9d --- /dev/null +++ b/include/assets/prefetch_lookups/texture_prefetch_lookup.hpp @@ -0,0 +1,7 @@ +#pragma once + +#include +#include +#include "assets/dynamic_data_stores/dynamic_texture_store.hpp" + +using texture_prefetch_lookup = std::unordered_map; diff --git a/include/assets/prefetch_queue.hpp b/include/assets/prefetch_queue.hpp new file mode 100644 index 0000000..da77caa --- /dev/null +++ b/include/assets/prefetch_queue.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include "util/string_list.hpp" + +struct file_dir_list +{ + ztu::string_list files; + ztu::string_list directories; +}; + +struct prefetch_queue +{ + file_dir_list obj_queue; + file_dir_list stl_queue; + + file_dir_list mtl_queue; + + file_dir_list uosr_queue; + file_dir_list uos_rgb_queue; + file_dir_list uos_normal_queue; + file_dir_list uos_queue; + file_dir_list kitti_queue; + + file_dir_list threedtk_pose_queue; + file_dir_list kitti_pose_queue; + + file_dir_list glsl_queue; +}; \ No newline at end of file diff --git a/include/opengl/data/material_data.hpp b/include/opengl/data/material_data.hpp new file mode 100644 index 0000000..8322263 --- /dev/null +++ b/include/opengl/data/material_data.hpp @@ -0,0 +1,58 @@ +#pragma once + +#include "assets/data/texture.hpp" +#include "assets/data/surface_properties.hpp" +#include "assets/components/material_components.hpp" + +#include "opengl/handles/material_handle.hpp" +#include "opengl/data/texture_data.hpp" + +#include + + +namespace zgl +{ +struct material_data +{ +private: + material_data( + const std::optional& texture_handle, + const std::optional& surface_properties_handle, + const std::optional& alpha_handle, + std::optional&& texture_data, + components::material::flags components + ); + +public: + material_data() = default; + + [[nodiscard]] static std::error_code build_from( + const std::optional& texture_opt, + const std::optional& surface_properties_opt, + const std::optional& transparency_opt, + material_component::flags components, + material_data& data + ); + + material_data(const material_data& other) = delete; + material_data& operator=(const material_data& other) = delete; + + material_data(material_data&& other) noexcept; + material_data& operator=(material_data&& other) noexcept; + + [[nodiscard]] material_handle handle() const; + + [[nodiscard]] material_component::flags components() const; + +private: + material_handle m_handle{}; + std::optional m_texture_data{ std::nullopt }; + material_component::flags m_component_types{ + material_component::flags::none + }; +}; +} + +#define INCLUDE_MATERIAL_DATA_IMPLEMENTATION +#include "opengl/data/material_data.ipp" +#undef INCLUDE_MATERIAL_DATA_IMPLEMENTATION diff --git a/include/opengl/data/mesh_data.hpp b/include/opengl/data/mesh_data.hpp new file mode 100755 index 0000000..734261a --- /dev/null +++ b/include/opengl/data/mesh_data.hpp @@ -0,0 +1,68 @@ +#pragma once + +#include "opengl/handles/mesh_handle.hpp" +#include "util/uix.hpp" + +#include +#include + +#include "assets/components/mesh_vertex_components.hpp" +#include "GL/glew.h" + +namespace zgl +{ +struct mesh_data +{ +private: + mesh_data( + GLuint vertex_vbo_id, + GLuint index_vbo_id, + GLuint vao_id, + ztu::u32 material_id, + components::mesh_vertex::flags components, + GLsizei index_count + ); + +public: + mesh_data() = default; + + [[nodiscard]] static std::error_code build_from( + std::span vertex_buffer, + std::span component_types, + std::span component_lengths, + GLsizei stride, + std::span index_buffer, + ztu::u32 material_id, + components::mesh_vertex::flags components, + mesh_data& data + ); + + mesh_data(const mesh_data& other) = delete; + mesh_data& operator=(const mesh_data& other) = delete; + + mesh_data(mesh_data&& other) noexcept; + mesh_data& operator=(mesh_data&& other) noexcept; + + ~mesh_data(); + + [[nodiscard]] mesh_handle handle() const; + + [[nodiscard]] components::mesh_vertex::flags components() const; + + [[nodiscard]] ztu::u32 material_id() const; + + +private: + mesh_handle m_handle{}; + GLuint m_vertex_vbo_id{ 0 }; + GLuint m_index_vbo_id{ 0 }; + ztu::u32 m_material_id{ 0 }; + components::mesh_vertex::flags m_component_types{ + components::mesh_vertex::flags::none + }; +}; +} + +#define INCLUDE_MESH_DATA_IMPLEMENTATION +#include "opengl/data/mesh_data.ipp" +#undef INCLUDE_MESH_DATA_IMPLEMENTATION diff --git a/include/opengl/data/point_cloud_data.hpp b/include/opengl/data/point_cloud_data.hpp new file mode 100644 index 0000000..9beeaee --- /dev/null +++ b/include/opengl/data/point_cloud_data.hpp @@ -0,0 +1,55 @@ +#pragma once + +#include "util/uix.hpp" + +#include +#include +#include "GL/glew.h" +#include "opengl/handles/point_cloud_handle.hpp" +#include "assets/components/point_cloud_vertex_components.hpp" + + +namespace zgl { +struct point_cloud_data { +private: + point_cloud_data( + GLuint vertex_buffer_id, + GLuint vao_id, + GLsizei point_count + ); + +public: + point_cloud_data() = default; + + [[nodiscard]] static std::error_code build_from( + std::span point_buffer, + std::span component_types, + std::span component_lengths, + GLsizei stride, + point_cloud_data& data + ); + + point_cloud_data(const point_cloud_data& other) = delete; + point_cloud_data& operator=(const point_cloud_data& other) = delete; + + point_cloud_data(point_cloud_data&& other) noexcept; + point_cloud_data& operator=(point_cloud_data&& other) noexcept; + + ~point_cloud_data(); + + [[nodiscard]] point_cloud_handle handle() const; + + [[nodiscard]] components::point_cloud_vertex::flags components() const; + +private: + point_cloud_handle m_handle{}; + GLuint m_vertex_vbo_id{ 0 }; + components::point_cloud_vertex::flags m_component_types{ + components::point_cloud_vertex::flags::none + }; +}; +} + +#define INCLUDE_POINT_CLOUD_DATA_IMPLEMENTATION +#include "opengl/data/point_cloud_data.ipp" +#undef INCLUDE_POINT_CLOUD_DATA_IMPLEMENTATION \ No newline at end of file diff --git a/include/opengl/data/shader_data.hpp b/include/opengl/data/shader_data.hpp new file mode 100644 index 0000000..72a3791 --- /dev/null +++ b/include/opengl/data/shader_data.hpp @@ -0,0 +1,43 @@ +#pragma once + +#include +#include +#include "GL/glew.h" +#include "opengl/handles/shader_handle.hpp" + +namespace zgl +{ +class shader_data +{ +private: + explicit shader_data(GLuint shader_id, GLenum type); + +public: + + shader_data() = default; + + [[nodiscard]] static std::error_code build_from( + GLenum type, + const std::string& source, + shader_data& data + ); + + shader_data(const shader_data& other) = delete; + shader_data& operator=(const shader_data& other) = delete; + + shader_data(shader_data&& other) noexcept; + shader_data& operator=(shader_data&& other) noexcept; + + [[nodiscard]] shader_handle handle() const; + + ~shader_data(); + +private: + shader_handle m_handle{}; + GLenum m_type{ GL_INVALID_ENUM }; +}; +} + +#define INCLUDE_SHADER_DATA_IMPLEMENTATION +#include "opengl/data/shader_data.ipp" +#undef INCLUDE_SHADER_DATA_IMPLEMENTATION diff --git a/include/opengl/data/texture_data.hpp b/include/opengl/data/texture_data.hpp new file mode 100644 index 0000000..44f6d37 --- /dev/null +++ b/include/opengl/data/texture_data.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include "GL/glew.h" +#include "opengl/handles/texture_handle.hpp" + +#include + +namespace zgl +{ +struct texture_data +{ +private: + explicit texture_data(GLuint texture_id); + +public: + texture_data() = default; + + template + [[nodiscard]] static std::error_code build_from( + std::span buffer, + GLenum format, + GLenum type, + GLsizei width, + GLsizei height, + texture_data& data + ); + + texture_data(const texture_data&) = delete; + texture_data& operator=(const texture_data&) = delete; + + texture_data(texture_data&&) noexcept; + texture_data& operator=(texture_data&&) noexcept; + + ~texture_data(); + + [[nodiscard]] texture_handle handle() const; + +private: + texture_handle m_handle{}; +}; +} + +#define INCLUDE_TEXTURE_DATA_IMPLEMENTATION +#include "opengl/data/texture_data.ipp" +#undef INCLUDE_TEXTURE_DATA_IMPLEMENTATION diff --git a/include/opengl/data_uploaders/texture_data_uploader.hpp b/include/opengl/data_uploaders/texture_data_uploader.hpp new file mode 100644 index 0000000..be5c621 --- /dev/null +++ b/include/opengl/data_uploaders/texture_data_uploader.hpp @@ -0,0 +1,84 @@ +#pragma once + +#include "../../assets/dynamic_read_buffers" +#include "assets/dynamic_data_stores/dynamic_texture_store.hpp" +#include "opengl/data/texture_data.hpp" +#include + +namespace zgl +{ +class texture_data_uploader +{ + + void upload( + std::span dynamic_data, + std::span dynamic_data_ids + ) { + + std::vector texture_ids; + std::vector invalid_texture_ids; + + texture_ids.resize(dynamic_data.size()); + + glGenTextures(texture_ids.size(), texture_ids.data()); + + auto texture_id_it = texture_ids.begin(); + + for (std::size_t i{}; i != dynamic_data.size(); ++i) + { + const auto& texture_id = *texture_id_it; + const auto& texture = dynamic_data[i]; + + glBindTexture(GL_TEXTURE_2D, texture_id); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + GLenum format; + switch (texture.components()) { + using enum components::texture::flags; + case luminance: + format = GL_LUMINANCE; + break; + case luminance | alpha: + format = GL_LUMINANCE_ALPHA; + break; + case red | green | blue: + format = GL_RGB; + break; + case red | green | blue | alpha: + format = GL_RGBA; + break; + default: + format = GL_INVALID_ENUM; + break; + } + + if (format == GL_INVALID_ENUM) + { + invalid_texture_ids.push_back(texture_id); + } + else + { + glTexImage2D( + GL_TEXTURE_2D, 0, + GL_RGBA8, + texture.width(), + texture.height(), + 0, + format, + GL_UNSIGNED_BYTE, + texture.data() + ); + glGenerateMipmap(GL_TEXTURE_2D); + } + } + + glBindTexture(GL_TEXTURE_2D, 0); + + glDeleteTextures(invalid_texture_ids.size(), invalid_texture_ids.data()); + } +}; +} diff --git a/include/opengl/handles/shader_handle.hpp b/include/opengl/handles/shader_handle.hpp new file mode 100644 index 0000000..a6aeee5 --- /dev/null +++ b/include/opengl/handles/shader_handle.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include "GL/glew.h" + +namespace zgl +{ +struct shader_handle +{ + constexpr bool operator==(const shader_handle&) const = default; + + GLuint shader_id{ 0 }; +}; +} \ No newline at end of file diff --git a/include/opengl/shader_program_lookup.hpp b/include/opengl/shader_program_lookup.hpp new file mode 100644 index 0000000..b6b0061 --- /dev/null +++ b/include/opengl/shader_program_lookup.hpp @@ -0,0 +1,43 @@ +#pragma once + +#include "opengl/handles/shader_program_handle.hpp" +#include +#include + +namespace zgl { +class shader_program_lookup +{ +public: + void add( + const shader_program_handle& shader_program_handle, + std::span all_attributes, + std::span all_uniforms + ); + + [[nodiscard]] std::optional find( + shader_program_handle::attribute_support_type attributes, + shader_program_handle::uniform_support_type uniforms, + std::span all_attributes + ) const; + + void print(); + +private: + using attribute_locations_type = ztu::u32; + + [[nodiscard]] static attribute_locations_type attribute_location_flags( + shader_program_handle::attribute_support_type attributes, + std::span all_attributes + ); + + struct attribute_entry_type + { + shader_program_handle::attribute_support_type attributes; + attribute_locations_type locations; // Do not go past location 31!!! + }; + + std::vector m_mesh_shader_program_uniforms; + std::vector m_mesh_shader_program_attributes; + std::vector m_mesh_shader_programs; +}; +} diff --git a/include/rendering/batch_renderers/mesh_batch_renderer.hpp b/include/rendering/batch_renderers/mesh_batch_renderer.hpp new file mode 100644 index 0000000..f8f6fd0 --- /dev/null +++ b/include/rendering/batch_renderers/mesh_batch_renderer.hpp @@ -0,0 +1,65 @@ +#pragma once + +#include "opengl/handles/shader_program_handle.hpp" +#include "rendering/batches/mesh_batch.hpp" +#include "assets/components/mesh_vertex_components.hpp" +#include "assets/components/material_components.hpp" +#include "opengl/handles/material_handle.hpp" +#include + +#include "geometry/aabb.hpp" +#include "rendering/modes/mesh_modes.hpp" +#include "rendering/shader_program_lookups/mesh_lookup.hpp" +#include "scene/lighting_setup.hpp" + +namespace rendering +{ +class mesh_batch_renderer +{ +public: + using batch_components_type = std::pair< + components::mesh_vertex::flags, + material_component::flags + >; + using batch_type = mesh_batch; + using batch_index_type = ztu::u32; + using batch_id_type = batch_index_type; + using id_type = std::pair; + + explicit mesh_batch_renderer(int render_mode_count); + + std::optional add( + const batch_components_type& batch_component, + const zgl::mesh_handle& mesh, + const aabb& bounding_box, + const zgl::model_matrix_handle& transform, + const zgl::material_handle& material, + const shader_program_lookups::mesh_lookup& shader_program_lookup + ); + + std::optional bounding_box(id_type); + + bool remove(id_type mesh_id); + + void render( + modes::mesh render_mode, + const glm::mat4& vp_matrix, + const glm::mat4& view_matrix, + const glm::vec3& view_pos, + const lighting_setup& lights + ); + +protected: + [[nodiscard]] std::pair lookup_batch(const batch_components_type& batch_components) const; + +private: + int m_render_mode_count; + + std::vector> m_component_lookup{}; + std::vector m_id_lookup{}; + std::vector> m_batches{}; + std::vector m_shader_programs{}; + + batch_id_type m_next_batch_id{ 0 }; +}; +} diff --git a/include/rendering/batch_renderers/point_cloud_batch_renderer.hpp b/include/rendering/batch_renderers/point_cloud_batch_renderer.hpp new file mode 100644 index 0000000..ee9eecc --- /dev/null +++ b/include/rendering/batch_renderers/point_cloud_batch_renderer.hpp @@ -0,0 +1,59 @@ +#pragma once + +#include "opengl/handles/shader_program_handle.hpp" +#include "rendering/batches/point_cloud_batch.hpp" +#include "assets/components/point_cloud_vertex_components.hpp" +#include "rendering/modes/point_cloud_modes.hpp" +#include + +#include "scene/lighting_setup.hpp" +#include "rendering/shader_program_lookups/point_cloud_lookup.hpp" + +namespace rendering +{ + +class point_cloud_batch_renderer +{ +public: + using batch_components_type = components::point_cloud_vertex::flags; + using batch_type = point_cloud_batch; + using batch_index_type = ztu::u32; + using batch_id_type = batch_index_type; + using id_type = std::pair; + + explicit point_cloud_batch_renderer(int render_mode_count); + + std::optional add( + batch_components_type batch_components, + const zgl::point_cloud_handle& point_cloud, + const aabb& bounding_box, + const zgl::model_matrix_handle& transform, + const shader_program_lookups::point_cloud_lookup& shader_program_lookup + ); + + std::optional bounding_box(id_type id); + + bool remove(id_type id); + + void render( + modes::point_cloud render_mode, + const glm::mat4& vp_matrix, + const glm::vec3& camera_position, + const lighting_setup& + ); + +protected: + [[nodiscard]] std::pair lookup_batch(const batch_components_type& batch_component) const; + +private: + int m_render_mode_count; + + std::vector> m_component_lookup; + std::vector m_id_lookup; + std::vector> m_batches; + std::vector m_shader_programs; + + batch_id_type m_next_batch_id{ 0 }; +}; + +} diff --git a/include/rendering/batches/mesh_batch.hpp b/include/rendering/batches/mesh_batch.hpp new file mode 100644 index 0000000..f5cbb87 --- /dev/null +++ b/include/rendering/batches/mesh_batch.hpp @@ -0,0 +1,55 @@ +#pragma once + +#include +#include +#include "opengl/handles/mesh_handle.hpp" +#include "opengl/handles/matrix_handles.hpp" +#include "opengl/handles/texture_handle.hpp" +#include "opengl/handles/mesh_handle.hpp" +#include "opengl/handles/material_handle.hpp" +#include "geometry/aabb.hpp" + +class mesh_batch +{ +public: + using id_type = ztu::u32; + + inline id_type add( + const zgl::mesh_handle& mesh, + const aabb& bounding_box, + const zgl::model_matrix_handle& transform, + const zgl::material_handle& material + ); + + inline std::optional bounding_box(id_type id); + + inline bool remove(id_type id); + + [[nodiscard]] inline std::span meshes() const; + + [[nodiscard]] inline std::span bounding_boxes() const; + + [[nodiscard]] inline std::span transforms() const; + + [[nodiscard]] inline std::span textures() const; + + [[nodiscard]] inline std::span surface_properties() const; + + [[nodiscard]] inline std::span alphas() const; + +private: + std::vector m_meshes{}; + std::vector m_bounding_boxes{}; + std::vector m_transforms{}; + std::vector m_textures{}; + std::vector m_surface_properties{}; + std::vector m_alphas{}; + + std::vector m_id_lookup{}; + + id_type m_next_mesh_id{ 0 }; +}; + +#define INCLUDE_MESH_BATCH_IMPLEMENTATION +#include "rendering/batches/mesh_batch.ipp" +#undef INCLUDE_MESH_BATCH_IMPLEMENTATION diff --git a/include/rendering/batches/point_cloud_batch.hpp b/include/rendering/batches/point_cloud_batch.hpp new file mode 100644 index 0000000..4c3a2d4 --- /dev/null +++ b/include/rendering/batches/point_cloud_batch.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include +#include "opengl/handles/point_cloud_handle.hpp" +#include "opengl/handles/matrix_handles.hpp" +#include + +#include "geometry/aabb.hpp" +#include + +class point_cloud_batch +{ +public: + using id_type = ztu::u32; + + point_cloud_batch() = default; + + inline id_type add( + const zgl::point_cloud_handle& point_cloud, + const aabb& bounding_box, + const zgl::model_matrix_handle& transform + ); + + inline std::optional bounding_box(id_type id); + + inline bool remove(id_type id); + + [[nodiscard]] inline std::span point_clouds() const; + + [[nodiscard]] inline std::span transforms() const; + + [[nodiscard]] inline std::span bounding_boxes() const; + +private: + std::vector m_point_clouds{}; + std::vector m_transforms{}; + std::vector m_bounding_boxes{}; + + std::vector m_id_lookup{}; + + id_type m_next_id{ 0 }; +}; + +#define INCLUDE_POINT_CLOUD_BATCH_IMPLEMENTATION +#include "rendering/batches/point_cloud_batch.ipp" +#undef INCLUDE_POINT_CLOUD_BATCH_IMPLEMENTATION diff --git a/include/rendering/modes/mesh_modes.hpp b/include/rendering/modes/mesh_modes.hpp new file mode 100644 index 0000000..b3038f3 --- /dev/null +++ b/include/rendering/modes/mesh_modes.hpp @@ -0,0 +1,13 @@ +#pragma once + +namespace rendering::modes +{ +enum class mesh +{ + wire_frame, + points, + faces, + lit_faces, + count +}; +}; diff --git a/include/rendering/modes/point_cloud_modes.hpp b/include/rendering/modes/point_cloud_modes.hpp new file mode 100644 index 0000000..0531c5c --- /dev/null +++ b/include/rendering/modes/point_cloud_modes.hpp @@ -0,0 +1,11 @@ +#pragma once + +namespace rendering::modes +{ +enum class point_cloud +{ + uniform_color, + rainbow, + count +}; +} diff --git a/include/rendering/requirements/mesh_requirements.hpp b/include/rendering/requirements/mesh_requirements.hpp new file mode 100644 index 0000000..fbe515d --- /dev/null +++ b/include/rendering/requirements/mesh_requirements.hpp @@ -0,0 +1,127 @@ +#pragma once + +#include "assets/components/material_components.hpp" +#include "assets/components/mesh_vertex_components.hpp" +#include "shader_program/capabilities/mesh_capabilities.hpp" + +#include + +namespace rendering::requirements::mesh +{ + +struct type +{ + shader_program::capabilities::mesh::indices::type shader_program_requirement_index{}; + components::mesh_vertex::flags vertex_requirements{ + components::mesh_vertex::flags::none + }; + material_component::flags material_requirements{ + material_component::flags::none + }; +}; + +enum class flags : int +{ + none = 0, + position = 1 << 0, + lit = 1 << 1, + textured = 1 << 2, + uniform_color = 1 << 3, + uniform_alpha = 1 << 4, + point = 1 << 5 +}; + +constexpr inline auto position = type{ + .shader_program_requirement_index = shader_program::capabilities::mesh::indices::position, + .vertex_requirements = components::mesh_vertex::flags::position +}; + +constexpr inline auto lit = type{ + .shader_program_requirement_index = shader_program::capabilities::mesh::indices::lit, + .vertex_requirements = components::mesh_vertex::flags::normal, + .material_requirements = material_component::flags::surface_properties +}; + +constexpr inline auto point = type{ + .shader_program_requirement_index = shader_program::capabilities::mesh::indices::point +}; + +constexpr inline auto textured = type{ + .shader_program_requirement_index = shader_program::capabilities::mesh::indices::textured, + .vertex_requirements = components::mesh_vertex::flags::tex_coord, + .material_requirements = material_component::flags::texture, +}; + +constexpr inline auto uniform_color = type{ + .shader_program_requirement_index = shader_program::capabilities::mesh::indices::uniform_color +}; + +constexpr inline auto uniform_alpha = type{ + .shader_program_requirement_index = shader_program::capabilities::mesh::indices::uniform_alpha, + .material_requirements = material_component::flags::transparency +}; + +constexpr inline auto all = std::array{ + position, lit, textured, uniform_color, uniform_alpha, point +}; + +} + + +[[nodiscard]] constexpr rendering::requirements::mesh::flags operator|( + const rendering::requirements::mesh::flags& a, const rendering::requirements::mesh::flags& b +) { + return static_cast(static_cast(a) | static_cast(b)); +} + +[[nodiscard]] constexpr rendering::requirements::mesh::flags operator&( + const rendering::requirements::mesh::flags& a, const rendering::requirements::mesh::flags& b +) { + return static_cast(static_cast(a) & static_cast(b)); +} + +[[nodiscard]] constexpr rendering::requirements::mesh::flags operator^( + const rendering::requirements::mesh::flags& a, const rendering::requirements::mesh::flags& b +) { + return static_cast(static_cast(a) ^ static_cast(b)); +} + +[[nodiscard]] constexpr rendering::requirements::mesh::flags operator~(const rendering::requirements::mesh::flags& a) { + return static_cast(~static_cast(a)); +} + +constexpr rendering::requirements::mesh::flags& operator|=(rendering::requirements::mesh::flags& a, const rendering::requirements::mesh::flags& b) { + return a = a | b; +} + +constexpr rendering::requirements::mesh::flags& operator&=(rendering::requirements::mesh::flags& a, const rendering::requirements::mesh::flags& b) { + return a = a & b; +} + +constexpr rendering::requirements::mesh::flags& operator^=(rendering::requirements::mesh::flags& a, const rendering::requirements::mesh::flags& b) { + return a = a ^ b; +} + +[[nodiscard]] constexpr bool operator<( + rendering::requirements::mesh::flags lhs, rendering::requirements::mesh::flags rhs +) { + return static_cast(lhs) < static_cast(rhs); +} + +[[nodiscard]] constexpr bool operator<=( + rendering::requirements::mesh::flags lhs, rendering::requirements::mesh::flags rhs +) { + return static_cast(lhs) <= static_cast(rhs); +} + +[[nodiscard]] constexpr bool operator>( + rendering::requirements::mesh::flags lhs, rendering::requirements::mesh::flags rhs +) { + return static_cast(lhs) > static_cast(rhs); +} + +[[nodiscard]] constexpr bool operator>=( + rendering::requirements::mesh::flags lhs, rendering::requirements::mesh::flags rhs +) { + return static_cast(lhs) >= static_cast(rhs); +} diff --git a/include/rendering/requirements/point_cloud_requirements.hpp b/include/rendering/requirements/point_cloud_requirements.hpp new file mode 100644 index 0000000..4502ae3 --- /dev/null +++ b/include/rendering/requirements/point_cloud_requirements.hpp @@ -0,0 +1,122 @@ +#pragma once + +#include "assets/components/point_cloud_vertex_components.hpp" +#include "shader_program/capabilities/point_cloud_capabilities.hpp" + +#include + +namespace rendering::requirements::point_cloud +{ + +struct type +{ + shader_program::capabilities::point_cloud::indices::type shader_program_requirement_index{}; + components::point_cloud_vertex::flags vertex_requirements{ + components::point_cloud_vertex::flags::none + }; +}; + + +enum class flags : int +{ + none = 0, + position = 1 << 0, + vertex_color = 1 << 1, + uniform_color = 1 << 2, + normal = 1 << 3, + reflectance = 1 << 4, + rainbow = 1 << 5 +}; + + +constexpr inline auto position = type{ + .shader_program_requirement_index = shader_program::capabilities::point_cloud::indices::position, + .vertex_requirements = components::point_cloud_vertex::flags::position +}; + +constexpr inline auto rainbow = type{ + .shader_program_requirement_index = shader_program::capabilities::point_cloud::indices::rainbow +}; + +constexpr inline auto vertex_color = type{ + .shader_program_requirement_index = shader_program::capabilities::point_cloud::indices::vertex_color, + .vertex_requirements = components::point_cloud_vertex::flags::color +}; + +constexpr inline auto uniform_color = type{ + .shader_program_requirement_index = shader_program::capabilities::point_cloud::indices::uniform_color +}; + +constexpr inline auto normal = type{ + .shader_program_requirement_index = shader_program::capabilities::point_cloud::indices::normal, + .vertex_requirements = components::point_cloud_vertex::flags::normal +}; + +constexpr inline auto reflectance = type{ + .shader_program_requirement_index = shader_program::capabilities::point_cloud::indices::reflectance, + .vertex_requirements = components::point_cloud_vertex::flags::reflectance +}; + +constexpr inline auto all = std::array{ + position, vertex_color, uniform_color, normal, reflectance, rainbow +}; +} + + +[[nodiscard]] constexpr rendering::requirements::point_cloud::flags operator|( + const rendering::requirements::point_cloud::flags& a, const rendering::requirements::point_cloud::flags& b +) { + return static_cast(static_cast(a) | static_cast(b)); +} + +[[nodiscard]] constexpr rendering::requirements::point_cloud::flags operator&( + const rendering::requirements::point_cloud::flags& a, const rendering::requirements::point_cloud::flags& b +) { + return static_cast(static_cast(a) & static_cast(b)); +} + +[[nodiscard]] constexpr rendering::requirements::point_cloud::flags operator^( + const rendering::requirements::point_cloud::flags& a, const rendering::requirements::point_cloud::flags& b +) { + return static_cast(static_cast(a) ^ static_cast(b)); +} + +[[nodiscard]] constexpr rendering::requirements::point_cloud::flags operator~(const rendering::requirements::point_cloud::flags& a) { + return static_cast(~static_cast(a)); +} + +constexpr rendering::requirements::point_cloud::flags& operator|=(rendering::requirements::point_cloud::flags& a, const rendering::requirements::point_cloud::flags& b) { + return a = a | b; +} + +constexpr rendering::requirements::point_cloud::flags& operator&=(rendering::requirements::point_cloud::flags& a, const rendering::requirements::point_cloud::flags& b) { + return a = a & b; +} + +constexpr rendering::requirements::point_cloud::flags& operator^=(rendering::requirements::point_cloud::flags& a, const rendering::requirements::point_cloud::flags& b) { + return a = a ^ b; +} + +[[nodiscard]] constexpr bool operator<( + rendering::requirements::point_cloud::flags lhs, rendering::requirements::point_cloud::flags rhs +) { + return static_cast(lhs) < static_cast(rhs); +} + +[[nodiscard]] constexpr bool operator<=( + rendering::requirements::point_cloud::flags lhs, rendering::requirements::point_cloud::flags rhs +) { + return static_cast(lhs) <= static_cast(rhs); +} + +[[nodiscard]] constexpr bool operator>( + rendering::requirements::point_cloud::flags lhs, rendering::requirements::point_cloud::flags rhs +) { + return static_cast(lhs) > static_cast(rhs); +} + +[[nodiscard]] constexpr bool operator>=( + rendering::requirements::point_cloud::flags lhs, rendering::requirements::point_cloud::flags rhs +) { + return static_cast(lhs) >= static_cast(rhs); +} diff --git a/include/rendering/shader_program_lookups/mesh_lookup.hpp b/include/rendering/shader_program_lookups/mesh_lookup.hpp new file mode 100644 index 0000000..c583c98 --- /dev/null +++ b/include/rendering/shader_program_lookups/mesh_lookup.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include "opengl/shader_program_lookup.hpp" +#include "opengl/handles/shader_program_handle.hpp" +#include "rendering/requirements/mesh_requirements.hpp" + +namespace rendering::shader_program_lookups +{ + +class mesh_lookup +{ +public: + void add( + const zgl::shader_program_handle& shader_program_handle + ); + + [[nodiscard]] std::optional find( + requirements::mesh::flags requirements + ) const; + + void print(); + +private: + zgl::shader_program_lookup m_shader_program_lookup; +}; + +} diff --git a/include/rendering/shader_program_lookups/point_cloud_lookup.hpp b/include/rendering/shader_program_lookups/point_cloud_lookup.hpp new file mode 100644 index 0000000..4fdcd30 --- /dev/null +++ b/include/rendering/shader_program_lookups/point_cloud_lookup.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include "opengl/shader_program_lookup.hpp" +#include "opengl/handles/shader_program_handle.hpp" +#include "rendering/requirements/point_cloud_requirements.hpp" + +namespace rendering::shader_program_lookups +{ + +class point_cloud_lookup +{ +public: + void add( + const zgl::shader_program_handle& shader_program_handle + ); + + [[nodiscard]] std::optional find( + requirements::point_cloud::flags requirements + ) const; + +private: + zgl::shader_program_lookup m_program_lookup; +}; + +} diff --git a/include/scene/camera.hpp b/include/scene/camera.hpp new file mode 100755 index 0000000..9195948 --- /dev/null +++ b/include/scene/camera.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include "scene/camera_view.hpp" + +class camera { +public: + virtual ~camera() = default; + + virtual void update( + float time_delta, + glm::vec2 mouse_pos_delta, + float mouse_wheel_delta, + camera_view& view + ) = 0; + + virtual void look_at( + const glm::vec3& origin, + const glm::vec3& target, + camera_view& view + ) = 0; +}; diff --git a/include/scene/flying_camera.hpp b/include/scene/flying_camera.hpp new file mode 100755 index 0000000..5b50b65 --- /dev/null +++ b/include/scene/flying_camera.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include "scene/camera.hpp" + + + +class flying_camera : public camera { +public: + explicit flying_camera(float yaw, float pitch, float roll); + + void update( + float time_delta, + glm::vec2 mouse_pos_delta, + float mouse_wheel_delta, + camera_view& view + ) override; + + void look_at( + const glm::vec3& origin, + const glm::vec3& target, + camera_view& view + ) override; + +private: + glm::vec3 m_velocity{ 0.0f, 0.0f, 0.0f }; + glm::mat3 m_world_rotation; + glm::vec3 m_world_up; + + float m_pitch, m_yaw, m_roll; +}; diff --git a/include/scene/lighting_setup.hpp b/include/scene/lighting_setup.hpp new file mode 100644 index 0000000..9b5a9e8 --- /dev/null +++ b/include/scene/lighting_setup.hpp @@ -0,0 +1,10 @@ +#pragma once + +#include "glm/glm.hpp" + +struct lighting_setup +{ + glm::vec3 point_light_direction; + glm::vec3 point_light_color; + glm::vec3 ambient_light_color; +}; diff --git a/include/shader_program/attributes/mesh_attributes.hpp b/include/shader_program/attributes/mesh_attributes.hpp new file mode 100644 index 0000000..c017a3d --- /dev/null +++ b/include/shader_program/attributes/mesh_attributes.hpp @@ -0,0 +1,83 @@ +#pragma once + +#include "opengl/shader_program_variable.hpp" +#include + +namespace shader_program::attributes::mesh +{ + +enum class flags : int { + none = 0, + position = 1 << 0, + normal = 1 << 1, + tex_coord = 1 << 2 +}; + +constexpr inline auto position = zgl::shader_program_variable({ GL_FLOAT_VEC3, 0 }, "vertex_position"); +constexpr inline auto normal = zgl::shader_program_variable({ GL_FLOAT_VEC3, 1 }, "vertex_normal"); +constexpr inline auto tex_coord = zgl::shader_program_variable({ GL_FLOAT_VEC2, 2 }, "vertex_tex_coord"); + +constexpr inline auto all = std::array{ + position, normal, tex_coord +}; + +} + + +[[nodiscard]] constexpr shader_program::attributes::mesh::flags operator|( + const shader_program::attributes::mesh::flags& a, const shader_program::attributes::mesh::flags& b +) { + return static_cast(static_cast(a) | static_cast(b)); +} + +[[nodiscard]] constexpr shader_program::attributes::mesh::flags operator&( + const shader_program::attributes::mesh::flags& a, const shader_program::attributes::mesh::flags& b +) { + return static_cast(static_cast(a) & static_cast(b)); +} + +[[nodiscard]] constexpr shader_program::attributes::mesh::flags operator^( + const shader_program::attributes::mesh::flags& a, const shader_program::attributes::mesh::flags& b +) { + return static_cast(static_cast(a) ^ static_cast(b)); +} + +[[nodiscard]] constexpr shader_program::attributes::mesh::flags operator~(const shader_program::attributes::mesh::flags& a) { + return static_cast(~static_cast(a)); +} + +constexpr shader_program::attributes::mesh::flags& operator|=(shader_program::attributes::mesh::flags& a, const shader_program::attributes::mesh::flags& b) { + return a = a | b; +} + +constexpr shader_program::attributes::mesh::flags& operator&=(shader_program::attributes::mesh::flags& a, const shader_program::attributes::mesh::flags& b) { + return a = a & b; +} + +constexpr shader_program::attributes::mesh::flags& operator^=(shader_program::attributes::mesh::flags& a, const shader_program::attributes::mesh::flags& b) { + return a = a ^ b; +} + +[[nodiscard]] constexpr bool operator<( + shader_program::attributes::mesh::flags lhs, shader_program::attributes::mesh::flags rhs +) { + return static_cast(lhs) < static_cast(rhs); +} + +[[nodiscard]] constexpr bool operator<=( + shader_program::attributes::mesh::flags lhs, shader_program::attributes::mesh::flags rhs +) { + return static_cast(lhs) <= static_cast(rhs); +} + +[[nodiscard]] constexpr bool operator>( + shader_program::attributes::mesh::flags lhs, shader_program::attributes::mesh::flags rhs +) { + return static_cast(lhs) > static_cast(rhs); +} + +[[nodiscard]] constexpr bool operator>=( + shader_program::attributes::mesh::flags lhs, shader_program::attributes::mesh::flags rhs +) { + return static_cast(lhs) >= static_cast(rhs); +} diff --git a/include/shader_program/attributes/point_cloud_attributes.hpp b/include/shader_program/attributes/point_cloud_attributes.hpp new file mode 100644 index 0000000..5533e34 --- /dev/null +++ b/include/shader_program/attributes/point_cloud_attributes.hpp @@ -0,0 +1,84 @@ +#pragma once + +#include "opengl/shader_program_variable.hpp" +#include + +namespace shader_program::attributes::point_cloud +{ + +enum class flags : int { + none = 0, + position = 1 << 0, + normal = 1 << 1, + color = 1 << 2, + reflectance = 1 << 3 +}; + +constexpr inline auto position = zgl::shader_program_variable({ GL_FLOAT_VEC3, 0 }, "vertex_position"); +constexpr inline auto normal = zgl::shader_program_variable({ GL_FLOAT_VEC3, 1 }, "vertex_normal"); +constexpr inline auto color = zgl::shader_program_variable({ GL_FLOAT_VEC3, 2 }, "vertex_color"); +constexpr inline auto reflectance = zgl::shader_program_variable({ GL_FLOAT, 2 }, "vertex_reflectance"); + +constexpr inline auto all = std::array{ + position, normal, color, reflectance +}; + +} + +[[nodiscard]] constexpr shader_program::attributes::point_cloud::flags operator|( + const shader_program::attributes::point_cloud::flags& a, const shader_program::attributes::point_cloud::flags& b +) { + return static_cast(static_cast(a) | static_cast(b)); +} + +[[nodiscard]] constexpr shader_program::attributes::point_cloud::flags operator&( + const shader_program::attributes::point_cloud::flags& a, const shader_program::attributes::point_cloud::flags& b +) { + return static_cast(static_cast(a) & static_cast(b)); +} + +[[nodiscard]] constexpr shader_program::attributes::point_cloud::flags operator^( + const shader_program::attributes::point_cloud::flags& a, const shader_program::attributes::point_cloud::flags& b +) { + return static_cast(static_cast(a) ^ static_cast(b)); +} + +[[nodiscard]] constexpr shader_program::attributes::point_cloud::flags operator~(const shader_program::attributes::point_cloud::flags& a) { + return static_cast(~static_cast(a)); +} + +constexpr shader_program::attributes::point_cloud::flags& operator|=(shader_program::attributes::point_cloud::flags& a, const shader_program::attributes::point_cloud::flags& b) { + return a = a | b; +} + +constexpr shader_program::attributes::point_cloud::flags& operator&=(shader_program::attributes::point_cloud::flags& a, const shader_program::attributes::point_cloud::flags& b) { + return a = a & b; +} + +constexpr shader_program::attributes::point_cloud::flags& operator^=(shader_program::attributes::point_cloud::flags& a, const shader_program::attributes::point_cloud::flags& b) { + return a = a ^ b; +} + +[[nodiscard]] constexpr bool operator<( + shader_program::attributes::point_cloud::flags lhs, shader_program::attributes::point_cloud::flags rhs +) { + return static_cast(lhs) < static_cast(rhs); +} + +[[nodiscard]] constexpr bool operator<=( + shader_program::attributes::point_cloud::flags lhs, shader_program::attributes::point_cloud::flags rhs +) { + return static_cast(lhs) <= static_cast(rhs); +} + +[[nodiscard]] constexpr bool operator>( + shader_program::attributes::point_cloud::flags lhs, shader_program::attributes::point_cloud::flags rhs +) { + return static_cast(lhs) > static_cast(rhs); +} + +[[nodiscard]] constexpr bool operator>=( + shader_program::attributes::point_cloud::flags lhs, shader_program::attributes::point_cloud::flags rhs +) { + return static_cast(lhs) >= static_cast(rhs); +} diff --git a/include/shader_program/capabilities/mesh_capabilities.hpp b/include/shader_program/capabilities/mesh_capabilities.hpp new file mode 100644 index 0000000..fd6b1f1 --- /dev/null +++ b/include/shader_program/capabilities/mesh_capabilities.hpp @@ -0,0 +1,85 @@ +#pragma once + +#include "assets/components/mesh_vertex_components.hpp" +#include "assets/components/point_cloud_vertex_components.hpp" +#include "assets/components/material_components.hpp" +#include "shader_program/attributes/mesh_attributes.hpp" +#include "shader_program/uniforms/mesh_uniforms.hpp" + +namespace shader_program::capabilities::mesh +{ + +struct type +{ + attributes::mesh::flags attributes{ + attributes::mesh::flags::none + }; + uniforms::mesh::flags uniforms{ + uniforms::mesh::flags::none + }; +}; + +namespace indices +{ +using type = ztu::u8; +constexpr inline type position = 0; +constexpr inline type lit = 1; +constexpr inline type textured = 2; +constexpr inline type uniform_color = 3; +constexpr inline type uniform_alpha = 4; +constexpr inline type point = 5; +} + +enum class flags : int +{ + none = 0, + position = 1 << indices::position, + lit = 1 << indices::lit, + textured = 1 << indices::textured, + uniform_color = 1 << indices::uniform_color, + uniform_alpha = 1 << indices::uniform_alpha, + point = 1 << indices::point +}; + +constexpr inline auto position = type{ + .attributes = attributes::mesh::flags::position, + .uniforms = uniforms::mesh::flags::mvp +}; + +constexpr inline auto lit = type{ + .attributes = attributes::mesh::flags::normal, + .uniforms = ( + uniforms::mesh::flags::model_matrix | + uniforms::mesh::flags::view_pos | + uniforms::mesh::flags::point_light_direction | + uniforms::mesh::flags::point_light_color | + uniforms::mesh::flags::ambient_light_color | + uniforms::mesh::flags::ambient_filter | + uniforms::mesh::flags::diffuse_filter | + uniforms::mesh::flags::specular_filter | + uniforms::mesh::flags::shininess + ) +}; + +constexpr inline auto point = type{ + .uniforms = uniforms::mesh::flags::point_size +}; + +constexpr inline auto textured = type{ + .attributes = attributes::mesh::flags::tex_coord, + .uniforms = uniforms::mesh::flags::tex +}; + +constexpr inline auto uniform_color = type{ + .uniforms = uniforms::mesh::flags::color +}; + +constexpr inline auto uniform_alpha = type{ + .uniforms = uniforms::mesh::flags::alpha +}; + +constexpr inline auto all = std::array{ + position, lit, textured, uniform_color, uniform_alpha, point +}; + +} diff --git a/include/shader_program/capabilities/point_cloud_capabilities.hpp b/include/shader_program/capabilities/point_cloud_capabilities.hpp new file mode 100644 index 0000000..018a09a --- /dev/null +++ b/include/shader_program/capabilities/point_cloud_capabilities.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include "shader_program/attributes/point_cloud_attributes.hpp" +#include "shader_program/uniforms/point_cloud_uniforms.hpp" + +#include "assets/components/mesh_vertex_components.hpp" +#include "assets/components/point_cloud_vertex_components.hpp" + +#include + +namespace shader_program::capabilities::point_cloud +{ + +struct type +{ + attributes::point_cloud::flags attributes{ + attributes::point_cloud::flags::none + }; + uniforms::point_cloud::flags uniforms{ + uniforms::point_cloud::flags::none + }; +}; + +namespace indices +{ +using type = ztu::u8; +constexpr inline type position = 0; +constexpr inline type vertex_color = 1; +constexpr inline type uniform_color = 2; +constexpr inline type normal = 3; +constexpr inline type reflectance = 4; +constexpr inline type rainbow = 5; +} + +enum class flags : int +{ + none = 0, + position = 1 << indices::position, + vertex_color = 1 << indices::vertex_color, + uniform_color = 1 << indices::uniform_color, + normal = 1 << indices::normal, + reflectance = 1 << indices::reflectance, + rainbow = 1 << indices::rainbow + }; + +constexpr inline auto position = type{ + .attributes = attributes::point_cloud::flags::position, + .uniforms = uniforms::point_cloud::flags::mvp +}; + +constexpr inline auto rainbow = type{}; + +constexpr inline auto vertex_color = type{ + .attributes = attributes::point_cloud::flags::color +}; + +constexpr inline auto uniform_color = type{ + .uniforms = uniforms::point_cloud::flags::color +}; + +constexpr inline auto normal = type{ + .attributes = attributes::point_cloud::flags::normal, + .uniforms = ( + uniforms::point_cloud::flags::model | + uniforms::point_cloud::flags::camera_position + ) +}; + +constexpr inline auto reflectance = type{ + .attributes = attributes::point_cloud::flags::reflectance + }; + +constexpr inline auto all = std::array{ + position, vertex_color, uniform_color, normal, reflectance, rainbow +}; + +} diff --git a/include/shader_program/uniforms/mesh_uniforms.hpp b/include/shader_program/uniforms/mesh_uniforms.hpp new file mode 100644 index 0000000..afa8dd8 --- /dev/null +++ b/include/shader_program/uniforms/mesh_uniforms.hpp @@ -0,0 +1,117 @@ +#pragma once + +#include "opengl/shader_program_variable.hpp" +#include + +namespace shader_program::uniforms::mesh +{ + +enum class flags : int +{ + none = 0, + mvp = 1 << 0, + model_matrix = 1 << 1, + point_size = 1 << 2, + color = 1 << 3, + tex = 1 << 4, + view_pos = 1 << 5, + point_light_direction = 1 << 6, + point_light_color = 1 << 7, + ambient_light_color = 1 << 8, + ambient_filter = 1 << 9, + diffuse_filter = 1 << 10, + specular_filter = 1 << 11, + shininess = 1 << 12, + alpha = 1 << 13 +}; + +constexpr inline auto mvp = zgl::shader_program_variable({ GL_FLOAT_MAT4, 0 }, "mvp_matrix"); +constexpr inline auto model_matrix = zgl::shader_program_variable({ GL_FLOAT_MAT4, 1 }, "model_matrix"); +constexpr inline auto point_size = zgl::shader_program_variable({ GL_FLOAT, 2 }, "point_size"); +constexpr inline auto color = zgl::shader_program_variable({ GL_FLOAT_VEC4, 3 }, "color"); +constexpr inline auto tex = zgl::shader_program_variable({ GL_SAMPLER_2D, 3 }, "tex"); +constexpr inline auto view_pos = zgl::shader_program_variable({ GL_FLOAT_VEC3, 4 }, "view_pos"); +constexpr inline auto point_light_direction = zgl::shader_program_variable({ GL_FLOAT_VEC3, 5 }, "point_light_direction"); +constexpr inline auto point_light_color = zgl::shader_program_variable({ GL_FLOAT_VEC3, 6 }, "point_light_color"); +constexpr inline auto ambient_light_color = zgl::shader_program_variable({ GL_FLOAT_VEC3, 7 }, "ambient_light_color"); +constexpr inline auto ambient_filter = zgl::shader_program_variable({ GL_FLOAT_VEC3, 8 }, "ambient_filter"); +constexpr inline auto diffuse_filter = zgl::shader_program_variable({ GL_FLOAT_VEC3, 9 }, "diffuse_filter"); +constexpr inline auto specular_filter = zgl::shader_program_variable({ GL_FLOAT_VEC3, 10 }, "specular_filter"); +constexpr inline auto shininess = zgl::shader_program_variable({ GL_FLOAT, 11 }, "shininess"); +constexpr inline auto alpha = zgl::shader_program_variable({ GL_FLOAT, 12 }, "alpha"); + +constexpr inline auto all = std::array{ + mvp, + model_matrix, + point_size, + color, + tex, + view_pos, + point_light_direction, + point_light_color, + ambient_light_color, + ambient_filter, + diffuse_filter, + specular_filter, + shininess, + alpha +}; +} + +[[nodiscard]] constexpr shader_program::uniforms::mesh::flags operator|( + const shader_program::uniforms::mesh::flags& a, const shader_program::uniforms::mesh::flags& b +) { + return static_cast(static_cast(a) | static_cast(b)); +} + +[[nodiscard]] constexpr shader_program::uniforms::mesh::flags operator&( + const shader_program::uniforms::mesh::flags& a, const shader_program::uniforms::mesh::flags& b +) { + return static_cast(static_cast(a) & static_cast(b)); +} + +[[nodiscard]] constexpr shader_program::uniforms::mesh::flags operator^( + const shader_program::uniforms::mesh::flags& a, const shader_program::uniforms::mesh::flags& b +) { + return static_cast(static_cast(a) ^ static_cast(b)); +} + +[[nodiscard]] constexpr shader_program::uniforms::mesh::flags operator~(const shader_program::uniforms::mesh::flags& a) { + return static_cast(~static_cast(a)); +} + +constexpr shader_program::uniforms::mesh::flags& operator|=(shader_program::uniforms::mesh::flags& a, const shader_program::uniforms::mesh::flags& b) { + return a = a | b; +} + +constexpr shader_program::uniforms::mesh::flags& operator&=(shader_program::uniforms::mesh::flags& a, const shader_program::uniforms::mesh::flags& b) { + return a = a & b; +} + +constexpr shader_program::uniforms::mesh::flags& operator^=(shader_program::uniforms::mesh::flags& a, const shader_program::uniforms::mesh::flags& b) { + return a = a ^ b; +} + +[[nodiscard]] constexpr bool operator<( + shader_program::uniforms::mesh::flags lhs, shader_program::uniforms::mesh::flags rhs +) { + return static_cast(lhs) < static_cast(rhs); +} + +[[nodiscard]] constexpr bool operator<=( + shader_program::uniforms::mesh::flags lhs, shader_program::uniforms::mesh::flags rhs +) { + return static_cast(lhs) <= static_cast(rhs); +} + +[[nodiscard]] constexpr bool operator>( + shader_program::uniforms::mesh::flags lhs, shader_program::uniforms::mesh::flags rhs +) { + return static_cast(lhs) > static_cast(rhs); +} + +[[nodiscard]] constexpr bool operator>=( + shader_program::uniforms::mesh::flags lhs, shader_program::uniforms::mesh::flags rhs +) { + return static_cast(lhs) >= static_cast(rhs); +} diff --git a/include/shader_program/uniforms/point_cloud_uniforms.hpp b/include/shader_program/uniforms/point_cloud_uniforms.hpp new file mode 100644 index 0000000..f849ab2 --- /dev/null +++ b/include/shader_program/uniforms/point_cloud_uniforms.hpp @@ -0,0 +1,97 @@ +#pragma once + +#include "opengl/shader_program_variable.hpp" +#include + +namespace shader_program::uniforms::point_cloud +{ + +enum class flags : int +{ + none = 0, + mvp = 1 << 0, + point_size = 1 << 1, + color = 1 << 2, + model = 1 << 3, + camera_position = 1 << 4, + rainbow_offset_y = 1 << 5, + rainbow_scale_y = 1 << 6, +}; + +constexpr inline auto mvp = zgl::shader_program_variable({ GL_FLOAT_MAT4, 0 }, "mvp"); +constexpr inline auto point_size = zgl::shader_program_variable({ GL_FLOAT, 2 }, "point_size"); +constexpr inline auto color = zgl::shader_program_variable({ GL_FLOAT_VEC4, 3 }, "color"); +constexpr inline auto model = zgl::shader_program_variable({ GL_FLOAT_MAT4, 4 }, "model"); +constexpr inline auto camera_position = zgl::shader_program_variable({ GL_FLOAT_VEC3, 5 }, "camera_position"); +constexpr inline auto rainbow_offset_y = zgl::shader_program_variable({ GL_FLOAT, 6 }, "rainbow_offset_y"); +constexpr inline auto rainbow_scale_y = zgl::shader_program_variable({ GL_FLOAT, 7 }, "rainbow_scale_y"); + +constexpr inline auto all = std::array{ + mvp, + point_size, + color, + model, + camera_position, + rainbow_offset_y, + rainbow_scale_y +}; + +} + +[[nodiscard]] constexpr shader_program::uniforms::point_cloud::flags operator|( + const shader_program::uniforms::point_cloud::flags& a, const shader_program::uniforms::point_cloud::flags& b +) { + return static_cast(static_cast(a) | static_cast(b)); +} + +[[nodiscard]] constexpr shader_program::uniforms::point_cloud::flags operator&( + const shader_program::uniforms::point_cloud::flags& a, const shader_program::uniforms::point_cloud::flags& b +) { + return static_cast(static_cast(a) & static_cast(b)); +} + +[[nodiscard]] constexpr shader_program::uniforms::point_cloud::flags operator^( + const shader_program::uniforms::point_cloud::flags& a, const shader_program::uniforms::point_cloud::flags& b +) { + return static_cast(static_cast(a) ^ static_cast(b)); +} + +[[nodiscard]] constexpr shader_program::uniforms::point_cloud::flags operator~(const shader_program::uniforms::point_cloud::flags& a) { + return static_cast(~static_cast(a)); +} + +constexpr shader_program::uniforms::point_cloud::flags& operator|=(shader_program::uniforms::point_cloud::flags& a, const shader_program::uniforms::point_cloud::flags& b) { + return a = a | b; +} + +constexpr shader_program::uniforms::point_cloud::flags& operator&=(shader_program::uniforms::point_cloud::flags& a, const shader_program::uniforms::point_cloud::flags& b) { + return a = a & b; +} + +constexpr shader_program::uniforms::point_cloud::flags& operator^=(shader_program::uniforms::point_cloud::flags& a, const shader_program::uniforms::point_cloud::flags& b) { + return a = a ^ b; +} + +[[nodiscard]] constexpr bool operator<( + shader_program::uniforms::point_cloud::flags lhs, shader_program::uniforms::point_cloud::flags rhs +) { + return static_cast(lhs) < static_cast(rhs); +} + +[[nodiscard]] constexpr bool operator<=( + shader_program::uniforms::point_cloud::flags lhs, shader_program::uniforms::point_cloud::flags rhs +) { + return static_cast(lhs) <= static_cast(rhs); +} + +[[nodiscard]] constexpr bool operator>( + shader_program::uniforms::point_cloud::flags lhs, shader_program::uniforms::point_cloud::flags rhs +) { + return static_cast(lhs) > static_cast(rhs); +} + +[[nodiscard]] constexpr bool operator>=( + shader_program::uniforms::point_cloud::flags lhs, shader_program::uniforms::point_cloud::flags rhs +) { + return static_cast(lhs) >= static_cast(rhs); +} diff --git a/include/util/arx.hpp b/include/util/arx.hpp new file mode 100755 index 0000000..fced69c --- /dev/null +++ b/include/util/arx.hpp @@ -0,0 +1,285 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include "util/uix.hpp" +#include "util/pack.hpp" +#include "util/function.hpp" +#include "util/string_literal.hpp" +#include "util/for_each.hpp" + + +namespace ztu { + +static constexpr char NO_SHORT_FLAG = '\0'; + +namespace arx_internal { + +template +concept parsing_function = ( + ztu::callable, std::string_view> and + (not std::is_class_v or std::is_empty_v) +); + +} // namespace arx_internal + +template< + char ShortName, + ztu::string_literal LongName, + typename T = bool, + auto Parse = std::nullptr_t{} +> requires ( +std::same_as or + arx_internal::parsing_function +) +struct arx_flag { + static constexpr auto short_name = ShortName; + static constexpr auto long_name = LongName; + using type = T; + static constexpr auto parse = Parse; +}; + +namespace arx_parsers { + +template +requires (Base > 0) +[[nodiscard]] inline std::optional integer(const std::string_view& str); + +template +[[nodiscard]] inline std::optional floating_point(const std::string_view& str); + +} // namespace arx_parsers + +namespace arx_internal { + +template +struct flag_by_name { + template + struct pred : std::conditional_t< + Flag::long_name == LongName, + std::true_type, + std::false_type + > { + }; + using type = ztu::find; +}; + +template +using flag_by_name_t = flag_by_name::type; + +template +using flag_type_by_name_t = flag_by_name_t::type; + +} // namespace arx_internal + + +template +class arx { +private: + static constexpr auto short_flag_prefix = std::string_view{ "-" }; + static constexpr auto long_flag_prefix = std::string_view{ "--" }; + static constexpr auto UNBOUND_ARGUMENT = ztu::isize_max; + +public: + inline arx(int num_args, const char* const* args); + + template + [[nodiscard]] inline std::optional> get() const; + + [[nodiscard]] inline std::optional get(ztu::isize position) const; + + [[nodiscard]] inline ztu::isize num_positional() const; + +protected: + [[nodiscard]] inline std::optional find_flag_value(ztu::isize flag_index) const; + + template + [[nodiscard]] inline std::optional> + parse_value(const std::string_view& value_str) const; + +private: + std::vector> m_arguments; + ztu::isize m_unbound_begin; +}; + + +namespace arx_parsers { + +template +requires (Base > 0) +[[nodiscard]] inline std::optional integer(const std::string_view& str) { + Type value{}; + const auto [ptr, ec] = std::from_chars(str.begin(), str.end(), value, Base); + if (ec == std::errc() and ptr == str.end()) { + return value; + } + return std::nullopt; +} + +template +[[nodiscard]] inline std::optional floating_point(const std::string_view& str) { + Type value{}; + const auto [ptr, ec] = std::from_chars(str.begin(), str.end(), value, Format); + if (ec == std::errc() and ptr == str.end()) { + return value; + } + return std::nullopt; +} + +} // namespace arx_parsers + +template +arx::arx(int num_args, const char* const* args) { + + m_arguments.reserve(std::max(num_args - 1, 0)); + + for (int i = 1; i < num_args; i++) { + const auto argument = std::string_view{ args[i] }; + + const auto found_match = for_each::indexed_type( + [&]() { + if (( + Flag::short_name != NO_SHORT_FLAG and + argument.length() == short_flag_prefix.length() + 1 and + argument.starts_with(short_flag_prefix) and + argument[short_flag_prefix.length()] == Flag::short_name + ) or ( + argument.length() == long_flag_prefix.length() + Flag::long_name.length() and + argument.starts_with(long_flag_prefix) and + argument.substr(long_flag_prefix.length()) == Flag::long_name + )) { + if constexpr (std::same_as) { + m_arguments.emplace_back(Index, argument); + } else { + if (i + 1 < num_args) { + const auto value = std::string_view{ args[++i] }; + m_arguments.emplace_back(Index, value); + } + } + return true; + } + return false; + } + ); + + if (not found_match) { + m_arguments.emplace_back(UNBOUND_ARGUMENT, argument); + } + } + + std::sort( + m_arguments.begin(), m_arguments.end(), [](const auto& a, const auto& b) -> bool { + return a.first < b.first; + } + ); + + const auto first_unbound = std::find_if( + m_arguments.begin(), m_arguments.end(), [](const auto& a) { + return a.first == UNBOUND_ARGUMENT; + } + ); + + const auto num_bound = m_arguments.end() - first_unbound; + + const auto last = std::unique( + std::reverse_iterator(first_unbound), m_arguments.rend(), [](const auto& a, const auto& b) { + return a.first == b.first; + } + ).base(); + + m_arguments.erase(m_arguments.begin(), last); + + m_unbound_begin = m_arguments.size() - num_bound; +}; + + +template +std::optional arx::find_flag_value(isize flag_index) const { + const auto begin = m_arguments.begin(); + const auto end = begin + m_unbound_begin; + const auto it = std::lower_bound( + begin, end, flag_index, [](const auto& a, const auto& b) { + return a.first < b; + } + ); + if (it != end and it->first == flag_index) { + return { it->second }; + } + return std::nullopt; +} + + +template +template +std::optional> arx::parse_value( + const std::string_view& value_str +) const { + + using Flag = arx_internal::flag_by_name_t; + using Type = Flag::type; + using opt_t = std::optional; + + opt_t ret; + + // Use custom parser if provided and if not try using a default parser + if constexpr ( + requires(const std::string_view str) { + { + std::invoke(Flag::parse, str) + } -> std::same_as; + } + ) { + return std::invoke(Flag::parse, value_str); + } else if constexpr (std::integral && not std::same_as) { + return arx_parsers::integer(value_str); + } else if constexpr (std::floating_point) { + return arx_parsers::floating_point(value_str); + } else if constexpr (std::same_as) { + return value_str; + } else if constexpr (std::same_as) { + return std::string(value_str); + } else { + Type::__cannot_parse_this_type; + } + + return ret; +} + + +template +template +std::optional> arx::get() const { + using Flag = arx_internal::flag_by_name_t; + static constexpr auto index = index_of; + + if (index < sizeof...(Flags)) { + const auto value_opt = find_flag_value(index); + if constexpr (std::same_as) { + return value_opt.has_value(); + } else if (value_opt) { + return parse_value(*value_opt); + } + } + + return std::nullopt; +} + +template +inline isize arx::num_positional() const { + return m_arguments.size() - m_unbound_begin; +} + +template +std::optional arx::get(isize position) const { + if (0 <= position and position < num_positional()) { + return { m_arguments[m_unbound_begin + position].second }; + } + return std::nullopt; +} + +} // namespace ztu + diff --git a/include/util/binary_ifstream.hpp b/include/util/binary_ifstream.hpp new file mode 100644 index 0000000..14dd1a1 --- /dev/null +++ b/include/util/binary_ifstream.hpp @@ -0,0 +1,207 @@ +#pragma once + +#include +#include +#include +#include +#include + +class binary_ifstream { +public: + using char_type = char; + using size_type = std::streamsize; + + [[nodiscard]] inline std::error_code open( + const std::filesystem::path& filename, + bool check_file_before_open + ); + + [[nodiscard]] inline std::error_code read(std::span buffer); + + template + [[nodiscard]] std::error_code read(T& integer); + + template + [[nodiscard]] std::error_code read_ieee754(T& floating_point); + + [[nodiscard]] inline std::error_code skip(size_type count); + + template + [[nodiscard]] std::error_code skip(); + + template + [[nodiscard]] std::error_code skip(); + + inline void close(); + +protected: + [[nodiscard]] inline static std::error_code check_file( + const std::filesystem::path& filename + ); + + [[nodiscard]] inline std::errc get_stream_error_code() const; + +private: + std::basic_ifstream in{}; +}; + +inline std::errc binary_ifstream::get_stream_error_code() const { + const auto state = in.rdstate(); + + auto code = std::errc{}; + + if (state != std::ios::goodbit) + { + code = std::errc::state_not_recoverable; + + if (state & std::ios::failbit) // irrecoverable stream error + { + code = std::errc::state_not_recoverable; + } + else if (state & std::ios::badbit) // input/output operation failed + { + code = std::errc::io_error; + } + else if (state & std::ios::eofbit) // input sequence has reached end-of-file + { + code = std::errc::result_out_of_range; + } + } + + return code; +} + +inline std::error_code binary_ifstream::check_file(const std::filesystem::path& filename) +{ + auto error = std::error_code{}; + + const auto is_file = std::filesystem::exists(filename, error); + + if (error) + { + return error; + } + + if (not is_file) + { + return std::make_error_code(std::errc::invalid_argument); + } + + const auto files_exists = std::filesystem::exists(filename, error); + + if (error) + { + return error; + } + + if (not files_exists) + { + return std::make_error_code(std::errc::no_such_file_or_directory); + } + + return {}; +} + +inline std::error_code binary_ifstream::open( + const std::filesystem::path& filename, + bool check_file_before_open +) { + if (check_file_before_open) + { + if (const auto error = check_file(filename)) + { + return error; + } + } + + in = {}; + + // Turn of exceptions, as errors are handled via the stream state. + in.exceptions(std::ios::goodbit); + + in.open(filename, std::ios::binary | std::ios::in); + + if (const auto error = std::make_error_code(get_stream_error_code())) + { + return error; + } + + if (not in.is_open()) + { + // Unknown error, so 'state_not_recoverable' is assumed. + return std::make_error_code(std::errc::state_not_recoverable); + } + + return {}; +} + +inline void binary_ifstream::close() +{ + in.close(); +} + +inline std::error_code binary_ifstream::skip(size_type count) +{ + in.ignore(count); + return std::make_error_code(get_stream_error_code()); +} + +template +std::error_code binary_ifstream::skip() +{ + return skip(sizeof(T)); +} + +template +std::error_code binary_ifstream::skip() +{ + return skip(sizeof(T)); +} + +inline std::error_code binary_ifstream::read(std::span buffer) +{ + in.read(buffer.data(), static_cast(buffer.size())); + return std::make_error_code(get_stream_error_code()); +} + +template +std::error_code binary_ifstream::read(T& integer) +{ + std::array buffer; + if (const auto error = read(buffer)) + { + return error; + } + + if constexpr (std::endian::native != E) + { + std::reverse(buffer.begin(), buffer.end()); + } + + // Use memcpy to avoid UB. + std::memcpy(static_cast(&integer), buffer.data(), buffer.size()); + + return {}; +} + +template +std::error_code binary_ifstream::read_ieee754(T& floating_point) +{ + static_assert(std::numeric_limits::is_iec559); + + std::array buffer; + if (const auto error = read(buffer)) + { + return error; + } + + if constexpr (std::endian::native != E) + { + std::reverse(buffer.begin(), buffer.end()); + } + + // Use memcpy to avoid UB. + std::memcpy(static_cast(&floating_point), buffer.data(), buffer.size()); + + return {}; +} diff --git a/include/util/enum_operators.hpp b/include/util/enum_operators.hpp new file mode 100644 index 0000000..601df2c --- /dev/null +++ b/include/util/enum_operators.hpp @@ -0,0 +1,57 @@ +#pragma once + +#define DEFINE_ENUM_FLAG_OPERATORS(ENUM_TYPE) \ +[[nodiscard]] constexpr ENUM_TYPE operator|( \ + const ENUM_TYPE lhs, const ENUM_TYPE rhs \ +) { \ + return static_cast( \ + static_cast>(lhs) | \ + static_cast>(rhs) \ + ); \ +} \ + \ +[[nodiscard]] constexpr ENUM_TYPE operator&( \ + const ENUM_TYPE lhs, const ENUM_TYPE rhs \ +) { \ + return static_cast( \ + static_cast>(lhs) & \ + static_cast>(rhs) \ + ); \ +} \ + \ +[[nodiscard]] constexpr ENUM_TYPE operator^( \ + const ENUM_TYPE lhs, const ENUM_TYPE rhs \ +) { \ + return static_cast( \ + static_cast>(lhs) ^ \ + static_cast>(rhs) \ + ); \ +} \ + \ +[[nodiscard]] constexpr ENUM_TYPE operator~(const ENUM_TYPE a) \ +{ \ + return static_cast( \ + ~static_cast>(a) \ + ); \ +} \ + \ +constexpr ENUM_TYPE& operator|=( \ + ENUM_TYPE& lhs, \ + const ENUM_TYPE rhs \ +) { \ + return lhs = lhs | rhs; \ +} \ + \ +constexpr ENUM_TYPE& operator&=( \ + ENUM_TYPE& lhs, \ + const ENUM_TYPE rhs \ +) { \ + return lhs = lhs & rhs; \ +} \ + \ +constexpr ENUM_TYPE& operator^=( \ + ENUM_TYPE& lhs, \ + const ENUM_TYPE rhs \ +) { \ + return lhs = lhs ^ rhs; \ +} diff --git a/include/util/extra_arx_parsers.hpp b/include/util/extra_arx_parsers.hpp new file mode 100755 index 0000000..41c8d2d --- /dev/null +++ b/include/util/extra_arx_parsers.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include +#include +#include +#include + +namespace extra_arx_parsers { + + template + requires (Count > 0) + [[nodiscard]] inline std::optional> glm_vec(const std::string_view& str) { + glm::vec vec{}; + auto it = str.cbegin(); + for (int i = 0; i < Count; i++) { + const auto [ptr, ec] = std::from_chars(it, str.cend(), vec[i], std::chars_format::general); + if (ec != std::errc()) { + return std::nullopt; + } + it = ptr + 1; // skip space in between components + } + if (it < str.cend()) { + return std::nullopt; + } + return vec; + } + +} diff --git a/include/util/for_each.hpp b/include/util/for_each.hpp new file mode 100755 index 0000000..75ddd17 --- /dev/null +++ b/include/util/for_each.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include + +namespace ztu::for_each { + + template + inline constexpr bool type(auto &&f) { + return (f.template operator()() || ...); + } + + template + inline constexpr bool value(auto &&f) { + return (f.template operator()() || ...); + } + + template + inline constexpr bool argument(auto &&f, Args&&... args) { + return (f(std::forward(args)) || ...); + } + + template + inline constexpr bool index(auto&& f) { + return [&](std::index_sequence) { + return (f.template operator()() || ...); + }(std::make_index_sequence()); + } + + template + inline constexpr bool indexed_type(auto &&f) { + return [&](std::index_sequence) { + return (f.template operator()() || ...); + }(std::make_index_sequence()); + } + + template + inline constexpr bool indexed_value(auto &&f) { + return [&](std::index_sequence) { + return (f.template operator()() || ...); + }(std::make_index_sequence()); + } + + template + inline constexpr bool indexed_argument(auto &&f, Args&&... args) { + return [&](std::index_sequence) { + return (f.template operator()(std::forward(args)) || ...); + }(std::make_index_sequence()); + } +} diff --git a/include/util/function.hpp b/include/util/function.hpp new file mode 100755 index 0000000..cc4b80d --- /dev/null +++ b/include/util/function.hpp @@ -0,0 +1,63 @@ +#pragma once + +#include +#include + +namespace ztu { + + template + concept callable = std::same_as, R>; + + template + concept runnable = callable; + + template + concept supplier = callable; + + template + concept consumer = callable; + + template + concept predicate = callable; + + + template + struct function_meta; + + template + struct function_meta { + using ret_t = R; + using args_t = std::tuple; + static constexpr bool is_const = false; + }; + + template + struct function_meta { + using class_t = C; + using ret_t = R; + using args_t = std::tuple; + static constexpr bool is_const = false; + }; + + template + struct function_meta { + using class_t = C; + using ret_t = R; + using args_t = std::tuple; + static constexpr bool is_const = true; + }; + + namespace function { + template + using class_t = typename function_meta::class_t; + + template + using ret_t = typename function_meta::ret_t; + + template + using args_t = typename function_meta::args_t; + + template + constexpr bool is_const_v = function_meta::is_const; + } +} diff --git a/include/util/id_type.hpp b/include/util/id_type.hpp new file mode 100644 index 0000000..ac91005 --- /dev/null +++ b/include/util/id_type.hpp @@ -0,0 +1,25 @@ +#pragma once + +namespace ztu +{ +template +class id_type_for +{ + friend Parent; + + using index_type = IndexType; + + explicit constexpr id_type_for(index_type index) : index{ index } {} + + index_type index{}; + +public: + constexpr id_type_for() = default; + constexpr auto operator<=>(const id_type_for&) const = default; + + constexpr operator bool() const + { + return index == index_type{}; + } +}; +} \ No newline at end of file diff --git a/include/util/image.hpp b/include/util/image.hpp new file mode 100755 index 0000000..a56d736 --- /dev/null +++ b/include/util/image.hpp @@ -0,0 +1,327 @@ +#pragma once + + +#include +#include +#include +#include +#include +#include + + +#if defined(__GNUC__) || defined(__GNUG__) +#pragma GCC diagnostic push +#pragma GCC system_header +#elif defined(_MSC_VER) +#pragma warning(push, 0) +#endif + +#define STB_IMAGE_IMPLEMENTATION +#define STB_IMAGE_STATIC +#include "stb_image.h" + + +#define STBI_MSC_SECURE_CRT +#define STB_IMAGE_WRITE_STATIC +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include "stb_image_write.h" + + +#if defined(__GNUC__) || defined(__GNUG__) +#pragma GCC diagnostic pop +#elif defined(_MSC_VER) +#pragma warning(pop) +#endif + +namespace ztu { + +template +class image { +public: + using value_type = C; + using size_type = std::make_signed_t; + using difference_type = size_type; + using reference = std::add_lvalue_reference_t; + using const_reference = std::add_const_t; + using pointer = value_type*; + using const_pointer = const value_type*; + using iterator = pointer; + using const_iterator = const_pointer; + +public: + [[nodiscard]] static std::error_code load(const char* filename, image& dst, bool flip = false); + + [[nodiscard]] static image create(size_type width, size_type height, const value_type& color); + +public: + image() = default; + + image(std::unique_ptr&& data, size_type width, size_type height); + + image(const image&); + + image(image&&) noexcept; + + [[nodiscard]] image& operator=(const image&); + + [[nodiscard]] image& operator=(image&&) noexcept; + + [[nodiscard]] value_type operator()(double x, double y) const; + + [[nodiscard]] const_reference operator()(size_type x, size_type y) const; + + [[nodiscard]] reference operator()(size_type x, size_type y); + + [[nodiscard]] const_iterator operator[](size_type y) const; + + [[nodiscard]] iterator operator[](size_type y); + + [[nodiscard]] int save(const std::string& filename) const; + + [[nodiscard]] bool contains(size_type x, size_type y) const; + + [[nodiscard]] size_type width() const; + + [[nodiscard]] size_type height() const; + + [[nodiscard]] std::pair size() const; + + [[nodiscard]] size_type num_pixels() const; + + [[nodiscard]] const_iterator begin() const; + + [[nodiscard]] iterator begin(); + + [[nodiscard]] const_iterator end() const; + + [[nodiscard]] iterator end(); + + [[nodiscard]] const_iterator cbegin() const; + + [[nodiscard]] const_iterator cend() const; + + [[nodiscard]] const_pointer data() const; + + [[nodiscard]] pointer data(); + +private: + std::unique_ptr m_data{ nullptr }; + size_type m_width{ 0 }, m_height{ 0 }; +}; + +template +image::image(std::unique_ptr&& data, const size_type width, const size_type height) : + m_data{ std::move(data) }, m_width{ width }, m_height{ height } { +}; + +template +image::image(const image& other) : + m_data{ new C[other.m_width * other.m_height] }, + m_width{ other.m_width }, m_height{ other.m_height } { + std::copy_n(other.m_data.get(), other.m_width * other.m_height, this->m_data.get()); +} + +template +image::image(image&& other) noexcept : + m_data{ std::move(other.m_data) }, + m_width{ other.m_width }, m_height{ other.m_height } { + other.m_width = 0; + other.m_height = 0; +} + +template +image& image::operator=(const image& other) { + if (this != &other) { + + const auto m_num_pixels = m_width * m_height; + const auto o_num_pixels = other.m_width * other.m_height; + + if (o_num_pixels > m_num_pixels) { + this->~image(); + this->m_data = new C[o_num_pixels]; + } + + std::copy_n(other.m_data, o_num_pixels, this->m_data); + + this->m_width = other.m_width; + this->m_height = other.m_height; + } + return *this; +} + +template +image& image::operator=(image&& other) noexcept { + if (this != &other) { + this->~image(); + + this->m_data = std::move(other.m_data); + this->m_width = other.m_width; + this->m_height = other.m_height; + + other.m_width = 0; + other.m_height = 0; + } + return *this; +} + + +template +std::error_code image::load(const char* filename, image& dst, bool flip) { + int width, height, channels; + + stbi_set_flip_vertically_on_load(flip); + + auto data = reinterpret_cast(stbi_load(filename, &width, &height, &channels, sizeof(C))); + + if (data == nullptr) { + return std::make_error_code(std::errc::no_such_file_or_directory); + } + + dst.m_data.reset(data); + dst.m_width = static_cast(width); + dst.m_height = static_cast(height); + + return {}; +} + +template +image image::create(const size_type width, const size_type height, const C& color) { + + const auto num_pixels = width * height; + C* data = new C[num_pixels]; + + std::fill_n(data, num_pixels, color); + + return image(data, width, height); +} + +template +typename image::size_type image::width() const { + return m_width; +} + +template +typename image::size_type image::height() const { + return m_height; +} + +template +std::pair::size_type, typename image::size_type> image::size() const { + return { m_width, m_height }; +} + +template +typename image::size_type image::num_pixels() const { + return m_width * m_height; +} + +template +int image::save(const std::string& filename) const { + + std::string ext = filename.substr(filename.rfind('.') + 1, filename.length()); + std::transform( + ext.begin(), ext.end(), ext.begin(), [](unsigned char c) { + return std::tolower(c); + } + ); + + int status = -1; + + if (ext == "png") { + status = stbi_write_png(filename.c_str(), m_width, m_height, sizeof(C), m_data, m_width * sizeof(C)); + } else if (ext == "bmp") { + status = stbi_write_bmp(filename.c_str(), m_width, m_height, sizeof(C), m_data); + } else if (ext == "tga") { + status = stbi_write_tga(filename.c_str(), m_width, m_height, sizeof(C), m_data); + } else if (ext == "jpg" || ext == "jpeg") { + status = stbi_write_jpg(filename.c_str(), m_width, m_height, sizeof(C), m_data, m_width * sizeof(C)); + } + + return status; +} + +template +bool image::contains(size_type x, size_type y) const { + return 0 <= x and x < m_width and 0 <= y and y < m_height; +} + +template +typename image::const_reference image::operator()(size_type x, size_type y) const { + const auto clamped_x = std::clamp(x, static_cast(0), m_width - 1); + const auto clamped_y = std::clamp(y, static_cast(0), m_height - 1); + return m_data[clamped_x + clamped_y * m_width]; +} + +template +typename image::reference image::operator()(size_type x, size_type y) { + const auto clamped_x = std::clamp(x, static_cast(0), m_width - 1); + const auto clamped_y = std::clamp(y, static_cast(0), m_height - 1); + return m_data[clamped_x + clamped_y * m_width]; +} + +template +typename image::value_type image::operator()(double x, double y) const { + auto min_x = static_cast(std::floor(x)); + auto min_y = static_cast(std::floor(y)); + const auto px00 = (*this)(min_x, min_y), px10 = (*this)(min_x + 1, min_y); + const auto px01 = (*this)(min_x, min_y + 1), px11 = (*this)(min_x + 1, min_y + 1); + const auto a_x = x - static_cast(min_x), a_y = y - static_cast(min_y); + return std::lerp( + std::lerp(px00, px10, a_x), + std::lerp(px01, px11, a_x), + a_y + ); +} + +template +typename image::const_iterator image::operator[](size_type y) const { + return &m_data[y * m_width]; +} + +template +typename image::iterator image::operator[](size_type y) { + return &m_data[y * m_width]; +} + + +template +typename image::const_iterator image::begin() const { + return m_data.get(); +} + +template +typename image::iterator image::begin() { + return m_data.get(); +} + +template +typename image::const_iterator image::end() const { + return m_data.get() + m_width * m_height; +} + +template +typename image::iterator image::end() { + return m_data.get() + m_width * m_height; +} + +template +typename image::const_iterator image::cbegin() const { + return const_cast(begin()); +} + +template +typename image::const_iterator image::cend() const { + return const_cast(begin()); +} + +template +typename image::const_pointer image::data() const { + return m_data.get(); +} + +template +typename image::pointer image::data() { + return m_data.get(); +} + +} // namespace ztu diff --git a/include/util/line_parser.hpp b/include/util/line_parser.hpp new file mode 100644 index 0000000..b7d2826 --- /dev/null +++ b/include/util/line_parser.hpp @@ -0,0 +1,87 @@ +#pragma once + +#include +#include +#include +#include "util/for_each.hpp" + +namespace ztu +{ +template +struct prefixed_line_parser { + const std::string_view prefix; + F parse; +}; + +template +struct line_repeating_type {}; + +static constexpr auto is_repeating = line_repeating_type{}; +static constexpr auto is_not_repeating = line_repeating_type{}; + +template +prefixed_line_parser make_line_parser(std::string_view prefix, line_repeating_type, F&& f) +{ + return { prefix, std::forward(f) }; +} + +template +E parse_lines(std::istream& in, const bool pedantic, prefixed_line_parser&&... parsers) +{ + + auto ec = E{}; + + std::string line; + while (std::getline(in, line)) [[likely]] + { + parse_current_line: + + auto line_updated = false; + + for_each::argument( + [&](prefixed_line_parser&& parser) -> bool + { + if (line.starts_with(parser.prefix)) + { + const auto prefix_length = parser.prefix.length(); + ec = parser.parse(line.substr(prefix_length)); + + if constexpr (R) + { + while (std::getline(in, line)) + { + if (line.starts_with(parser.prefix)) [[likely]] + { + ec = parser.parse(line.substr(prefix_length)); + } + else [[unlikely]] + { + line_updated = true; + break; + } + } + } + + return true; + } + + return false; + }, + std::forward>(parsers)... + ); + + if (pedantic) { + if (ec != E{}) [[unlikely]] { + return ec; + } + } + + if (line_updated) + { + goto parse_current_line; + } + } + + return E{}; +} +} diff --git a/include/util/logger.hpp b/include/util/logger.hpp new file mode 100755 index 0000000..09ff75a --- /dev/null +++ b/include/util/logger.hpp @@ -0,0 +1,1742 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ztu { +namespace logger::settings { + +/** + * @brief Placeholder symbol used in format strings to signal value position. + */ +constexpr char format_symbol = '%'; + +/** + * @brief symbol for escaping the format symbol. + */ +constexpr char format_escape_symbol = '\\'; + +/** + * @brief Fall back to dynamic global config (instead of the static one) if none is provided. + */ +constexpr auto use_dynamic_global_config = false; + +/** + * @brief Throw a compile time error if there are errors in a given format string.. + */ +constexpr auto compile_time_exceptions = true; + +/** + * @brief Throw a run time error if there are errors in a given format string.. + */ +inline auto run_time_exceptions = false; + +} // namespace logger::settings + +namespace logger_internal { + +template +concept printeable = requires(std::ostream& os, const T& value) +{ + { os << value } -> std::same_as; +}; + +namespace format_errors { + +using type = std::uint8_t; +static constexpr type none{ 0b0 }, too_many_values = error_t{ 0b1 }, not_enough_values{ 0b10 }; + +} // namespace format_errors + +template +struct basic_format_string { + using string_view = std::basic_string_view; + using size_type = string_view::size_type; + + string_view view; + std::array seg_lens; + format_errors::type errors{ format_errors::none }; + + template + consteval basic_format_string(const CharT (&s)[N]) : view(s) { + compile(logger::settings::compile_time_exceptions); + } + + template + requires(std::is_convertible_v) + basic_format_string(const S& s) : view(s) { + compile(logger::settings::run_time_exceptions); + } + + constexpr void compile(const bool throw_exceptions) { + auto begin = view.data(); + const auto end = begin + view.length(); + for (auto& seg_len : seg_lens) { + const auto it = find_format_symbol(begin, end); + seg_len = it - begin; + if (it == end) { + if (throw_exceptions) { + throw std::invalid_argument("More values than placeholders."); + } + errors = format_errors::too_many_values; + begin = end; + } else { + begin = it + 1; + } + } + if (find_format_symbol(begin, end) != end) { + if (throw_exceptions) { + throw std::invalid_argument("Fewer values than placeholders."); + } + errors = format_errors::not_enough_values; + } + } + + static constexpr const CharT* find_format_symbol(const CharT* begin, const CharT* const end) { + auto escaped = false; + while (begin != end) { + if (*begin == logger::settings::format_escape_symbol) { + escaped = true; + } else { + if (*begin == logger::settings::format_symbol and not escaped) { + break; + } + escaped = false; + } + ++begin; + } + return begin; + } +}; + +template +using format_string = basic_format_string...>; + + +template + requires(N > 0) +struct basic_string_literal { + + std::array m_value{}; + + consteval basic_string_literal(const CharT (&str)[N]) { + std::copy_n(std::begin(str), N, m_value.begin()); + m_value.back() = '\0'; + } + + [[nodiscard]] constexpr std::basic_string_view view() const { + return { m_value.data(), N - 1 }; + } +}; + +template +using string_literal = basic_string_literal; + +using flags_int_t = std::uint16_t; + +} // namespace logger_internal + +/** + * @brief A lightweight logging library providing color-coded output and flexible configuration. + * + * @details This namespace serves as a wrapper around std::ostream, offering a + * simple yet powerful logging interface with support for ANSI color-coded log + * levels. It provides a global logging context as well as customizable logger + * contexts, allowing users to configure output streams, log levels and more. + */ +namespace logger { + +/** + * @brief Enumeration representing the log levels. + * + * The log levels are ordered from least verbose to most verbose, as follows: + * - @c mute: Indicates that no log messages should be emitted. + * - @c generic: A catch-all levels for generic log messages via 'println'. + * - @c error: Used for critical errors that require immediate attention. + * - @c warn: Used for potential issues that may need attention. + * - @c info: Used for significant events and information about the process. + * - @c log: General log messages providing information about the application's operation. + * - @c debug: Detailed debug information helpful for troubleshooting. + */ +enum class level : std::uint8_t { + mute = 0, + generic = 1, + error = 2, + warn = 3, + info = 4, + log = 5, + debug = 6 +}; + +/** + * @brief Enumeration of feature flag for the logger class. + * + * The `flag` enum is a set of feature flag used in the logger class to control the behavior of log message output. + * Each flag corresponds to a specific feature or information component that can be included in log messages, + * providing a flexible and customizable way to tailor the levels of detail included in log outputs + * based on the specific needs of the application. + * + * @c none: Disables all flag. Log messages will only include the message without any additional information. + * @c all: Enables all available flag. Log messages will include all following features: + * @c colors: Enables color-coding for log messages, providing visual cues for different log levels. + * @c locking: Enables the use of a spin lock mechanism for thread-safe log message output. + * @c identifier -----------------------------------------------------------------------------------+ + * @c thread_id -----------------------------------------------------------------------+ | + * @c function_name ------------------------------------------------------------+ | | + * @c line_number ------------------------------------------------------+ | | | + * @c filename -----------------------------------------------+ | | | | + * @c level_name ----------------------------------------+ | | | | | + * @c timestamp -------------------------------+ | | | | | | + * @c time ----------------+ | | | | | | | + * @c date ----+ | | | | | | | | + * | | | | | | | | | + * example message: [2024-1-8][19:44:9.535][1704739449535][log][main.cpp:61][main][140654793804672][tester] Hello World + */ +enum class flag : logger_internal::flags_int_t { + none = 0, + all = std::numeric_limits::max(), + colors = (1 << 0), + locking = (1 << 1), + date = (1 << 2), + time = (1 << 3), + timestamp = (1 << 4), + level_name = (1 << 5), + filename = (1 << 6), + line_number = (1 << 7), + function_name = (1 << 8), + thread_id = (1 << 9) +}; + +} // namespace logger +} // namespace ztu + +[[nodiscard]] constexpr ztu::logger::flag operator|(const ztu::logger::flag& a, const ztu::logger::flag& b) { + return static_cast( + static_cast(a) | static_cast(b) + ); +} + +[[nodiscard]] constexpr ztu::logger::flag operator&(const ztu::logger::flag& a, const ztu::logger::flag& b) { + return static_cast( + static_cast(a) & static_cast(b) + ); +} + +[[nodiscard]] constexpr ztu::logger::flag operator^(const ztu::logger::flag& a, const ztu::logger::flag& b) { + return static_cast( + static_cast(a) ^ static_cast(b) + ); +} + +[[nodiscard]] constexpr ztu::logger::flag operator~(const ztu::logger::flag& a) { + return static_cast(~static_cast(a)); +} + +constexpr ztu::logger::flag& operator|=(ztu::logger::flag& a, const ztu::logger::flag& b) { + return a = a | b; +} + +constexpr ztu::logger::flag& operator&=(ztu::logger::flag& a, const ztu::logger::flag& b) { + return a = a & b; +} + +constexpr ztu::logger::flag& operator^=(ztu::logger::flag& a, const ztu::logger::flag& b) { + return a = a ^ b; +} + +inline std::ostream& operator<<(std::ostream& out, ztu::logger::level level) { + switch (level) { + using enum ztu::logger::level; + case mute: + return out << "mute"; + case error: + return out << "error"; + case warn: + return out << "warn"; + case info: + return out << "info"; + case log: + return out << "log"; + case debug: + return out << "debug"; + case generic: + return out << "generic"; + default: + return out << "unknown(" << std::to_string(static_cast(level)) << ")"; + } +} + +namespace ztu { +namespace logger { + +/** + * @brief Context structure for configuring the behavior of the logger class. + * + * The `context` struct provides a means to configure the logger's behavior by + * specifying the output streams, log message formatting flag, log levels, and + * an optional spin lock for thread safety. + * + * @c out: Pointer to the output stream for standard log messages. + * @c err: Pointer to the output stream for error log messages. + * @c lock: Atomic flag used for thread-safe log message output when the `locking` flag is enabled. + */ +struct context { + std::ostream* out; + std::ostream* err; + std::atomic_flag lock = ATOMIC_FLAG_INIT; + + inline context(std::ostream&, std::ostream&); +}; + +/** + * @brief Dynamic config structure for configuring the behavior of the logger class. + * + * The `context` struct provides a means to configure the logger's behavior by sspecifying the output streams, + * log message formatting flag, log levels, and an optional spin lock for thread safety. + * + * @param flag Flags representing the formatting options for log messages. See the flag enum for details. + * @param threshold Log levels setting, controlling the verbosity of logged messages. See the levels enum for details. + * @param identifier Atomic flag used for thread-safe log message output when the `locking` flag is enabled. + */ +struct dynamic_config { + flag flags; + level threshold; + std::string identifier{}; +}; + +/** + * @brief Static config structure for configuring the behavior of the logger class. + * + * The `context` struct provides a means to configure the logger's behavior by sspecifying the output streams, + * log message formatting flag, log levels, and an optional spin lock for thread safety. + * + * @tparam Flags Flags representing the formatting options for log messages. See the flag enum for details. + * @tparam Threshold Log levels setting, controlling the verbosity of logged messages. See the levels enum for details. + * @tparam Identifier Atomic flag used for thread-safe log message output when the `locking` flag is enabled. + */ +template +struct static_config { + static constexpr auto flags = Flags; + static constexpr auto threshold = Threshold; + static constexpr auto identifier = Identifier.view(); +}; + +/** + * @brief Returns the global fallback context used in all logging functions. + * + * @return A reference to the global context. + */ +inline context& global_context(); + +/** + * @brief Returns the global dynamic fallback config used in all logging functions. + * + * @return A reference to the global dynamic config. + */ +inline dynamic_config& global_dynamic_config(); + +/** + * @brief The global static fallback config used in all logging functions. + */ +inline constexpr auto global_static_config = static_config< + (flag::colors | flag::locking /*| flag::time*/ | flag::level_name | flag::filename | flag::line_number), + level::debug, + "">{}; + +namespace functions { +/** + * @brief Formats and logs an error message using the @c global_handle. + * + * The format string may contain placeholders (@c fmt_symbol) + * replaced by corresponding values provided in the arguments (@c args). + * + * @tparam Args Variadic template for the types of arguments passed to the format string. + * @param ctx The logging context used for writing out the formatted message. + * @param cfg The config for the message formatting and filtering. + * @param fmt The format string for the error message, supporting placeholders. + * @param args The values to be formatted and included in the error message. + */ +template +struct error { + explicit error( + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location = std::source_location::current() + ); + + explicit error( + context& ctx, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location = std::source_location::current() + ); + + explicit error( + const dynamic_config& cfg, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location = std::source_location::current() + ); + + template + explicit error( + const static_config& cfg, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location = std::source_location::current() + ); + + explicit error( + context& ctx, + const dynamic_config& cfg, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location = std::source_location::current() + ); + + template + explicit error( + context& ctx, + const static_config& cfg, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location = std::source_location::current() + ); +}; + +template +error(logger_internal::format_string, Args&&...) -> error; + +template +error(context& ctx, logger_internal::format_string, Args&&...) -> error; + +template +error(const dynamic_config& cfg, logger_internal::format_string, Args&&...) -> error; + +template +error(context& ctx, const dynamic_config& cfg, logger_internal::format_string, Args&&...) -> error; + +template +error(const static_config& cfg, logger_internal::format_string, Args&&...) + -> error; + +template +error( + context& ctx, + const static_config& cfg, + logger_internal::format_string, + Args&&... +) -> error; + +/** + * @brief Formats and logs a warn message using the @c global_handle. + * + * The format string may contain placeholders (@c fmt_symbol) + * replaced by corresponding values provided in the arguments (@c args). + * + * @tparam Args Variadic template for the types of arguments passed to the format string. + * @param ctx The logging context used for writing out the formatted message. + * @param cfg The config for the message formatting and filtering. + * @param fmt The format string for the warn message, supporting placeholders. + * @param args The values to be formatted and included in the warn message. + */ +template +struct warn { + explicit warn( + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location = std::source_location::current() + ); + + explicit warn( + context& ctx, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location = std::source_location::current() + ); + + explicit warn( + const dynamic_config& cfg, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location = std::source_location::current() + ); + + template + explicit warn( + const static_config& cfg, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location = std::source_location::current() + ); + + explicit warn( + context& ctx, + const dynamic_config& cfg, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location = std::source_location::current() + ); + + template + explicit warn( + context& ctx, + const static_config& cfg, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location = std::source_location::current() + ); +}; + +template +warn(logger_internal::format_string, Args&&...) -> warn; + +template +warn(context& ctx, logger_internal::format_string, Args&&...) -> warn; + +template +warn(const dynamic_config& cfg, logger_internal::format_string, Args&&...) -> warn; + +template +warn(context& ctx, const dynamic_config& cfg, logger_internal::format_string, Args&&...) -> warn; + +template +warn(const static_config& cfg, logger_internal::format_string, Args&&...) + -> warn; + +template +warn( + context& ctx, + const static_config& cfg, + logger_internal::format_string, + Args&&... +) -> warn; + +/** + * @brief Formats and logs an info message using the @c global_handle. + * + * The format string may contain placeholders (@c fmt_symbol) + * replaced by corresponding values provided in the arguments (@c args). + * + * @tparam Args Variadic template for the types of arguments passed to the format string. + * @param ctx The logging context used for writing out the formatted message. + * @param cfg The config for the message formatting and filtering. + * @param fmt The format string for the info message, supporting placeholders. + * @param args The values to be formatted and included in the info message. + */ +template +struct info { + explicit info( + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location = std::source_location::current() + ); + + explicit info( + context& ctx, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location = std::source_location::current() + ); + + explicit info( + const dynamic_config& cfg, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location = std::source_location::current() + ); + + template + explicit info( + const static_config& cfg, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location = std::source_location::current() + ); + + explicit info( + context& ctx, + const dynamic_config& cfg, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location = std::source_location::current() + ); + + template + explicit info( + context& ctx, + const static_config& cfg, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location = std::source_location::current() + ); +}; + +template +info(logger_internal::format_string, Args&&...) -> info; + +template +info(context& ctx, logger_internal::format_string, Args&&...) -> info; + +template +info(const dynamic_config& cfg, logger_internal::format_string, Args&&...) -> info; + +template +info(context& ctx, const dynamic_config& cfg, logger_internal::format_string, Args&&...) -> info; + +template +info(const static_config& cfg, logger_internal::format_string, Args&&...) + -> info; + +template +info( + context& ctx, + const static_config& cfg, + logger_internal::format_string, + Args&&... +) -> info; + +/** + * @brief Formats and logs a log message using the @c global_handle. + * + * The format string may contain placeholders (@c fmt_symbol) + * replaced by corresponding values provided in the arguments (@c args). + * + * @tparam Args Variadic template for the types of arguments passed to the format string. + * @param ctx The logging context used for writing out the formatted message. + * @param cfg The config for the message formatting and filtering. + * @param fmt The format string for the log message, supporting placeholders. + * @param args The values to be formatted and included in the log message. + */ +template +struct log { + explicit log( + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location = std::source_location::current() + ); + + explicit log( + context& ctx, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location = std::source_location::current() + ); + + explicit log( + const dynamic_config& cfg, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location = std::source_location::current() + ); + + template + explicit log( + const static_config& cfg, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location = std::source_location::current() + ); + + explicit log( + context& ctx, + const dynamic_config& cfg, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location = std::source_location::current() + ); + + template + explicit log( + context& ctx, + const static_config& cfg, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location = std::source_location::current() + ); +}; + +template +log(logger_internal::format_string, Args&&...) -> log; + +template +log(context& ctx, logger_internal::format_string, Args&&...) -> log; + +template +log(const dynamic_config& cfg, logger_internal::format_string, Args&&...) -> log; + +template +log(context& ctx, const dynamic_config& cfg, logger_internal::format_string, Args&&...) -> log; + +template +log(const static_config& cfg, logger_internal::format_string, Args&&...) + -> log; + +template +log(context& ctx, + const static_config& cfg, + logger_internal::format_string, + Args&&...) -> log; + +/** + * @brief Formats and logs a debug message using the @c global_handle. + * + * The format string may contain placeholders (@c fmt_symbol) + * replaced by corresponding values provided in the arguments (@c args). + * + * @tparam Args Variadic template for the types of arguments passed to the format string. + * @param ctx The logging context used for writing out the formatted message. + * @param cfg The config for the message formatting and filtering. + * @param fmt The format string for the debug message, supporting placeholders. + * @param args The values to be formatted and included in the debug message. + */ +template +struct debug { + explicit debug( + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location = std::source_location::current() + ); + + explicit debug( + context& ctx, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location = std::source_location::current() + ); + + explicit debug( + const dynamic_config& cfg, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location = std::source_location::current() + ); + + template + explicit debug( + const static_config& cfg, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location = std::source_location::current() + ); + + explicit debug( + context& ctx, + const dynamic_config& cfg, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location = std::source_location::current() + ); + + template + explicit debug( + context& ctx, + const static_config& cfg, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location = std::source_location::current() + ); +}; + +template +debug(logger_internal::format_string, Args&&...) -> debug; + +template +debug(context& ctx, logger_internal::format_string, Args&&...) -> debug; + +template +debug(const dynamic_config& cfg, logger_internal::format_string, Args&&...) -> debug; + +template +debug(context& ctx, const dynamic_config& cfg, logger_internal::format_string, Args&&...) -> debug; + +template +debug(const static_config& cfg, logger_internal::format_string, Args&&...) + -> debug; + +template +debug( + context& ctx, + const static_config& cfg, + logger_internal::format_string, + Args&&... +) -> debug; + +/** + * @brief Formats and logs a generic message using the @c global_handle. + * + * The format string may contain placeholders (@c fmt_symbol) + * replaced by corresponding values provided in the arguments (@c args). + * + * @tparam Args Variadic template for the types of arguments passed to the format string. + * @param ctx The logging context used for writing out the formatted message. + * @param cfg The config for the message formatting and filtering. + * @param fmt The format string for the generic message, supporting placeholders. + * @param args The values to be formatted and included in the generic message. + */ +template +struct println { + explicit println( + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location = std::source_location::current() + ); + + explicit println( + context& ctx, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location = std::source_location::current() + ); + + explicit println( + const dynamic_config& cfg, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location = std::source_location::current() + ); + + template + explicit println( + const static_config& cfg, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location = std::source_location::current() + ); + + explicit println( + context& ctx, + const dynamic_config& cfg, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location = std::source_location::current() + ); + + template + explicit println( + context& ctx, + const static_config& cfg, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location = std::source_location::current() + ); +}; + +template +println(logger_internal::format_string, Args&&...) -> println; + +template +println(context& ctx, logger_internal::format_string, Args&&...) -> println; + +template +println(const dynamic_config& cfg, logger_internal::format_string, Args&&...) -> println; + +template +println(context& ctx, const dynamic_config& cfg, logger_internal::format_string, Args&&...) + -> println; + +template +println(const static_config& cfg, logger_internal::format_string, Args&&...) + -> println; + +template +println( + context& ctx, + const static_config& cfg, + logger_internal::format_string, + Args&&... +) -> println; + +} // namespace functions + +} // namespace logger + +namespace logger_internal { + +enum class color : std::uint8_t { + reset = 0, + black = 30, + red = 31, + green = 32, + yellow = 33, + blue = 34, + magenta = 35, + cyan = 36, + white = 37 +}; + +constexpr std::size_t num_digits(std::uint32_t n, const std::uint32_t base = 10) { + if (n < base) { + return 1; + } + + auto digits = std::size_t{}; + while (n > 0) { + n /= base; + ++digits; + } + + return digits; +} + +template +constexpr auto int_to_str() { + auto str = std::array{}; + + auto index = str.size() - 1; + auto num = N; + do { + str[index] = static_cast('0' + num % B); + num /= B; + } while (index--); + + return str; +} + +template +constexpr auto create_ansi_color() { + constexpr auto color_index = static_cast(color); + constexpr auto color_index_str = int_to_str(); + + constexpr auto prefix = std::string_view("\x1B["); + constexpr auto postfix = [&]() -> std::string_view { + if constexpr (bright) { + return ";1m"; + } else { + return "m"; + } + }(); + + constexpr auto color_str_length = (prefix.size() + color_index_str.size() + postfix.size()); + std::array color_str{}; + + auto it = color_str.begin(); + const auto append = [&it](auto begin, const auto end) { + while (begin != end) { + *it++ = *begin++; + } + }; + + append(prefix.begin(), prefix.end()); + append(color_index_str.begin(), color_index_str.end()); + append(postfix.begin(), postfix.end()); + + return color_str; +} + +inline constexpr auto reset_color_array = create_ansi_color(); +inline constexpr auto reset_color = std::string_view(reset_color_array.data(), reset_color_array.size()); + +inline constexpr auto internal_color_array = create_ansi_color(); +inline constexpr auto internal_color = std::string_view(internal_color_array.data(), internal_color_array.size()); + +inline constexpr std::size_t level_count = 6; +inline constexpr std::array level_names{ "generic", "error", "warning", + "info", "log", "debug" }; + +template +constexpr auto create_color_tuple() { + return std::make_tuple(create_ansi_color()...); +} + +template +constexpr auto build_string_lookup(const std::tuple& string_tuple, std::index_sequence) { + return std::array{ std::string_view(std::get(string_tuple).data(), std::get(string_tuple).size())... }; +} + +inline constexpr auto level_dark_color_tuple = create_color_tuple< + false, + color::white, + color::red, + color::yellow, + color::green, + color::blue, + color::magenta>(); + +inline constexpr auto level_dark_color_lookup = build_string_lookup( + level_dark_color_tuple, std::make_index_sequence{} +); + +inline constexpr auto level_bright_color_tuple = create_color_tuple< + true, + color::white, + color::red, + color::yellow, + color::green, + color::blue, + color::magenta>(); + +inline constexpr auto level_bright_color_lookup = build_string_lookup( + level_bright_color_tuple, std::make_index_sequence{} +); + +inline void print_escaped_string( + std::ostream& out, + const std::string_view& reset_clr, + const std::string_view& internal_clr, + const char* begin, + const char* const end +) { + auto escaped = false; + auto it = begin; + while (it != end) { + if (*it == logger::settings::format_escape_symbol) { + escaped = true; + } else { + if (*it == logger::settings::format_symbol) { + out.write(begin, it - begin - escaped); + begin = it + 1 - escaped; + if (not escaped) { + out << internal_clr << logger::settings::format_symbol << reset_clr; + } + } + escaped = false; + } + ++it; + } + out.write(begin, it - begin); +} + +template +void print_format_impl( + std::ostream& out, + const std::string_view&, + std::string_view& reset_clr, + const std::string_view& internal_clr, + std::size_t pos, + bool&, + const Fmt& fmt +) { + print_escaped_string(out, reset_clr, internal_clr, &fmt.view[pos], fmt.view.data() + fmt.view.length()); +} + +template +void print_format_impl( + std::ostream& out, + const std::string_view& value_clr, + std::string_view& reset_clr, + const std::string_view& internal_clr, + std::size_t pos, + bool& excess_value, + Fmt& fmt, + Arg&& arg, + Rest&&... rest +) { + const auto arg_index = fmt.seg_lens.size() - sizeof...(Rest) - 1; + const auto len = fmt.seg_lens[arg_index]; + const auto str = &fmt.view[pos]; + print_escaped_string(out, reset_clr, internal_clr, str, str + len); + pos += len; + if (pos == fmt.view.length()) { + if (excess_value) { + out << ','; + } else { + out << internal_clr << " <["; + excess_value = true; + } + reset_clr = internal_clr; + } else { + ++pos; + } + out << value_clr << arg << reset_clr; + print_format_impl(out, value_clr, reset_clr, internal_clr, pos, excess_value, fmt, std::forward(rest)...); +} + +template +void print_format( + std::ostream& out, + const std::string_view& value_clr, + std::string_view& reset_clr, + const std::string_view& internal_clr, + Fmt& fmt, + Args&&... args +) { + const auto reset = reset_clr; + auto excess_value = false; + print_format_impl(out, value_clr, reset_clr, internal_clr, 0ul, excess_value, fmt, std::forward(args)...); + if (fmt.errors & format_errors::too_many_values) { + out << "] Excess value(s)>"; + } else if (fmt.errors & format_errors::not_enough_values) { + out << internal_clr << " "; + } + out << reset; +} + +template +void println_impl( + logger::context& ctx, + const logger::dynamic_config& cfg, + const std::source_location& location, + logger::level threshold, + format_string fmt, + Args&&... args +) { + using namespace logger; + + const auto current_level_index = static_cast(cfg.threshold); + auto level_index = static_cast(threshold); + if (current_level_index < level_index) { + return; + } + + if ((cfg.flags & flag::locking) != flag::none) { + while (ctx.lock.test_and_set(std::memory_order_relaxed)) { + } + } + + level_index = std::min(level_index, static_cast(level_count)) - 1; + + std::string_view bright_clr, dark_clr, reset_clr, internal_clr; + if ((cfg.flags & flag::colors) != flag::none) { + bright_clr = level_bright_color_lookup[level_index]; + dark_clr = level_dark_color_lookup[level_index]; + reset_clr = reset_color; + internal_clr = internal_color; + } + + auto& stream = (threshold == level::warn or threshold == level::error) ? *ctx.err : *ctx.out; + + const auto print_prefix = [&](const auto&... values) { + stream << '[' << bright_clr; + ((stream << values), ...) << reset_clr << ']'; + }; + + static constexpr auto needs_clock = flag::date | flag::time | flag::timestamp; + if ((cfg.flags & needs_clock) != flag::none) { + namespace chr = std::chrono; + + using clock = chr::system_clock; + const auto now = clock::now(); + + static constexpr auto needs_time = flag::date | flag::time; + if ((cfg.flags & needs_time) != flag::none) { + const auto time = clock::to_time_t(now); + const auto local_time = std::localtime(&time); + + if ((cfg.flags & flag::date) != flag::none) { + print_prefix(1'900 + local_time->tm_year, '-', 1 + local_time->tm_mon, '-', local_time->tm_mday); + } + + if ((cfg.flags & flag::time) != flag::none) { + const auto truncated = std::chrono::system_clock::from_time_t(time); + const auto millis = chr::duration_cast(now - truncated).count(); + + print_prefix(local_time->tm_hour, ':', local_time->tm_min, ':', local_time->tm_sec, '.', millis); + } + } + + if ((cfg.flags & flag::timestamp) != flag::none) { + const auto timestamp = chr::duration_cast(now.time_since_epoch()); + print_prefix(timestamp.count()); + } + } + + if ((cfg.flags & flag::level_name) != flag::none) { + print_prefix(level_names[level_index]); + } + + if ((cfg.flags & flag::filename) != flag::none) { + if ((cfg.flags & flag::line_number) != flag::none) { + print_prefix(' ', location.file_name(), ':', std::dec, location.line()); + } else { + print_prefix(location.file_name()); + } + } else if ((cfg.flags & flag::line_number) != flag::none) { + print_prefix(std::dec, location.line()); + } + + if ((cfg.flags & flag::function_name) != flag::none) { + print_prefix(location.function_name()); + } + + if ((cfg.flags & flag::thread_id) != flag::none) { + print_prefix(std::this_thread::get_id()); + } + + if (not cfg.identifier.empty()) { + print_prefix(cfg.identifier); + } + + static constexpr auto prefix_flags = ~(flag::colors | flag::locking); + if ((cfg.flags & prefix_flags) != flag::none or not cfg.identifier.empty()) { + stream << ' '; + } + + logger_internal::print_format(stream, dark_clr, reset_clr, internal_clr, fmt, std::forward(args)...); + stream << '\n'; + + ctx.lock.clear(); +} + +template +void println_impl( + logger::context& ctx, + const logger::static_config&, + const std::source_location& location, + logger::level threshold, + format_string fmt, + Args&&... args +) { + using namespace logger; + + constexpr auto current_level_index = static_cast(Threshold); + auto level_index = static_cast(threshold); + if (current_level_index < level_index) { + return; + } + + if constexpr ((Flags & flag::locking) != flag::none) { + while (ctx.lock.test_and_set(std::memory_order_relaxed)) { + } + } + + level_index = std::min(level_index, static_cast(level_count)) - 1; + + std::string_view bright_clr, dark_clr, reset_clr, internal_clr; + if constexpr ((Flags & flag::colors) != flag::none) { + bright_clr = level_bright_color_lookup[level_index]; + dark_clr = level_dark_color_lookup[level_index]; + reset_clr = reset_color; + internal_clr = internal_color; + } + + auto& stream = (threshold == level::warn or threshold == level::error) ? *ctx.err : *ctx.out; + + const auto print_prefix = [&](const auto&... values) { + stream << '[' << bright_clr; + ((stream << values), ...) << reset_clr << ']'; + }; + + static constexpr auto needs_clock = flag::date | flag::time | flag::timestamp; + if constexpr ((Flags & needs_clock) != flag::none) { + namespace chr = std::chrono; + + using clock = chr::system_clock; + const auto now = clock::now(); + + static constexpr auto needs_time = flag::date | flag::time; + if constexpr ((Flags & needs_time) != flag::none) { + const auto time = clock::to_time_t(now); + const auto local_time = std::localtime(&time); + + if constexpr ((Flags & flag::date) != flag::none) { + print_prefix(1'900 + local_time->tm_year, '-', 1 + local_time->tm_mon, '-', local_time->tm_mday); + } + + if constexpr ((Flags & flag::time) != flag::none) { + const auto truncated = std::chrono::system_clock::from_time_t(time); + const auto millis = chr::duration_cast(now - truncated).count(); + + print_prefix(local_time->tm_hour, ':', local_time->tm_min, ':', local_time->tm_sec, '.', millis); + } + } + + if ((Flags & flag::timestamp) != flag::none) { + const auto timestamp = chr::duration_cast(now.time_since_epoch()); + print_prefix(timestamp.count()); + } + } + + if constexpr ((Flags & flag::level_name) != flag::none) { + print_prefix(level_names[level_index]); + } + + if constexpr ((Flags & flag::filename) != flag::none) { + if constexpr ((Flags & flag::line_number) != flag::none) { + print_prefix(' ', location.file_name(), ':', std::dec, location.line()); + } else { + print_prefix(location.file_name()); + } + } else if constexpr ((Flags & flag::line_number) != flag::none) { + print_prefix(std::dec, location.line()); + } + + if constexpr ((Flags & flag::function_name) != flag::none) { + print_prefix(location.function_name()); + } + + if constexpr ((Flags & flag::thread_id) != flag::none) { + print_prefix(std::this_thread::get_id()); + } + + constexpr auto identifier = Identifier.view(); + if constexpr (not identifier.empty()) { + print_prefix(identifier); + } + + static constexpr auto prefix_flags = ~(flag::colors | flag::locking); + if constexpr ((Flags & prefix_flags) != flag::none or not identifier.empty()) { + stream << ' '; + } + + logger_internal::print_format(stream, dark_clr, reset_clr, internal_clr, fmt, std::forward(args)...); + stream << '\n'; + + ctx.lock.clear(); +} +} // namespace logger_internal + +namespace logger { + +context::context(std::ostream& new_out, std::ostream& new_err) : out{ &new_out }, err{ &new_err } { +} + +context& global_context() { + static auto ctx = context(std::cout, std::cerr); + return ctx; +} + +dynamic_config& global_dynamic_config() { + static auto cfg = dynamic_config{ (flag::colors | flag::locking | flag::time | flag::level_name | flag::filename | + flag::line_number), + level::debug, + "" }; + return cfg; +} + +namespace functions { +template +error::error( + logger_internal::format_string fmt, Args&&... args, const std::source_location& location +) { + if constexpr (settings::use_dynamic_global_config) { + println_impl( + global_context(), + global_dynamic_config(), + location, + level::error, + fmt, + std::forward(args)... + ); + } else { + println_impl(global_context(), global_static_config, location, level::error, fmt, std::forward(args)...); + } +} + +template +error::error( + context& ctx, logger_internal::format_string fmt, Args&&... args, const std::source_location& location +) { + if constexpr (settings::use_dynamic_global_config) { + println_impl(ctx, global_dynamic_config(), location, level::error, fmt, std::forward(args)...); + } else { + println_impl(ctx, global_static_config, location, level::error, fmt, std::forward(args)...); + } +} + +template +error::error( + const dynamic_config& cfg, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location +) { + println_impl(global_context(), cfg, location, level::error, fmt, std::forward(args)...); +} + +template +template +error::error( + const static_config& cfg, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location +) { + println_impl(global_context(), cfg, location, level::error, fmt, std::forward(args)...); +} + +template +error::error( + context& ctx, + const dynamic_config& cfg, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location +) { + println_impl(ctx, cfg, location, level::error, fmt, std::forward(args)...); +} + +template +template +error::error( + context& ctx, + const static_config& cfg, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location +) { + println_impl(ctx, cfg, location, level::error, fmt, std::forward(args)...); +} + +template +warn::warn(logger_internal::format_string fmt, Args&&... args, const std::source_location& location) { + if constexpr (settings::use_dynamic_global_config) { + println_impl( + global_context(), + global_dynamic_config(), + location, + level::warn, + fmt, + std::forward(args)... + ); + } else { + println_impl(global_context(), global_static_config, location, level::warn, fmt, std::forward(args)...); + } +} + +template +warn::warn( + context& ctx, logger_internal::format_string fmt, Args&&... args, const std::source_location& location +) { + if constexpr (settings::use_dynamic_global_config) { + println_impl(ctx, global_dynamic_config(), location, level::warn, fmt, std::forward(args)...); + } else { + println_impl(ctx, global_static_config, location, level::warn, fmt, std::forward(args)...); + } +} + +template +warn::warn( + const dynamic_config& cfg, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location +) { + println_impl(global_context(), cfg, location, level::warn, fmt, std::forward(args)...); +} + +template +template +warn::warn( + const static_config& cfg, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location +) { + println_impl(global_context(), cfg, location, level::warn, fmt, std::forward(args)...); +} + +template +warn::warn( + context& ctx, + const dynamic_config& cfg, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location +) { + println_impl(ctx, cfg, location, level::warn, fmt, std::forward(args)...); +} + +template +template +warn::warn( + context& ctx, + const static_config& cfg, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location +) { + println_impl(ctx, cfg, location, level::warn, fmt, std::forward(args)...); +} + +template +info::info(logger_internal::format_string fmt, Args&&... args, const std::source_location& location) { + if constexpr (settings::use_dynamic_global_config) { + println_impl( + global_context(), + global_dynamic_config(), + location, + level::info, + fmt, + std::forward(args)... + ); + } else { + println_impl(global_context(), global_static_config, location, level::info, fmt, std::forward(args)...); + } +} + +template +info::info( + context& ctx, logger_internal::format_string fmt, Args&&... args, const std::source_location& location +) { + if constexpr (settings::use_dynamic_global_config) { + println_impl(ctx, global_dynamic_config(), location, level::info, fmt, std::forward(args)...); + } else { + println_impl(ctx, global_static_config, location, level::info, fmt, std::forward(args)...); + } +} + +template +info::info( + const dynamic_config& cfg, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location +) { + println_impl(global_context(), cfg, location, level::info, fmt, std::forward(args)...); +} + +template +template +info::info( + const static_config& cfg, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location +) { + println_impl(global_context(), cfg, location, level::info, fmt, std::forward(args)...); +} + +template +info::info( + context& ctx, + const dynamic_config& cfg, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location +) { + println_impl(ctx, cfg, location, level::info, fmt, std::forward(args)...); +} + +template +template +info::info( + context& ctx, + const static_config& cfg, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location +) { + println_impl(ctx, cfg, location, level::info, fmt, std::forward(args)...); +} + +template +log::log(logger_internal::format_string fmt, Args&&... args, const std::source_location& location) { + if constexpr (settings::use_dynamic_global_config) { + println_impl(global_context(), global_dynamic_config(), location, level::log, fmt, std::forward(args)...); + } else { + println_impl(global_context(), global_static_config, location, level::log, fmt, std::forward(args)...); + } +} + +template +log::log( + context& ctx, logger_internal::format_string fmt, Args&&... args, const std::source_location& location +) { + if constexpr (settings::use_dynamic_global_config) { + println_impl(ctx, global_dynamic_config(), location, level::log, fmt, std::forward(args)...); + } else { + println_impl(ctx, global_static_config, location, level::log, fmt, std::forward(args)...); + } +} + +template +log::log( + const dynamic_config& cfg, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location +) { + println_impl(global_context(), cfg, location, level::log, fmt, std::forward(args)...); +} + +template +template +log::log( + const static_config& cfg, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location +) { + println_impl(global_context(), cfg, location, level::log, fmt, std::forward(args)...); +} + +template +log::log( + context& ctx, + const dynamic_config& cfg, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location +) { + println_impl(ctx, cfg, location, level::log, fmt, std::forward(args)...); +} + +template +template +log::log( + context& ctx, + const static_config& cfg, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location +) { + println_impl(ctx, cfg, location, level::log, fmt, std::forward(args)...); +} + +template +debug::debug( + logger_internal::format_string fmt, Args&&... args, const std::source_location& location +) { + if constexpr (settings::use_dynamic_global_config) { + println_impl( + global_context(), + global_dynamic_config(), + location, + level::debug, + fmt, + std::forward(args)... + ); + } else { + println_impl(global_context(), global_static_config, location, level::debug, fmt, std::forward(args)...); + } +} + +template +debug::debug( + context& ctx, logger_internal::format_string fmt, Args&&... args, const std::source_location& location +) { + if constexpr (settings::use_dynamic_global_config) { + println_impl(ctx, global_dynamic_config(), location, level::debug, fmt, std::forward(args)...); + } else { + println_impl(ctx, global_static_config, location, level::debug, fmt, std::forward(args)...); + } +} + +template +debug::debug( + const dynamic_config& cfg, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location +) { + println_impl(global_context(), cfg, location, level::debug, fmt, std::forward(args)...); +} + +template +template +debug::debug( + const static_config& cfg, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location +) { + println_impl(global_context(), cfg, location, level::debug, fmt, std::forward(args)...); +} + +template +debug::debug( + context& ctx, + const dynamic_config& cfg, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location +) { + println_impl(ctx, cfg, location, level::debug, fmt, std::forward(args)...); +} + +template +template +debug::debug( + context& ctx, + const static_config& cfg, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location +) { + println_impl(ctx, cfg, location, level::debug, fmt, std::forward(args)...); +} + +template +println::println( + logger_internal::format_string fmt, Args&&... args, const std::source_location& location +) { + if constexpr (settings::use_dynamic_global_config) { + println_impl( + global_context(), + global_dynamic_config(), + location, + level::generic, + fmt, + std::forward(args)... + ); + } else { + println_impl( + global_context(), + global_static_config, + location, + level::generic, + fmt, + std::forward(args)... + ); + } +} + +template +println::println( + context& ctx, logger_internal::format_string fmt, Args&&... args, const std::source_location& location +) { + if constexpr (settings::use_dynamic_global_config) { + println_impl(ctx, global_dynamic_config(), location, level::generic, fmt, std::forward(args)...); + } else { + println_impl(ctx, global_static_config, location, level::generic, fmt, std::forward(args)...); + } +} + +template +println::println( + const dynamic_config& cfg, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location +) { + println_impl(global_context(), cfg, location, level::generic, fmt, std::forward(args)...); +} + +template +template +println::println( + const static_config& cfg, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location +) { + println_impl(global_context(), cfg, location, level::generic, fmt, std::forward(args)...); +} + +template +println::println( + context& ctx, + const dynamic_config& cfg, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location +) { + println_impl(ctx, cfg, location, level::generic, fmt, std::forward(args)...); +} + +template +template +println::println( + context& ctx, + const static_config& cfg, + logger_internal::format_string fmt, + Args&&... args, + const std::source_location& location +) { + println_impl(ctx, cfg, location, level::generic, fmt, std::forward(args)...); +} + +} // namespace functions + +using namespace functions; + +} // namespace logger +} // namespace ztu diff --git a/include/util/result.hpp b/include/util/result.hpp new file mode 100644 index 0000000..f571064 --- /dev/null +++ b/include/util/result.hpp @@ -0,0 +1,139 @@ +#pragma once + +#include +#include +#include + +namespace ztu { + +template +using result = std::expected; + +/* +template +class result { + static constexpr auto value_index = 0; + static constexpr auto error_index = 1; + +public: + constexpr result(T value); + constexpr result(const std::error_code& error); + + template + void emplace(Args&&... args); + + [[nodiscard]] constexpr bool ok() const; + + [[nodiscard]] constexpr std::error_code error() const; + + [[nodiscard]] constexpr const T& value() const; + + [[nodiscard]] constexpr T& value(); + + [[nodiscard]] constexpr T&& value() &&; + + [[nodiscard]] operator bool() const; + + [[nodiscard]] const T& operator*() const; + + [[nodiscard]] T& operator*(); + + [[nodiscard]] T&& operator*() &&; + + [[nodiscard]] bool operator==(const T& rhs) const; + + [[nodiscard]] bool operator==(const std::error_code& rhs) const; + + template + auto map(Func&& func) const -> result + { + if (ok()) return func(value()); + return error(); + } + +private: + std::variant m_data; +}; + +// Member function definitions + +template +constexpr result::result(T value) + : m_data{ std::in_place_index_t{}, std::move(value) } +{} + +template +constexpr result::result(const std::error_code& error) + : m_data{ std::in_place_index_t{}, error } +{} + +template +template +void result::emplace(Args&&... args) { + m_data.template emplace(std::forward(args)...); +} + +template +template +auto result::map(Func&& func) const -> result { + if (ok()) return func(value()); + return error(); +} + +template +constexpr bool result::ok() const { + return m_data.index() == value_index; +} + +template +constexpr std::error_code result::error() const { + auto error_ptr = std::get_if(m_data); + return error_ptr ? *error_ptr : std::error_code{}; +} + +template +constexpr const T& result::value() const { + return std::get(m_data); +} + +template +constexpr T& result::value() { + return std::get(m_data); +} + +template +constexpr T&& result::value() && { + return std::get(std::move(m_data)); +} + +template +constexpr result::operator bool() const { + return ok(); +} + +template +const T& result::operator*() const { + return *std::get_if(m_data); +} + +template +T& result::operator*() { + return *std::get_if(m_data); +} + +template +T&& result::operator*() && { + return *std::get_if(std::move(m_data)); +} + +template +bool result::operator==(const T& rhs) const { + return ok() and value() == rhs; +} + +template +bool result::operator==(const std::error_code& rhs) const { + return not ok() and error() == rhs; +}*/ + +} diff --git a/include/util/specialised_lambda.hpp b/include/util/specialised_lambda.hpp new file mode 100644 index 0000000..6fa1b78 --- /dev/null +++ b/include/util/specialised_lambda.hpp @@ -0,0 +1,10 @@ +#pragma once + +namespace ztu +{ +template +struct specialised_lambda : Lambdas... { using Lambdas::operator()...; }; + +template +specialised_lambda(Lambdas...) -> specialised_lambda; +} diff --git a/include/util/string_indexer.hpp b/include/util/string_indexer.hpp new file mode 100755 index 0000000..5bd63af --- /dev/null +++ b/include/util/string_indexer.hpp @@ -0,0 +1,126 @@ +#pragma once + +#include +#include +#include +#include +#include +#include "util/uix.hpp" +#include + +template +class string_indexer { +private: + struct index_type { + unsigned int hash; + ztu::usize index; + + [[nodiscard]] inline constexpr auto operator<=>(const index_type &other) const { + return hash <=> other.hash; + } + + [[nodiscard]] inline constexpr auto operator==(const index_type &other) const { + return hash == other.hash; + } + + [[nodiscard]] inline constexpr auto operator<=>(const unsigned other_hash) const { + return hash <=> other_hash; + } + }; + + [[nodiscard]] inline constexpr static unsigned hash(std::span str); + +public: + template + requires (sizeof...(Ts) == NumKeys) + consteval explicit string_indexer(const Ts&... keys) noexcept; + + [[nodiscard]] inline constexpr std::optional index_of(std::span str) const; + + [[nodiscard]] inline constexpr std::optional name_of(ztu::usize index) const; + + [[nodiscard]] inline constexpr std::span keys() const; + +private: + std::array m_lookup{}; + std::array m_keys{}; +}; + +template +[[nodiscard]] inline constexpr unsigned string_indexer::hash(std::span str) { + + unsigned prime = 0x1000193; + unsigned hashed = 0x811c9dc5; + + for (const auto &c : str) { + hashed = hashed ^ c; + hashed *= prime; + } + + return hashed; +} + +template +template requires (sizeof...(Ts) == NumKeys) +consteval string_indexer::string_indexer(const Ts&... keys) noexcept { + + ztu::for_each::indexed_argument([&](const auto &key) { + // Since std::string_view does only truncate the '\0' of strings in the 'const char*' constructor + // and does not deem otherwise equal views of truncated and untruncated strings equal, + // all strings need to be truncated before constructing the view. + const auto begin = std::begin(key), end = std::end(key); + m_keys[Index] = { begin, std::find(begin, end, '\0') }; + m_lookup[Index] = { hash(m_keys[Index] ), Index }; + return false; + }, keys...); + + std::sort(m_lookup.begin(), m_lookup.end()); + + auto it = m_lookup.begin(); + while ((it = std::adjacent_find(it, m_lookup.end())) != m_lookup.end()) { + const auto match = it->hash; + for (auto it_a = it + 1; it_a != m_lookup.end() && it_a->hash == match; it_a++) { + const auto &key_a = m_keys[it_a->index]; + for (auto it_b = it; it_b != it_a; it_b++) { + const auto &key_b = m_keys[it_b->index]; + if (key_a == key_b) { + throw std::logic_error("Duplicate keys"); + } + } + } + } +} + +template +[[nodiscard]] inline constexpr std::optional string_indexer::index_of(std::span str) const { + const auto sv = std::string_view(str.begin(), std::find(str.begin(), str.end(), '\0')); + + const auto hashed = hash(sv); + const auto it = std::lower_bound(m_lookup.begin(), m_lookup.end(), hashed); + + if (it == m_lookup.end() or hashed != it->hash) + return std::nullopt; + + do [[unlikely]] { + const auto candidate_index = it->index; + if (m_keys[candidate_index] == sv) [[likely]] { + return candidate_index; + } + } while (it < m_lookup.end() && it->hash == hashed); + + return std::nullopt; +} + +template +[[nodiscard]] inline constexpr std::optional string_indexer::name_of(ztu::usize index) const { + if (index < NumKeys) { + return m_keys[index]; + } else { + return std::nullopt; + } +} + +template +[[nodiscard]] inline constexpr std::span string_indexer::keys() const { + return { m_keys }; +} diff --git a/include/util/string_list.hpp b/include/util/string_list.hpp new file mode 100644 index 0000000..6ba8808 --- /dev/null +++ b/include/util/string_list.hpp @@ -0,0 +1,259 @@ +#pragma once + +#include +#include +#include +#include + +namespace ztu +{ + +class string_list_iterator +{ +public: + using iterator_category = std::random_access_iterator_tag; + using value_type = std::string_view; + using difference_type = std::ptrdiff_t; + using pointer = const value_type*; + using reference = const value_type&; + + string_list_iterator( + std::string_view buffer, + std::span lengths, + std::size_t index, + std::size_t char_offset) + : m_buffer{ buffer }, m_lengths{ lengths }, m_index{ index }, m_char_offset{ char_offset } {} + + value_type operator*() const + { + return m_buffer.substr(m_char_offset, m_lengths[m_index]); + } + + string_list_iterator& operator++() + { + ++m_index; + m_char_offset += m_lengths[m_index]; + return *this; + } + + string_list_iterator operator++(int) + { + auto tmp = *this; + ++(*this); + return tmp; + } + + string_list_iterator& operator--() + { + --m_index; + m_char_offset -= m_lengths[m_index]; + return *this; + } + + string_list_iterator operator--(int) + { + auto tmp = *this; + --(*this); + return tmp; + } + + string_list_iterator& operator+=(difference_type n) + { + const auto negative = n < difference_type{ 0 }; + const auto positive = n > difference_type{ 0 }; + const auto step = difference_type{ positive } - difference_type{ negative }; + n = negative ? -n : n; + + while (n--) + { + m_char_offset += step * m_lengths[m_index]; + m_index += step; + } + return *this; + } + + string_list_iterator& operator-=(const difference_type n) + { + return *this += -n; + } + + string_list_iterator operator+(const difference_type n) const + { + auto tmp = *this; + return tmp += n; + } + + string_list_iterator operator-(const difference_type n) const + { + auto tmp = *this; + return tmp -= n; + } + + difference_type operator-(const string_list_iterator& other) const + { + return static_cast(m_index) - static_cast(other.m_index); + } + + value_type operator[](const difference_type n) const + { + auto tmp = *this; + tmp += n; + return *tmp; + } + + bool operator==(const string_list_iterator& other) const { + return m_index == other.m_index && m_char_offset == other.m_char_offset; + } + + bool operator!=(const string_list_iterator& other) const { + return !(*this == other); + } + + bool operator<(const string_list_iterator& other) const { + return m_index < other.m_index; + } + + bool operator<=(const string_list_iterator& other) const { + return m_index <= other.m_index; + } + + bool operator>(const string_list_iterator& other) const { + return m_index > other.m_index; + } + + bool operator>=(const string_list_iterator& other) const { + return m_index >= other.m_index; + } + +private: + std::string_view m_buffer; + std::span m_lengths; + std::size_t m_index; + std::size_t m_char_offset; +}; + +class string_list +{ + using index_type = std::uint32_t; + using iterator = string_list_iterator; + +public: + string_list() = default; + + string_list(std::initializer_list init) + { + for (const auto& str : init) { + push_back(str); + } + } + + void reserve(const std::size_t characters, const std::size_t lengths) + { + m_buffer.reserve(characters); + m_lengths.reserve(lengths); + } + + std::size_t character_count() const + { + return m_buffer.size(); + } + + void push_back(const std::string_view& str) + { + m_buffer.reserve(m_buffer.size() + str.length() + 1); + m_buffer.insert(m_buffer.end(), str.begin(), str.end()); + m_buffer.push_back('\0'); + m_lengths.push_back(static_cast(str.size())); + } + + void push_back(const string_list& list) + { + m_buffer.insert(m_buffer.end(), list.m_buffer.begin(), list.m_buffer.end()); + m_lengths.insert(m_lengths.end(), list.m_lengths.begin(), list.m_lengths.end()); + } + + void pop_back() + { + m_buffer.resize(m_buffer.size() - m_lengths.back()); + m_lengths.pop_back(); + } + + [[nodiscard]] std::string_view operator[](index_type index) const + { + auto offset = std::size_t{}; + for (index_type i{}; i != index; ++i) + { + offset += m_lengths[i]; + } + + return { &m_buffer[offset], m_lengths[index] }; + } + + [[nodiscard]] index_type size() const + { + return m_lengths.size(); + } + + [[nodiscard]] bool empty() const + { + return m_lengths.empty(); + } + + void clear() + { + m_buffer.clear(); + m_lengths.clear(); + } + + void reserve(index_type new_cap) + { + m_buffer.reserve(new_cap); + m_lengths.reserve(new_cap); + } + + [[nodiscard]] index_type capacity() const + { + return m_buffer.capacity(); + } + + [[nodiscard]] iterator begin() const + { + return iterator{ + std::string_view{ m_buffer.data(), m_buffer.size() }, + m_lengths, + 0, 0 + }; + } + + [[nodiscard]] iterator end() const + { + return iterator{ + std::string_view{ m_buffer.data(), m_buffer.size() }, + m_lengths, + m_lengths.size(), + m_buffer.size() + }; + } + + [[nodiscard]] const std::vector& buffer() const + { + return m_buffer; + } + + [[nodiscard]] const std::vector& string_lengths() const + { + return m_lengths; + } + + void swap(string_list& other) noexcept + { + m_buffer.swap(other.m_buffer); + m_lengths.swap(other.m_lengths); + } + +private: + std::vector m_buffer{}; + std::vector m_lengths{}; +}; + +} \ No newline at end of file diff --git a/include/util/string_literal.hpp b/include/util/string_literal.hpp new file mode 100644 index 0000000..df896a9 --- /dev/null +++ b/include/util/string_literal.hpp @@ -0,0 +1,734 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ztu { + +#define ztu_ic inline constexpr +#define ztu_nic [[nodiscard]] inline constexpr + +#ifdef __cpp_exceptions +#define ZTU_ASSERT(exp, error) \ + if (not (exp)) [[unlikely]] throw (error); +#else +#include +#include +#define ZTU_TO_STRING_IMPL(x) #x +#define ZTU_TO_STRING(x) ZTU_TO_STRING_IMPL(x) +#define ZTU_ASSERT(exp, error) \ + if (not (exp)) [[unlikely]] { \ + puts(__FILE__ ":" ZTU_TO_STRING(__LINE__) ": Assertion '" ZTU_TO_STRING(exp) "' failed."); \ + abort(); \ + } +#endif + + +using sl_ssize_t = std::make_signed_t; + +template + requires (N > 0) +struct string_literal { + //--------------[ typedefs ]--------------// + + static constexpr auto max_size = N - 1; + using this_type = string_literal; + using array_type = std::array; + using value_type = array_type::value_type; + using size_type = sl_ssize_t; + using difference_type = array_type::difference_type; + using reference = array_type::reference; + using const_reference = array_type::const_reference; + using pointer = array_type::pointer; + using const_pointer = array_type::const_pointer; + using iterator = array_type::iterator; + using const_iterator = array_type::const_iterator; + using reverse_iterator = array_type::reverse_iterator; + using const_reverse_iterator = array_type::const_reverse_iterator; + + + //--------------[ constructors ]--------------// + + ztu_ic string_literal(); + + ztu_ic string_literal(const char (&str)[N]); + + template + requires (M < N) + ztu_ic string_literal(const char (&str)[M]); + + template + requires ( + (1 < sizeof...(Chars) and sizeof...(Chars) < N) and + (std::same_as and ...) + ) + ztu_ic string_literal(Chars... chars); + + ztu_ic string_literal(char c, ssize_t count = 1); + + ztu_ic string_literal(const char* str, std::size_t len); + + ztu_ic string_literal(const std::string &str); + + ztu_ic string_literal(const std::string_view &str); + + + //--------------[ array interface ]--------------// + + ztu_nic reference at(size_type index); + ztu_nic const_reference at(size_type index) const; + + ztu_nic reference operator[](size_type index); + ztu_nic const_reference operator[](size_type index) const; + + ztu_nic reference front(); + ztu_nic const_reference front() const; + + ztu_nic reference back(); + ztu_nic const_reference back() const; + + ztu_nic pointer data() noexcept; + ztu_nic const_pointer data() const noexcept; + + ztu_nic iterator begin() noexcept; + ztu_nic const_iterator begin() const noexcept; + ztu_nic const_iterator cbegin() const noexcept; + + ztu_nic iterator end() noexcept; + ztu_nic const_iterator end() const noexcept; + ztu_nic const_iterator cend() const noexcept; + + ztu_nic reverse_iterator rbegin() noexcept; + ztu_nic const_reverse_iterator rbegin() const noexcept; + ztu_nic const_reverse_iterator rcbegin() const noexcept; + + ztu_nic reverse_iterator rend() noexcept; + ztu_nic const_reverse_iterator rend() const noexcept; + ztu_nic const_reverse_iterator rcend() const noexcept; + + ztu_nic bool empty() const noexcept; + ztu_nic size_type size() const noexcept; + ztu_nic size_type unused_size() const noexcept; + + + //--------------[ string interface ]--------------// + + static constexpr auto max_length = max_size; + + ztu_nic pointer c_str() noexcept; + ztu_nic const_pointer c_str() const noexcept; + + ztu_nic std::string_view view() const; + + ztu_nic size_type length() const noexcept; + + ztu_nic size_type find(char c, ssize_t pos = 0) const; + ztu_nic size_type find(const char *str, size_type len, ssize_t pos) const; + ztu_nic size_type find(const char *str, ssize_t pos = 0) const; + ztu_nic size_type find(const std::string_view &str, ssize_t pos = 0) const; + ztu_nic size_type find(const std::string &str, ssize_t pos = 0) const; + template + ztu_ic size_type find(const string_literal &str) const; + + ztu_ic this_type& resize(size_type new_size, char fill = ' '); + + ztu_ic this_type& erase(const_iterator begin_it, const_iterator end_it); + ztu_ic this_type& erase(size_type index, size_type count = 1); + + ztu_ic this_type& insert(size_type index, char c, size_type repeat = 1); + ztu_ic this_type& insert(size_type index, const char *str); + ztu_ic this_type& insert(size_type index, const char *str, size_type len); + ztu_ic this_type& insert(size_type index, const std::string_view &str); + ztu_ic this_type& insert(size_type index, const std::string &str); + template + ztu_ic this_type& insert(size_type index, const string_literal &str); + + ztu_ic this_type& replace(size_type index, size_type count, char c, size_type repeat = 1); + ztu_ic this_type& replace(size_type index, size_type count, const char *str); + ztu_ic this_type& replace(size_type index, size_type count, const char *str, size_type len); + ztu_ic this_type& replace(size_type index, size_type count, const std::string_view &str); + ztu_ic this_type& replace(size_type index, size_type count, const std::string &str); + template + ztu_ic this_type& replace(size_type index, size_type count, const string_literal &str); + + //--------------[ operators ]--------------// + + ztu_nic bool operator==(const std::string &str) const; + ztu_nic bool operator==(const std::string_view &str) const; + ztu_nic bool operator==(const char* str) const; + template + ztu_nic bool operator==(const string_literal &str) const; + + + template + ztu_ic void assign(const char* str, size_type len); + + ztu_ic this_type& operator=(const std::string &str); + ztu_ic this_type& operator=(const std::string_view &str); + ztu_ic this_type& operator=(const char* str); + template + ztu_ic this_type& operator=(const string_literal &str); + + + ztu_ic void append(const char* str, size_type len); + + ztu_ic this_type& operator+=(const std::string &str); + ztu_ic this_type& operator+=(const std::string_view &str); + ztu_ic this_type& operator+=(const char* str); + template + ztu_ic this_type& operator+=(const string_literal &str); + + + template + ztu_nic string_literal operator+(const string_literal &str) const; + + template + inline friend std::ostream& operator<<(std::ostream &out, const string_literal& str); + + array_type m_value{}; +}; + +//--------------[ predefines ]--------------// + +namespace detail { + template::max()> + ztu_nic sl_ssize_t strlen(const char* str) { + sl_ssize_t len = 0; + while (str[len] != '\0') { + len++; + ZTU_ASSERT(len < MaxLength, std::invalid_argument("given string is not null terminated")); + } + return len; + } +} + +template requires (N > 0) +ztu_nic string_literal::size_type string_literal::size() const noexcept { + return detail::strlen(m_value.data()); +} + +template requires (N > 0) +ztu_nic string_literal::size_type string_literal::length() const noexcept { + return this->size(); +} + +template requires (N > 0) +ztu_nic string_literal::size_type string_literal::unused_size() const noexcept { + return max_size - this->size(); +} + + +//--------------[ constructors ]--------------// + +template requires (N > 0) +ztu_ic string_literal::string_literal() = default; + +template requires (N > 0) +ztu_ic string_literal::string_literal(const char (&str)[N]) { + std::copy_n(std::begin(str), N, m_value.begin()); + m_value[N - 1] = '\0'; +} + +template requires (N > 0) +template requires (M < N) +ztu_ic string_literal::string_literal(const char (&str)[M]) { + std::copy_n(std::begin(str), M, m_value.begin()); + m_value[M - 1] = '\0'; +} + +template requires (N > 0) +template + requires ( + (1 < sizeof...(Chars) and sizeof...(Chars) < N) and + (std::same_as and ...) + ) +ztu_ic string_literal::string_literal(Chars... chars) + : m_value{ + chars... // default initialization of array elements to 0 terminates string + } {} + +template requires (N > 0) +ztu_ic string_literal::string_literal(const char c, ssize_t count) + : m_value{} +{ + ZTU_ASSERT(count <= max_size, std::length_error("Given count exceeds capacity.")); + std::fill_n(m_value.begin(), count, c); +} + + +template requires (N > 0) +ztu_ic string_literal::string_literal(const char* data, std::size_t size) { + ZTU_ASSERT(size <= max_size, std::length_error("given string exceeds capacity")); + std::copy_n(data, size, m_value.begin()); + m_value[size] = '\0'; +} + +template requires (N > 0) +ztu_ic string_literal::string_literal(const std::string &str) + : string_literal(str.data(), str.size()) {} + +template requires (N > 0) +ztu_ic string_literal::string_literal(const std::string_view &str) + : string_literal(str.data(), str.size()) {} + +//--------------[ array interface ]--------------// + +template requires (N > 0) +ztu_nic string_literal::reference string_literal::at(size_type index) { + const auto m_length = this->length(); + ZTU_ASSERT(0 <= index and index < m_length, std::out_of_range("given index exceeds length")); + return m_value[index]; +} + +template requires (N > 0) +ztu_nic string_literal::const_reference string_literal::at(size_type index) const { + const auto m_length = this->length(); + ZTU_ASSERT(0 <= index and index < m_length, std::out_of_range("given index exceeds length")); + return m_value[index]; +} + +template requires (N > 0) +ztu_nic string_literal::reference string_literal::operator[](size_type index) { + return m_value[index]; +} + +template requires (N > 0) +ztu_nic string_literal::const_reference string_literal::operator[](size_type index) const { + return m_value[index]; +} + +template requires (N > 0) +ztu_nic string_literal::reference string_literal::front() { + return m_value.front(); +} + +template requires (N > 0) +ztu_nic string_literal::const_reference string_literal::front() const { + return m_value.front(); +} + +template requires (N > 0) +ztu_nic string_literal::reference string_literal::back() { + return m_value[this->size() - 1]; +} + +template requires (N > 0) +ztu_nic string_literal::const_reference string_literal::back() const { + return m_value[this->size() - 1]; +} + +template requires (N > 0) +ztu_nic string_literal::pointer string_literal::data() noexcept { + return m_value.data(); +} + +template requires (N > 0) +ztu_nic string_literal::const_pointer string_literal::data() const noexcept { + return m_value.data(); +} + +template requires (N > 0) +ztu_nic string_literal::iterator string_literal::begin() noexcept { + return m_value.begin(); +} + +template requires (N > 0) +ztu_nic string_literal::const_iterator string_literal::begin() const noexcept { + return m_value.begin(); +} + +template requires (N > 0) +ztu_nic string_literal::const_iterator string_literal::cbegin() const noexcept { + return m_value.cbegin(); +} + +template requires (N > 0) +ztu_nic string_literal::iterator string_literal::end() noexcept { + return this->begin() + this->size(); +} + +template requires (N > 0) +ztu_nic string_literal::const_iterator string_literal::end() const noexcept { + return this->begin() + this->size(); +} + +template requires (N > 0) +ztu_nic string_literal::const_iterator string_literal::cend() const noexcept { + return this->begin() + this->size(); +} + +template requires (N > 0) +ztu_nic string_literal::reverse_iterator string_literal::rbegin() noexcept { + return m_value.rbegin() + this->unused_size(); +} + +template requires (N > 0) +ztu_nic string_literal::const_reverse_iterator string_literal::rbegin() const noexcept { + return m_value.rbegin() + this->unused_size(); +} + +template requires (N > 0) +ztu_nic string_literal::const_reverse_iterator string_literal::rcbegin() const noexcept { + return m_value.rcbegin() + this->unused_size(); +} + +template requires (N > 0) +ztu_nic string_literal::reverse_iterator string_literal::rend() noexcept { + return m_value.rend(); +} + +template requires (N > 0) +ztu_nic string_literal::const_reverse_iterator string_literal::rend() const noexcept { + return m_value.rend(); +} + +template requires (N > 0) +ztu_nic string_literal::const_reverse_iterator string_literal::rcend() const noexcept { + return m_value.crend(); +} + +template requires (N > 0) +ztu_nic bool string_literal::empty() const noexcept { + return this->front() == '\0'; +} + + +//--------------[ string interface ]--------------// + +template requires (N > 0) +ztu_nic string_literal::pointer string_literal::c_str() noexcept { + return m_value.data(); +} + +template requires (N > 0) +ztu_nic string_literal::const_pointer string_literal::c_str() const noexcept { + return m_value.data(); +} + +template requires (N > 0) +ztu_nic std::string_view string_literal::view() const { + return { this->c_str() }; +} + +template requires (N > 0) +ztu_nic string_literal::size_type string_literal::find(char c, ssize_t pos) const { + const auto m_length = this->length(); + ZTU_ASSERT(0 <= pos and pos <= m_length, std::out_of_range("Given start pos is out of range.")); + return std::find(this->begin() + pos, this->begin() + m_length, c) - this->begin(); +} + +template requires (N > 0) +ztu_nic string_literal::size_type string_literal::find(const char *str, size_type len, ssize_t pos) const { + const auto m_length = this->length(); + ZTU_ASSERT(0 <= pos and pos <= m_length, std::out_of_range("Given start pos is out of range.")); + return std::search(this->begin() + pos, this->begin() + m_length, str, str + len) - this->begin(); +} + +template requires (N > 0) +ztu_nic string_literal::size_type string_literal::find(const char *str, ssize_t pos) const { + return this->find(str, detail::strlen(str), pos); +} + +template requires (N > 0) +ztu_nic string_literal::size_type string_literal::find(const std::string_view &str, ssize_t pos) const { + return this->find(str.data(), str.length(), pos); +} + +template requires (N > 0) +ztu_nic string_literal::size_type string_literal::find(const std::string &str, ssize_t pos) const { + return this->find(str.data(), str.length(), pos); +} + +template requires (N > 0) +template +ztu_ic string_literal::size_type string_literal::find(const string_literal &str) const { + return this->find(str.c_str(), str.length()); +} + +template requires (N > 0) +ztu_ic string_literal::this_type& string_literal::resize(size_type new_size, char fill) { + ZTU_ASSERT(0 <= new_size and new_size <= max_size, std::length_error("New size exceeds capacity.")); + m_value[new_size] = '\0'; + if (const auto m_size = this->size(); new_size > m_size) + std::fill_n(this->begin() + m_size, new_size - m_size, fill); + return *this; +} + +template requires (N > 0) +ztu_ic string_literal::this_type& string_literal::erase(const_iterator begin_it, const_iterator end_it) { + const auto m_size = this->size(); + const auto m_end = this->begin() + m_size; + ZTU_ASSERT(this->begin() <= begin_it and begin_it <= m_end, std::out_of_range("begin iterator out of range")); + ZTU_ASSERT(this->begin() <= end_it and end_it <= m_end, std::out_of_range("end iterator out of range")); + auto mutable_begin = this->begin() + (begin_it - this->cbegin()); + auto mutable_end = this->begin() + (end_it - this->cbegin()); + const auto right_begin = mutable_end; + const auto right_end = this->begin() + m_size + 1; + std::copy(right_begin, right_end, mutable_begin); + return *this; +} + +template requires (N > 0) +ztu_ic string_literal::this_type& string_literal::erase(size_type index, size_type count) { + const auto begin_it = this->begin() + index; + return this->erase(begin_it, begin_it + count); +} + +template requires (N > 0) +ztu_ic string_literal::this_type& string_literal::insert(size_type index, char c, size_type count) { + const auto m_size = this->size(); + ZTU_ASSERT(0 <= index and index <= m_size, std::out_of_range("given index is out of range")); + ZTU_ASSERT(0 <= count and count <= (max_size - m_size), std::length_error("given sequence exceeds capacity")); + // move right of index with terminator + const auto right_begin = this->begin() + index; + const auto right_end = this->begin() + m_size + 1; + std::copy_backward(right_begin, right_end, right_end + count); + std::fill_n(this->begin() + index, count, c); + return *this; +} + +template requires (N > 0) +ztu_ic string_literal::this_type& string_literal::insert(size_type index, const char *str, size_type count) { + const auto m_size = this->size(); + ZTU_ASSERT(0 <= index and index <= m_size, std::out_of_range("given index is out of range")); + ZTU_ASSERT(0 <= count and count <= (max_size - m_size), std::length_error("given sequence exceeds capacity")); + // move right of index with terminator + const auto right_begin = this->begin() + index; + const auto right_end = this->begin() + m_size + 1; + std::copy_backward(right_begin, right_end, right_end + count); + std::copy_n(str, count, this->begin() + index); + return *this; +} + +template requires (N > 0) +ztu_ic string_literal::this_type& string_literal::insert(size_type index, const char *str) { + return this->insert(index, str, detail::strlen(str)); +} + +template requires (N > 0) +ztu_ic string_literal::this_type& string_literal::insert(size_type index, const std::string_view &str) { + return this->insert(index, str.data(), str.length()); +} + +template requires (N > 0) +ztu_ic string_literal::this_type& string_literal::insert(size_type index, const std::string &str) { + return this->insert(index, str.data(), str.length()); +} + +template requires (N > 0) +template +ztu_ic string_literal::this_type& string_literal::insert(size_type index, const string_literal &str) { + return this->insert(index, str.data(), str.length()); +} + + +template requires (N > 0) +ztu_ic string_literal::this_type& string_literal::replace(size_type index, size_type count, char c, size_type repeat) { + const auto m_size = this->size(); + ZTU_ASSERT(0 <= index and index + count <= m_size, std::out_of_range("given index is out of range")); + ZTU_ASSERT(0 <= count and 0 <= repeat, std::out_of_range("count and repeat must be none negative")); + const auto delta_size = repeat - count; + ZTU_ASSERT((m_size + delta_size) <= max_size, std::length_error("given sequence exceeds capacity")); + + const auto right_begin = this->begin() + index + count; + const auto right_end = this->begin() + m_size + 1; + + if (delta_size < 0) { + std::copy(right_begin, right_end, right_begin + delta_size); + } else if (delta_size > 0) { + std::copy_backward(right_begin, right_end, right_end + delta_size); + } + std::fill_n(this->begin() + index, repeat, c); + + return *this; +} + +template requires (N > 0) +ztu_ic string_literal::this_type& string_literal::replace(size_type index, size_type count, const char *str, size_type len) { + const auto m_size = this->size(); + ZTU_ASSERT(0 <= index and index + count <= m_size, std::out_of_range("given index is out of range")); + ZTU_ASSERT(0 <= count and 0 <= len, std::out_of_range("count and len must be none negative")); + const auto delta_size = len - count; + ZTU_ASSERT((m_size + delta_size) <= max_size, std::length_error("given sequence exceeds capacity")); + + const auto right_begin = this->begin() + index + count; + const auto right_end = this->begin() + m_size + 1; + + if (delta_size < 0) { + std::copy(right_begin, right_end, right_begin + delta_size); + } else if (delta_size > 0) { + std::copy_backward(right_begin, right_end, right_end + delta_size); + } + std::copy_n(str, len, this->begin() + index); + + return *this; +} + +template requires (N > 0) +ztu_ic string_literal::this_type& string_literal::replace(size_type index, size_type count, const char *str) { + return this->replace(index, count, str, detail::strlen(str)); +} + +template requires (N > 0) +ztu_ic string_literal::this_type& string_literal::replace(size_type index, size_type count, const std::string_view &str) { + return this->replace(index, count, str.data(), str.length()); +} + +template requires (N > 0) +ztu_ic string_literal::this_type& string_literal::replace(size_type index, size_type count, const std::string &str) { + return this->replace(index, count, str.data(), str.length()); +} + +template requires (N > 0) +template +ztu_ic string_literal::this_type& string_literal::replace(size_type index, size_type count, const string_literal &str) { + return this->replace(index, count, str.data(), str.length()); +} + + +//--------------[ operators ]--------------// + +template requires (N > 0) +ztu_nic bool string_literal::operator==(const std::string &str) const { + const auto o_length = static_cast(str.length()); + for (size_type i = 0; i < o_length; i++) { + if (m_value[i] == '\0' or m_value[i] != str[i]) { + return false; + } + } + return true; +} + +template requires (N > 0) +ztu_nic bool string_literal::operator==(const std::string_view &str) const { + const auto o_length = static_cast(str.length()); + for (size_type i = 0; i < o_length; i++) { + if (m_value[i] == '\0' or m_value[i] != str[i]) { + return false; + } + } + return true; +} + +template requires (N > 0) +ztu_nic bool string_literal::operator==(const char* str) const { + size_type i = 0; + do { + if (i == max_size) [[unlikely]] + return true; + if (m_value[i] != str[i]) + return false; + } while (m_value[i++] != '\0'); + return true; +} + +template requires (N > 0) +template +ztu_nic bool string_literal::operator==(const string_literal &str) const { + return (*this) == str.c_str(); +} + +template requires (N > 0) +template +ztu_ic void string_literal::assign(const char* str, size_type len) { + if constexpr (not KnownToFit) { + ZTU_ASSERT(len <= max_size, std::length_error("given string exceeds capacity")); + } + std::copy_n(str, len, m_value.begin()); + m_value[len] = '\0'; +} + +template requires (N > 0) +ztu_ic string_literal::this_type& string_literal::operator=(const std::string &str) { + assign(str.data(), str.length()); + return *this; +} + +template requires (N > 0) +ztu_ic string_literal::this_type& string_literal::operator=(const std::string_view &str) { + assign(str.data(), str.length()); + return *this; +} + +template requires (N > 0) +ztu_ic string_literal::this_type& string_literal::operator=(const char* str) { + assign(str, detail::strlen(str)); + return *this; +} + +template requires (N > 0) +template +ztu_ic string_literal::this_type& string_literal::operator=(const string_literal &str) { + assign(str.data(), str.length()); + return *this; +} + +template requires (N > 0) +ztu_ic void string_literal::append(const char* str, size_type len) { + const auto m_length = this->length(); + ZTU_ASSERT(len <= (max_size - m_length), std::length_error("given string exceeds available capacity")); + std::copy_n(str, len, this->begin() + m_length); + m_value[m_length + len] = '\0'; +} + +template requires (N > 0) +ztu_ic string_literal::this_type& string_literal::operator+=(const std::string &str) { + append(str.data(), str.length()); + return *this; +} + +template requires (N > 0) +ztu_ic string_literal::this_type& string_literal::operator+=(const std::string_view &str) { + append(str.data(), str.length()); + return *this; +} + +template requires (N > 0) +ztu_ic string_literal::this_type& string_literal::operator+=(const char* str) { + append(str, detail::strlen(str)); + return *this; +} + +template requires (N > 0) +template +ztu_ic string_literal::this_type& string_literal::operator+=(const string_literal &str) { + append(str.data(), str.length()); + return *this; +} + +template requires (N > 0) +template +ztu_nic string_literal string_literal::operator+(const string_literal &str) const { + string_literal combined{}; + + const auto m_length = this->length(); + const auto o_length = str.length(); + + std::copy_n(this->begin(), m_length, combined.begin()); + std::copy_n(str.begin(), o_length + 1, combined.begin() + m_length); // copy termination + + return combined; +} + +template +inline std::ostream& operator<<(std::ostream &out, const string_literal& str) { + return out << str.c_str(); +} + +namespace string_literals { +template +constexpr auto operator"" _sl() { + return Str; +} +} // string_literals + +#undef ztu_ic +#undef ztu_nic + +} // ztu diff --git a/include/util/string_lookup.hpp b/include/util/string_lookup.hpp new file mode 100644 index 0000000..2064e0a --- /dev/null +++ b/include/util/string_lookup.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include +#include +#include + +namespace ztu +{ + +namespace detail +{ +struct string_hash +{ + using is_transparent = void; + using hash_type = std::hash; + + [[nodiscard]] std::size_t operator()(const char *txt) const + { + return hash_type{}(txt); + } + [[nodiscard]] std::size_t operator()(std::string_view txt) const + { + return hash_type{}(txt); + } + [[nodiscard]] std::size_t operator()(const std::string &txt) const + { + return hash_type{}(txt); + } +}; +} + +template +using string_lookup = std::unordered_map>; + +} diff --git a/include/util/uix.hpp b/include/util/uix.hpp new file mode 100755 index 0000000..eb337dc --- /dev/null +++ b/include/util/uix.hpp @@ -0,0 +1,90 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + + +namespace ztu { + +namespace uix { + +using u8 = std::uint8_t; +using u16 = std::uint16_t; +using u32 = std::uint32_t; +using u64 = std::uint64_t; +using usize = std::size_t; + +using i8 = std::int8_t; +using i16 = std::int16_t; +using i32 = std::int32_t; +using i64 = std::int64_t; +using isize = ssize_t; + +[[maybe_unused]] static constexpr auto u8_max = std::numeric_limits::max(); +[[maybe_unused]] static constexpr auto u16_max = std::numeric_limits::max(); +[[maybe_unused]] static constexpr auto u32_max = std::numeric_limits::max(); +[[maybe_unused]] static constexpr auto u64_max = std::numeric_limits::max(); +[[maybe_unused]] static constexpr auto usize_max = std::numeric_limits::max(); + +[[maybe_unused]] static constexpr auto i8_max = std::numeric_limits::max(); +[[maybe_unused]] static constexpr auto i16_max = std::numeric_limits::max(); +[[maybe_unused]] static constexpr auto i32_max = std::numeric_limits::max(); +[[maybe_unused]] static constexpr auto i64_max = std::numeric_limits::max(); +[[maybe_unused]] static constexpr auto isize_max = std::numeric_limits::max(); + +[[maybe_unused]] static constexpr auto u8_min = std::numeric_limits::min(); +[[maybe_unused]] static constexpr auto u16_min = std::numeric_limits::min(); +[[maybe_unused]] static constexpr auto u32_min = std::numeric_limits::min(); +[[maybe_unused]] static constexpr auto u64_min = std::numeric_limits::min(); +[[maybe_unused]] static constexpr auto usize_min = std::numeric_limits::min(); + +[[maybe_unused]] static constexpr auto i8_min = std::numeric_limits::min(); +[[maybe_unused]] static constexpr auto i16_min = std::numeric_limits::min(); +[[maybe_unused]] static constexpr auto i32_min = std::numeric_limits::min(); +[[maybe_unused]] static constexpr auto i64_min = std::numeric_limits::min(); +[[maybe_unused]] static constexpr auto isize_min = std::numeric_limits::min(); + +} // namespace ztu + +using namespace uix; + +namespace detail { +template +struct pack_at {}; + +template requires (Index > 0) +struct pack_at { + using type = pack_at::type; +}; + +template +struct pack_at<0, T, Ts...> { + using type = T; +}; +} + + +template +using uint_t = detail::pack_at::type; + +template requires (N > 0) +using uint_holding = uint_t<(std::bit_width(static_cast(N)) + 7) / 8>; + + +template +using int_t = detail::pack_at::type; + +template requires (N != 0) +using int_holding = int_t< + std::bit_width( + N < 0 ? + std::max(static_cast(-(N + 1)), static_cast(1)) : + static_cast(N) + ) / 8 + 1 +>; + +} // namespace ztu diff --git a/include/util/unroll_bool_template.hpp b/include/util/unroll_bool_template.hpp new file mode 100644 index 0000000..9d780d5 --- /dev/null +++ b/include/util/unroll_bool_template.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include + +template +auto unroll_bool_function_template(F&& f) +{ + return f.template operator()(); +} + +template +auto unroll_bool_function_template(F&& f, const Bool b, const Bools... bs) +{ + if (b) + { + return unroll_bool_function_template(std::forward(f), bs...); + } + return unroll_bool_function_template(std::forward(f), bs...); +} diff --git a/include/viewer/asset_loader.hpp b/include/viewer/asset_loader.hpp new file mode 100644 index 0000000..81d0af2 --- /dev/null +++ b/include/viewer/asset_loader.hpp @@ -0,0 +1,195 @@ +#pragma once + +#include +#include +#include + +#include "assets/data_loaders/glsl_loader.hpp" +#include "../assets/dynamic_read_buffers" +#include "opengl/handles/mesh_handle.hpp" +#include "opengl/handles/material_handle.hpp" +#include "opengl/handles/point_cloud_handle.hpp" +#include "SFML/Window.hpp" + +#include "opengl/data/mesh_data.hpp" +#include "opengl/data/material_data.hpp" +#include "opengl/data/point_cloud_data.hpp" +#include "opengl/data/shader_program_data.hpp" + +#include "assets/dynamic_data_loaders/dynamic_mesh_loader.hpp" +#include "assets/data_loaders/obj_loader.hpp" +#include "assets/data_loaders/stl_loader.hpp" + +#include "assets/dynamic_data_loaders/dynamic_point_cloud_loader.hpp" +#include "assets/data_loaders/kitti_loader.hpp" +#include "assets/data_loaders/uos_loader.hpp" +#include "assets/data_loaders/uos_normal_loader.hpp" +#include "assets/data_loaders/uos_rgb_loader.hpp" +#include "assets/data_loaders/uosr_loader.hpp" +#include "geometry/aabb.hpp" +#include "opengl/data/shader_data.hpp" + + +namespace viewer +{ + +class asset_loader +{ +public: + using dynamic_mesh_loader_type = dynamic_mesh_loader< + obj_loader, stl_loader + >; + + using dynamic_point_cloud_loader_type = dynamic_point_cloud_loader< + kitti_loader, + uos_loader, + uos_normal_loader, + uos_rgb_loader, + uos_loader, + uosr_loader + >; + + struct dynamic_point_cloud_handle_type + { + zgl::point_cloud_handle handle; + aabb bounding_box; + components::point_cloud_vertex::flags components; + }; + + struct dynamic_material_handle_type + { + zgl::material_handle handle; + material_component::flags components; + }; + + struct dynamic_mesh_handle_type + { + zgl::mesh_handle handle; + aabb bounding_box; + components::mesh_vertex::flags components; + }; + + using material_reference_entry_type = std::pair; + + std::error_code init( + components::mesh_vertex::flags enabled_mesh_components, + material_component::flags enabled_material_components, + components::point_cloud_vertex::flags enabled_point_cloud_components, + const dynamic_material_data& default_material + ); + + std::error_code load_shader( + GLenum type, + const std::filesystem::path& filename, + zgl::shader_handle& shader_handle + ); + + std::error_code build_shader_program( + const zgl::shader_handle& vertex_shader, + const zgl::shader_handle& geometry_shader, + const zgl::shader_handle& fragment_shader, + zgl::shader_program_handle& shader_program_handle + ); + + std::error_code load_asset( + const std::string& format, + const std::filesystem::path& filename, + std::vector>& dynamic_mesh_handles, + std::vector& dynamic_point_cloud_handles + ); + + std::error_code load_asset_directory( + const std::string& format, + const std::filesystem::path& path, + std::vector>& dynamic_mesh_handles, + std::vector& dynamic_point_cloud_handles + ); + + bool unload(const zgl::shader_program_handle& shader_handle); + + void unload_shader_data(); + + bool unload(const zgl::mesh_handle& mesh_handle); + + bool unload(const zgl::point_cloud_handle& point_cloud_handle); + + +protected: + std::error_code load_mesh( + const std::string& format, + const std::filesystem::path& filename, + std::vector>& dynamic_mesh_handles + ); + + std::error_code load_point_cloud( + const std::string& format, + const std::filesystem::path& filename, + std::vector& dynamic_point_cloud_handles + ); + + std::error_code load_mesh_directory( + const std::string& format, + const std::filesystem::path& path, + std::vector>& dynamic_mesh_handles + ); + + std::error_code load_point_cloud_directory( + const std::string& format, + const std::filesystem::path& path, + std::vector& dynamic_point_cloud_handles + ); + + std::error_code process_materials_and_meshes( + std::vector>& dynamic_mesh_handles + ); + + std::error_code process_point_clouds( + std::vector& dynamic_point_cloud_handles + ); + + + std::error_code create_gl_materials(); + + void create_gl_meshes(std::span material_references); + + void create_gl_point_clouds(); + + std::error_code create_gl_shader(); + +private: + //sf::Context m_ctx; + + components::mesh_vertex::flags m_enabled_mesh_components{ + components::mesh_vertex::flags::none + }; + material_component::flags m_enabled_material_components{ + material_component::flags::none + }; + + components::point_cloud_vertex::flags m_enabled_point_cloud_components{ + components::point_cloud_vertex::flags::none + }; + + glsl_loader m_shader_loader{}; + dynamic_mesh_loader_type m_mesh_loader{}; + dynamic_point_cloud_loader_type m_point_cloud_loader{}; + + dynamic_shader_data m_dynamic_shader_data_buffer{}; + std::vector m_dynamic_mesh_data_buffer{}; + std::vector m_dynamic_material_data_buffer{}; + std::vector m_dynamic_point_cloud_buffer{}; + + std::vector m_vertex_buffer{}; + + std::vector m_gl_shader_data{}; + std::vector m_gl_shader_program_data{}; + std::vector> m_gl_mesh_data{}; + std::vector> m_gl_point_cloud_data{}; + std::vector m_gl_material_data{}; + std::vector m_gl_material_data_references{}; + + ztu::u32 next_materials_id{ 0 }; + +}; + +} diff --git a/include/viewer/asset_types.hpp b/include/viewer/asset_types.hpp new file mode 100644 index 0000000..1d6c174 --- /dev/null +++ b/include/viewer/asset_types.hpp @@ -0,0 +1,9 @@ +#pragma once + +namespace viewer +{ +enum class asset_types { + mesh, + point_cloud +}; +} diff --git a/include/viewer/dynamic_shader_program_loading.hpp b/include/viewer/dynamic_shader_program_loading.hpp new file mode 100644 index 0000000..e043e30 --- /dev/null +++ b/include/viewer/dynamic_shader_program_loading.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include +#include "asset_loader.hpp" +#include "instance.hpp" + +namespace viewer::dynamic_shader_program_loading +{ + +void load_directory( + asset_loader& loader, + instance& z3d, + std::mutex& gl_resource_lock, + std::mutex& progress_lock, + std::string& progress_title, + float& progress_ratio, + const std::filesystem::path& path +); + +std::size_t count_shader_files( + const std::filesystem::path& path +); + +} diff --git a/include/viewer/instance.hpp b/include/viewer/instance.hpp new file mode 100644 index 0000000..969c96b --- /dev/null +++ b/include/viewer/instance.hpp @@ -0,0 +1,118 @@ +#pragma once + +#include +#include + +#include "viewer/settings.hpp" +#include + +#include "asset_types.hpp" +#include "geometry/aabb.hpp" +#include "rendering/modes/mesh_modes.hpp" +#include "rendering/modes/point_cloud_modes.hpp" + +#include "scene/flying_camera.hpp" +#include "scene/camera_view.hpp" +#include "scene/lighting_setup.hpp" + +#include "opengl/data/mesh_data.hpp" +#include "opengl/data/material_data.hpp" +#include "opengl/data/point_cloud_data.hpp" +#include "opengl/data/shader_program_data.hpp" +#include "rendering/batch_renderers/mesh_batch_renderer.hpp" +#include "rendering/batch_renderers/point_cloud_batch_renderer.hpp" +#include "rendering/shader_program_lookups/mesh_lookup.hpp" +#include "rendering/shader_program_lookups/point_cloud_lookup.hpp" + +#include "SFML/Graphics/RenderWindow.hpp" +#include "SFML/Graphics/Font.hpp" +#include "SFML/Graphics/Image.hpp" + + +namespace viewer { + +class instance +{ +static constexpr auto id_mesh_index = static_cast(asset_types::mesh); +static constexpr auto id_point_cloud_index = static_cast(asset_types::point_cloud); + +public: + using asset_id = std::variant< + rendering::mesh_batch_renderer::id_type, + rendering::point_cloud_batch_renderer::id_type + >; + + instance(); + + std::error_code init(std::string title); + + std::optional add_mesh( + const zgl::mesh_handle& mesh, + const aabb& bounding_box, + components::mesh_vertex::flags mesh_components, + const zgl::material_handle& material, + material_component::flags material_components + ); + + std::optional add_point_cloud( + const zgl::point_cloud_handle& point_cloud, + const aabb& bounding_box, + components::point_cloud_vertex::flags point_cloud_components + ); + + void add_shader_program( + asset_types type, zgl::shader_program_handle shader_program_handle + ); + + void set_background_color(const glm::vec4& color); + + bool remove(asset_id id); + + void run_progress( + std::mutex& lock, + std::string& title, + float& progress, + double fps = 30.0f + ); + + bool look_at(asset_id id); + + void run(std::mutex& gl_resource_lock, double fps = std::numeric_limits::max()); + + void windowed(unsigned int width, unsigned int height, bool decorations = true); + + void fullscreen(); + + void size(unsigned int width, unsigned int height); + +protected: + bool update(double dt); + + void render(); + +public: // TODO fix + sf::ContextSettings m_context_settings; + sf::RenderWindow m_window; + std::string m_title; + glm::ivec2 m_screen_size; + + sf::Font m_font{}; + sf::Image m_logo{}, m_spinner{}; + + rendering::shader_program_lookups::mesh_lookup m_mesh_shader_program_lookup; + rendering::shader_program_lookups::point_cloud_lookup m_point_cloud_shader_program_lookup; + + rendering::mesh_batch_renderer m_mesh_renderer; + rendering::point_cloud_batch_renderer m_point_cloud_renderer; + + rendering::modes::mesh m_mesh_render_mode; + rendering::modes::point_cloud m_point_cloud_render_mode; + + flying_camera m_camera; + camera_view m_view; + + settings m_settings; + bool m_mouse_locked{ false }; +}; + +} // namespace viewer \ No newline at end of file diff --git a/include/viewer/settings.hpp b/include/viewer/settings.hpp new file mode 100644 index 0000000..f297081 --- /dev/null +++ b/include/viewer/settings.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include "scene/lighting_setup.hpp" + +namespace viewer +{ +struct settings +{ + float mouse_sensitivity{ 0.001f }; + float scroll_speed{ 0.1f }; + lighting_setup lighting{ + .point_light_direction = { 1, -1, 1 }, + //.point_light_direction = { -1, -1, -1 }, + .point_light_color = { 3.0f, 3.0f, 3.0f }, + .ambient_light_color = { 0.5f, 0.5f, 0.5f } + }; +}; +} diff --git a/main.cpp b/main.cpp new file mode 100755 index 0000000..84c27a4 --- /dev/null +++ b/main.cpp @@ -0,0 +1,431 @@ +#include "viewer/instance.hpp" +#include "util/logger.hpp" +#include +#include +#include +#include + +#include "util/string_lookup.hpp" +#include "viewer/asset_loader.hpp" +#include "viewer/asset_types.hpp" +#include "viewer/dynamic_shader_program_loading.hpp" +#include "util/logger.hpp" +/* +void controller_task( + int args_count, char* args[], + viewer::instance* z3d, + std::mutex* gl_resource_lock, + std::mutex* progress_lock, + std::string* progress_title, + float* progress_ratio +) { + + using namespace std::chrono_literals; + std::this_thread::sleep_for(0.2s); + + using loader_type = viewer::asset_loader; + + auto loader = loader_type{}; + + + auto default_material = dynamic_material_data{}; + std::ignore = default_material.initialized_surface_properties(); // TODO ... + + constexpr auto enabled_mesh_components = ( + components::mesh_vertex::flags::position | + components::mesh_vertex::flags::tex_coord | + components::mesh_vertex::flags::normal | + components::mesh_vertex::flags::color | + components::mesh_vertex::flags::reflectance +); + + constexpr auto enabled_material_components = ( + material_component::flags::texture | + material_component::flags::surface_properties | + material_component::flags::transparency + ); + + // TODO Add point cloud component selection to loaders or remove it all together + constexpr auto enabled_point_cloud_components = ( + components::point_cloud_vertex::flags::position | + components::point_cloud_vertex::flags::normal | + components::point_cloud_vertex::flags::color | + components::point_cloud_vertex::flags::reflectance + ); + if (const auto e = loader.init( + enabled_mesh_components, + enabled_material_components, + enabled_point_cloud_components, + default_material + )) { + ztu::logger::error( + "Error while initializing resource loader: [%] %", + e.category().name(), + e.message() + ); + return; + } + + viewer::dynamic_shader_program_loading::load_directory( + loader, + *z3d, + *gl_resource_lock, + *progress_lock, + *progress_title, + *progress_ratio, + std::filesystem::current_path() / ".." / "shaders" + ); + + progress_lock->lock(); + *progress_title = "Assigning shaders..."; + *progress_ratio = 0.9f; + progress_lock->unlock(); + + progress_lock->lock(); + *progress_title = "Starting..."; + *progress_ratio = 1.0f; + progress_lock->unlock(); + + progress_lock->lock(); + *progress_ratio = std::numeric_limits::max(); + progress_lock->unlock(); + + + std::vector asset_ids; + auto dynamic_mesh_handles = std::vector>{}; + + auto dynamic_point_cloud_handles = std::vector{}; + + for (int i = 1; i + 2 <= args_count; i += 2) + { + + auto format = std::string{ args[i] }; + auto filename = std::filesystem::path{ args[i + 1] }; + + if (const auto e = loader.load_asset( + format, filename, dynamic_mesh_handles, dynamic_point_cloud_handles + )) { + ztu::logger::error( + "Error while loading file %: [%] %", + filename, + e.category().name(), + e.message() + ); + } + + for (const auto& [ dynamic_mesh_handle, dynamic_material_handle ] : dynamic_mesh_handles) + { + const auto& [ mesh_handle, bounding_box, mesh_components ] = dynamic_mesh_handle; + const auto& [ material_handle, material_components ] = dynamic_material_handle; + + ztu::logger::debug("mesh components: %", static_cast(mesh_components)); + + ztu::logger::debug("material components: %", static_cast(material_components)); + + gl_resource_lock->lock(); + const auto asset_id = z3d->add_mesh( + mesh_handle, bounding_box, mesh_components, + material_handle, material_components + ); + gl_resource_lock->unlock(); + ztu::logger::debug("Added mesh to z3d"); + + if (asset_id) + { + asset_ids.push_back(*asset_id); + } + else + { + ztu::logger::warn( + "Ignored mesh as its layout is not supported by z3d." + ); + } + } + + for (const auto& [ point_cloud_handle, bounding_box, point_cloud_components ] : dynamic_point_cloud_handles) + { + gl_resource_lock->lock(); + const auto asset_id = z3d->add_point_cloud( + point_cloud_handle, bounding_box, point_cloud_components + ); + gl_resource_lock->unlock(); + ztu::logger::debug("Added poitn cloud to z3d"); + + if (asset_id) + { + asset_ids.push_back(*asset_id); + } + else + { + ztu::logger::warn( + "Ignored Point cloud, as its layout is not supported by z3d." + ); + } + } + + dynamic_mesh_handles.clear(); + dynamic_point_cloud_handles.clear(); + } + + if (not asset_ids.empty()) + { + // TODO resource lock does not help with update + gl_resource_lock->lock(); + z3d->look_at(asset_ids.front()); + gl_resource_lock->unlock(); + } + + // TODO fix + std::this_thread::sleep_for(20h); +}*/ + + +void controller_task( + int args_count, char* args[], + viewer::instance* z3d, + viewer::asset_loader& loader, + std::mutex* gl_resource_lock, + std::mutex* progress_lock, + std::string* progress_title, + float* progress_ratio +) { + + using namespace std::chrono_literals; + + auto default_material = dynamic_material_data{}; + std::ignore = default_material.initialized_surface_properties(); // TODO ... + + constexpr auto enabled_mesh_components = ( + components::mesh_vertex::flags::position | + components::mesh_vertex::flags::tex_coord | + components::mesh_vertex::flags::normal | + components::mesh_vertex::flags::color | + components::mesh_vertex::flags::reflectance +); + + constexpr auto enabled_material_components = ( + material_component::flags::texture | + material_component::flags::surface_properties | + material_component::flags::transparency + ); + + // TODO Add point cloud component selection to loaders or remove it all together + constexpr auto enabled_point_cloud_components = ( + components::point_cloud_vertex::flags::position | + components::point_cloud_vertex::flags::normal | + components::point_cloud_vertex::flags::color | + components::point_cloud_vertex::flags::reflectance + ); + if (const auto e = loader.init( + enabled_mesh_components, + enabled_material_components, + enabled_point_cloud_components, + default_material + )) { + ztu::logger::error( + "Error while initializing resource loader: [%] %", + e.category().name(), + e.message() + ); + return; + } + + viewer::dynamic_shader_program_loading::load_directory( + loader, + *z3d, + *gl_resource_lock, + *progress_lock, + *progress_title, + *progress_ratio, + std::filesystem::current_path() / ".." / "shaders" + ); + + progress_lock->lock(); + *progress_title = "Assigning shaders..."; + *progress_ratio = 0.9f; + progress_lock->unlock(); + + progress_lock->lock(); + *progress_title = "Starting..."; + *progress_ratio = 1.0f; + progress_lock->unlock(); + + progress_lock->lock(); + *progress_ratio = std::numeric_limits::max(); + progress_lock->unlock(); + + using loader_type = viewer::asset_loader; + + std::vector asset_ids; + auto dynamic_mesh_handles = std::vector>{}; + + auto dynamic_point_cloud_handles = std::vector{}; + + for (int i = 1; i + 2 <= args_count; i += 2) + { + + auto format = std::string{ args[i] }; + auto filename = std::filesystem::path{ args[i + 1] }; + + if (const auto e = loader.load_asset( + format, filename, dynamic_mesh_handles, dynamic_point_cloud_handles + )) { + ztu::logger::error( + "Error while loading file %: [%] %", + filename, + e.category().name(), + e.message() + ); + } + + for (const auto& [ dynamic_mesh_handle, dynamic_material_handle ] : dynamic_mesh_handles) + { + const auto& [ mesh_handle, bounding_box, mesh_components ] = dynamic_mesh_handle; + const auto& [ material_handle, material_components ] = dynamic_material_handle; + + //ztu::logger::debug("mesh components: %", static_cast(mesh_components)); + //ztu::logger::debug("material components: %", static_cast(material_components)); + + gl_resource_lock->lock(); + const auto asset_id = z3d->add_mesh( + mesh_handle, bounding_box, mesh_components, + material_handle, material_components + ); + gl_resource_lock->unlock(); + ztu::logger::debug("Added mesh to z3d"); + + if (asset_id) + { + asset_ids.push_back(*asset_id); + } + else + { + ztu::logger::warn( + "Ignored mesh as its layout is not supported by z3d." + ); + } + } + + for (const auto& [ point_cloud_handle, bounding_box, point_cloud_components ] : dynamic_point_cloud_handles) + { + gl_resource_lock->lock(); + const auto asset_id = z3d->add_point_cloud( + point_cloud_handle, bounding_box, point_cloud_components + ); + gl_resource_lock->unlock(); + ztu::logger::debug("Added poitn cloud to z3d"); + + if (asset_id) + { + asset_ids.push_back(*asset_id); + } + else + { + ztu::logger::warn( + "Ignored Point cloud, as its layout is not supported by z3d." + ); + } + } + + dynamic_mesh_handles.clear(); + dynamic_point_cloud_handles.clear(); + } + + if (not asset_ids.empty()) + { + // TODO resource lock does not help with update + gl_resource_lock->lock(); + z3d->look_at(asset_ids.front()); + gl_resource_lock->unlock(); + } +} + +int main(int args_count, char* args[]) { + + // Clion struggles interleaving the error stream, so we just don't use it... + ztu::logger::global_context().err = &std::cout; + ztu::logger::global_dynamic_config().flags &= ~ztu::logger::flag::time; + + auto z3d = viewer::instance{}; + + if (const auto e = z3d.init("Z3D")) + { + ztu::logger::error( + "Failed to initialize viewer: [%] %", + e.category().name(), + e.message() + ); + return EXIT_FAILURE; + } + + //glEnable(GL_DEBUG_OUTPUT); + glDebugMessageCallback( + []( + GLenum source, + GLenum type, + GLuint id, + GLenum severity, + GLsizei length, + const GLchar* message, + const void* userParam + ) { + ztu::logger::error("[%] % type: %%, severity: %%, msg: %", + "OpenGL", + (type == GL_DEBUG_TYPE_ERROR ? "ERROR" : ""), + std::hex, type, + std::hex, severity, + message + ); + }, + nullptr + ); + + std::mutex gl_resource_lock; + std::mutex progress_lock; + std::string progress_title = "Initializing..."; + float progress_ratio = 0.0f; + + /*auto controller_thread = std::thread( + controller_task, + args_count, + args, + &z3d, + &gl_resource_lock, + &progress_lock, + &progress_title, + &progress_ratio + );*/ + + auto loader = viewer::asset_loader{}; + + controller_task( + args_count, + args, + &z3d, + loader, + &gl_resource_lock, + &progress_lock, + &progress_title, + &progress_ratio + ); + + z3d.run_progress( + progress_lock, + progress_title, + progress_ratio + ); + + z3d.size(1280, 720); + + + z3d.run(gl_resource_lock, 60.0f); + + + return EXIT_SUCCESS; +} diff --git a/source/assets/data_loaders/generic/generic_3dtk_loader.ipp b/source/assets/data_loaders/generic/generic_3dtk_loader.ipp new file mode 100644 index 0000000..e53ee1b --- /dev/null +++ b/source/assets/data_loaders/generic/generic_3dtk_loader.ipp @@ -0,0 +1,444 @@ +#ifndef INCLUDE_GENERIC_3DTK_LOADER_IMPLEMENTATION +# error Never include this file directly include 'generic_3dtk_loader.hpp' +#endif + +#include +#include +#include "util/logger.hpp" +#include "glm/glm.hpp" +#include "glm/gtx/euler_angles.hpp" +#include +#include + +template +ztu::result generic_3dtk_loader::parse_index( + const std::string_view filename +) { + static constexpr auto prefix = std::string_view{ "scan" }; + + auto name_view = filename.substr(0, name_view.find('.')); + + if (name_view.length() <= prefix.length()) [[unlikely]] + { + return std::make_error_code(std::errc::invalid_argument); + } + + name_view = name_view.substr(prefix.length()); + + pose_prefetch_lookup::index_type index; + + const auto res = std::from_chars(name_view.begin(), name_view.end(), index); + + if (res.ec != std::errc{}) [[unlikely]] + { + return std::make_error_code(res.ec); + } + + return index; +} + +template +std::error_code generic_3dtk_loader::prefetch( + const file_dir_list& paths, + prefetch_queue& queue +) { + + auto path_buffer = std::filesystem::path{}; + + for (const auto filename : paths.files) + { + path_buffer.assign(filename.begin(), filename.end()); + path_buffer.replace_extension(".pose"); + queue.uos_queue.files.push_back(path_buffer.c_str()); + } + + // TODO optimize + for (const auto directory : paths.directories) + { + queue.uos_queue.directories.push_back(directory); + } +} + +template +std::error_code generic_3dtk_loader::load( + dynamic_point_cloud_buffer& buffer, + const file_dir_list& paths, + prefetch_lookup& asset_lookup, + dynamic_point_cloud_store& store, + const bool pedantic +) { + namespace fs = std::filesystem; + + auto in = std::ifstream{}; + auto path_buffer = fs::path{}; + auto error = std::error_code{}; + + const auto load_file = [&](const char* filename) + { + // TODO look up pose + auto scan_index = pose_prefetch_lookup::index_type{}; + + if (auto res = parse_index(filename)) + { + scan_index = *res; + } + else [[unlikely]] + { + error = res.error(); + ztu::logger::error( + "Error occurred while parsing scan index in filename %: [%] %", + error.category().name(), + error.message() + ); + } + + auto pose = asset_lookup.poses.find(); + + + const auto id_it = id_lookup.find(filename); + + if (id_it != id_lookup.end()) [[unlikely]] + { + return; + } + + in.open(filename); + if (in.is_open()) + { + if ((error = this->read_point_file(filename, buffer))) { + return error; + } + + this->transform_point_cloud(buffer.positions(), pose); + + const auto id = store.add(std::move(buffer)); + id_lookup.emplace_hint(id_it, filename, id); + } + else + { + ztu::logger::error("Cannot open 3dtk file %", filename); + } + + in.close(); + }; + + for (const auto filename : paths.files) + { + load_file(filename.data()); + } + + + + for (const auto directory : paths.directories) + { + directory_buffer.assign(directory.begin(), directory.end()); + directory_buffer /= "frames"; + + const auto directory_exists = not fs::is_directory(directory_buffer, error); + + if (error or not directory_exists) [[unlikely]] + { + ztu::logger::error("Could not open point cloud directory %", directory_buffer); + continue; + } + + for (const auto& filename : fs::directory_iterator{ directory_buffer }) { + + auto point_filename = reinterpret_cast(filename); + if (point_filename.extension() != ".3d") + { + continue; + } + + load_file(filename.c_str()); + } + } + + return {}; +} + +template +std::error_code generic_3dtk_loader::load_directory( + dynamic_data_loader_ctx& ctx, + dynamic_point_cloud_store& store, + const std::filesystem::path& path, + const bool pedantic +) { + namespace fs = std::filesystem; + + std::error_code error; + + const auto directory_exists = not fs::is_directory(path, error); + if (error) + { + return error; + } + + if (not directory_exists) + { + return make_error_code(std::errc::no_such_file_or_directory); + } + + for (const auto& filename : fs::directory_iterator{ path / "frames" }) { + + auto point_filename = reinterpret_cast(filename); + if (point_filename.extension() != ".3d") { + continue; + } + + if ((error = load(ctx, store, point_filename, pedantic))) + { + ztu::logger::error( + "Error while loading point cloud '%': [%] %", + point_filename, + error.category().name(), + error.message() + ); + } + } + + return {}; +} + +template +std::error_code read_vector(std::string_view& line, std::array& vec) { + for (auto& component : vec) + { + auto it = line.begin(); + + const auto [minus, plus] = std::pair{ *it == '-', *it == '+' }; + it += plus or minus ? 3 : 2; // skip '[-+]?0x' + + const auto [ ptr, ec ] = std::from_chars( + it, line.end(), + component, + std::chars_format::hex + ); + + if (ec != std::errc{}) + { + return std::make_error_code(ec); + } + + if (minus) { + component *= -1.0; + } + + line = { ptr + sizeof(' '), line.end() }; + } + + return {}; +} + +template +std::error_code generic_3dtk_loader::read_point_file( + const std::filesystem::path& filename, + dynamic_point_cloud_data& point_cloud +) { + std::error_code error; + + auto in = std::ifstream(filename); + + if (not in.is_open()) { + return std::make_error_code(static_cast(errno)); + } + + std::string line; + if (not std::getline(in, line)) + { + return std::make_error_code(std::errc::invalid_seek); + } + + constexpr auto expected_component_count = []() + { + auto count = std::tuple_size_v; + + if (Normal) + { + count += std::tuple_size_v; + } + + if (Color) + { + count += std::tuple_size_v; + } + + if (Reflectance) + { + count += std::tuple_size_v; + } + + return count; + }(); + + ztu::u32 component_count; + std::chars_format float_format; + + if ((error = analyze_component_format(line, component_count, float_format))) + { + return error; + } + + if (component_count != expected_component_count) + { + return std::make_error_code(std::errc::invalid_argument); + } + + auto& positions = point_cloud.positions(); + auto& normals = point_cloud.normals(); + auto& colors = point_cloud.colors(); + auto& reflectances = point_cloud.reflectances(); + + do + { + auto line_view = std::string_view{ line }; + + components::point_cloud_vertex::position position; + if ((error = read_vector(line_view, position))) + { + return error; + } + positions.push_back(position); + + if constexpr (Normal) + { + components::point_cloud_vertex::normal normal; + if ((error = read_vector(line_view, normal))) + { + return error; + } + normals.push_back(normal); + } + + if constexpr (Color) + { + components::point_cloud_vertex::color color; + if ((error = read_vector(line_view, color))) + { + return error; + } + colors.push_back(color); + } + + if constexpr (Reflectance) + { + components::point_cloud_vertex::reflectance reflectance; + if ((error = read_vector(line_view, reflectance))) + { + return error; + } + reflectances.push_back(reflectance); + } + } + while (std::getline(in, line)); + + return {}; +} + +std::error_code base_3dtk_loader::read_pose_file( + const std::filesystem::path& filename, + glm::mat4& pose +) { + auto in = std::ifstream(filename); + if (not in.is_open()) { + return std::make_error_code(static_cast(errno)); + } + std::string line; + + std::array numbers{}; + + for (std::size_t row{}; row != 2; ++row) { + + std::getline(in, line); + + auto it = line.cbegin().base(); + auto end = line.cend().base(); + + for (glm::vec3::length_type col{}; col != 3; ++col) { + + const auto [ ptr, ec ] = std::from_chars( + it, end, + numbers[row][col], + std::chars_format::general + ); + + if (ec != std::errc{}) { + return std::make_error_code(ec); + } + + it = ptr + 1; // skip space in between components + } + } + + const auto& translation = numbers[0]; + auto& angles = numbers[1]; + angles *= static_cast(M_PI / 180.0); + + pose = ( + glm::translate(glm::identity(), translation) * + glm::eulerAngleXYZ(angles[0], angles[1], angles[2]) + ); + + return {}; +} + +std::error_code base_3dtk_loader::analyze_component_format( + const std::string& line, + ztu::u32& component_count, + std::chars_format& format +) { + auto begin = line.cbegin().base(); + auto end = line.cend().base(); + + format = std::chars_format::general; + + component_count = 0; + float buffer; + + for (auto it = begin; it < end; it += sizeof(' ')) + { + it += *it == '-' or *it == '+'; + + std::chars_format current_format; + if (*it == '0' and std::next(it) < end and *std::next(it) == 'x') + { + it += 2; // skip '0x' + current_format = std::chars_format::hex; + } + else + { + current_format = std::chars_format::general; + } + + if (it == begin and current_format != format) + { + return std::make_error_code(std::errc::invalid_argument); + } + + const auto [next_it, err] = std::from_chars(it, end, buffer, current_format); + if (err != std::errc()) + { + return std::make_error_code(err); + } + + it = next_it; + format = current_format; + ++component_count; + } + + return {}; +} + + +void base_3dtk_loader::transform_point_cloud( + std::span points, + const glm::mat4& pose +) { + for (auto& [ x, y, z ] : points) { + auto vec = glm::vec4{ x, y, z, 1.0f }; + vec = pose * vec; + x = vec.x; + y = vec.y; + z = vec.z; + } +} diff --git a/source/assets/data_loaders/glsl_loader.cpp b/source/assets/data_loaders/glsl_loader.cpp new file mode 100644 index 0000000..43cbe31 --- /dev/null +++ b/source/assets/data_loaders/glsl_loader.cpp @@ -0,0 +1,36 @@ +#include "assets/data_loaders/glsl_loader.hpp" + +#include + +std::error_code glsl_loader::load( + const std::filesystem::path& filename, + std::string& source +) { + + auto file = std::ifstream(filename); + if (not file.is_open()) + { + return std::make_error_code(std::errc::no_such_file_or_directory); + } + + file.seekg(0, std::ios::end); + const auto size = file.tellg(); + + if (size == 0 or size == std::numeric_limits::max()) + { + return std::make_error_code(std::errc::invalid_seek); + } + + source.reserve(size); + + file.seekg(0, std::ios::beg); + + source.assign( + std::istreambuf_iterator(file), + std::istreambuf_iterator() + ); + + file.close(); + + return {}; +} diff --git a/source/assets/data_loaders/kitti_loader.cpp b/source/assets/data_loaders/kitti_loader.cpp new file mode 100644 index 0000000..71b73a9 --- /dev/null +++ b/source/assets/data_loaders/kitti_loader.cpp @@ -0,0 +1,302 @@ +#include "assets/data_loaders/kitti_loader.hpp" + +#include "glm/glm.hpp" + +#include +#include +#include + +#include "assets/components/point_cloud_vertex_components.hpp" +#include "util/binary_ifstream.hpp" +#include "util/logger.hpp" + + +ztu::result kitti_loader::parent_directory(const std::string_view path) +{ + const auto sep_index = path.rfind(std::filesystem::path::preferred_separator); + + if (sep_index == std::string_view::npos) + { + return std::unexpected(std::make_error_code(std::errc::no_such_file_or_directory)); + } + + return path.substr(0, sep_index); +}; + +std::error_code kitti_loader::prefetch( + const file_dir_list& paths, + prefetch_queue& queue +) { + + // Directories can simply be passed on + queue.kitti_pose_queue.directories.push_back(paths.directories); + + // For files, we just forward the files directory + for (const auto file : queue.kitti_pose_queue.files) + { + if (const auto base_directory = parent_directory(file).and_then(parent_directory)) + { + queue.kitti_pose_queue.directories.push_back(*base_directory); + } + else + { + // TODO remove from list + ztu::logger::error("Malformed kitti file path: %.", file); + } + } + + return {}; +} + +std::error_code kitti_loader::load( + dynamic_point_cloud_buffer& buffer, + const file_dir_list& paths, + prefetch_lookup& id_lookup, + dynamic_data_store& store, + bool +) { + namespace fs = std::filesystem; + std::error_code error; + + std::vector pose_its; + pose_its.reserve(paths.files.size()); + + auto processed_filenames = ztu::string_list{}; + + auto path_buffer = fs::path{}; + + const auto preprocess_filename = [&]( + std::string_view path, + const auto& directory, + std::string_view filename, + const pose_prefetch_lookup::directory_iterator& dir_it + ) { + const auto pose_index = frame_id_from_filename(filename); + if (not pose_index) [[unlikely]] + { + ztu::logger::error("Could not parse frame id from kitti file path: %.", filename); + return; + } + + const auto [ index_it, pose_id_match ] = id_lookup.poses.find_index(dir_it, *pose_index); + + if (not pose_id_match) [[unlikely]] + { + ztu::logger::error("No matching pose index (%) found in directory (%).", directory, *pose_index); + return; + } + + const auto [ pose_it, pose_match ] = store.poses.find(pose_id_match); + if (not pose_id_match) [[unlikely]] + { + ztu::logger::error("No matching pose found for id: %.", pose_id_match); + return; + } + + processed_filenames.push_back(path); + pose_its.push_back(pose_it); + }; + + for (const auto file : paths.files) + { + path_buffer.assign(file.begin(), file.end()); + + if (not fs::is_regular_file(path_buffer)) + { + ztu::logger::error("Given kitti file does not exist: %.", path_buffer); + continue; + } + + const auto sep_index = file.rfind(fs::path::preferred_separator); + + if (sep_index == std::string_view::npos) [[unlikely]] + { + ztu::logger::error("Could not parse frame directory from kitti file path: %.", file); + continue; + } + + const auto base_directory = parent_directory(file.substr(0, sep_index)); + if (not base_directory) [[unlikely]] + { + ztu::logger::error("Could not parse base directory from kitti file path: %.", file); + continue; + } + + const auto [ dir_it, dir_match ] = id_lookup.poses.find_directory(*base_directory); + if (not dir_match) [[unlikely]] + { + ztu::logger::error("No matching pose directory found for %.", file); + continue; + } + + const auto filename = file.substr(sep_index + 1); + + preprocess_filename( + file, + *base_directory, + filename, + dir_it + ); + } + + for (const auto directory : paths.directories) + { + path_buffer.assign(directory.begin(), directory.end()); + + const auto [ dir_it, dir_match ] = id_lookup.poses.find_directory(path_buffer); + if (not dir_match) [[unlikely]] + { + ztu::logger::error("No matching pose directory found for %.", path_buffer); + continue; + } + + path_buffer /= frame_folder; + + if (not fs::is_directory(path_buffer)) + { + ztu::logger::error("Given kitti directory does not exist: %.", directory); + continue; + } + + for (const auto& file : fs::directory_iterator{ path_buffer }) + { + const auto file_path = std::string_view{ file.path().c_str() }; + + const auto extension_begin = file_path.rfind('.'); + + if (extension_begin == std::string_view::npos or file_path.substr(extension_begin) != ".bin") + { + continue; + } + + auto filename_begin = file_path.rfind(fs::path::preferred_separator, extension_begin); + filename_begin = filename_begin == std::string_view::npos ? 0 : filename_begin + 1; + + const auto filename_only = file_path.substr(filename_begin); + + const auto pose_index = frame_id_from_filename(filename_only); + if (not pose_index) [[unlikely]] + { + ztu::logger::error("Could not parse frame id from kitti filename: %.", filename_only); + continue; + } + + const auto [ index_it, pose_id_match ] = id_lookup.poses.find_index(dir_it, *pose_index); + + if (not pose_id_match) [[unlikely]] + { + ztu::logger::error("No matching pose index (%) found in directory (%).", directory, *pose_index); + continue; + } + + const auto [ pose_it, pose_match ] = store.poses.find(pose_id_match); + if (not pose_id_match) [[unlikely]] + { + ztu::logger::error("No matching pose found for id: %.", pose_id_match); + continue; + } + + processed_filenames.push_back(file_path); + pose_its.push_back(pose_it); + + preprocess_filename( + file_path, + directory, + filename_only, + dir_it + ); + } + } + + + for (const auto [ filename, pose_it ] : std::ranges::views::zip_view(processed_filenames, pose_its)) + { + buffer.clear(); + + if ((error = load_point_file(filename, buffer))) + { + return error; + } + + transform_point_cloud(buffer.positions(), *pose_it); + + store.point_clouds.add(buffer); + } + + return {}; +} + +void kitti_loader::transform_point_cloud( + std::span points, + const glm::mat4& pose +) { + for (auto& [ x, y, z ] : points) { + auto vec = glm::vec4{ x, y, z, 1.0f }; + vec = pose * vec; + x = vec.x; + y = vec.y; + z = vec.z; + } +} + +std::error_code kitti_loader::load_point_file( + const std::filesystem::path& filename, + dynamic_point_cloud_buffer& point_cloud +) { + + auto in = binary_ifstream{}; + + auto error = std::error_code{}; + + if ((error == in.open(filename, true))) + { + return error; + } + + const auto read_vector = [&in](auto& vector) -> std::error_code + { + for (auto& component : vector) + { + float component32; + if (const auto e = in.read_ieee754(component32)) + { + return e; + } + component = component32; + } + return {}; + }; + + components::point_cloud_vertex::position position; + + auto& positions = point_cloud.positions(); + + while (not ((error = read_vector(position)))) { + positions.push_back(position); + if ((error = in.skip())) // TODO what am I skipping here?!? + { + break; + } + } + + if (static_cast(error.value()) != std::errc::result_out_of_range) + { + return error; + } + + return {}; +} + +ztu::result kitti_loader::frame_id_from_filename( + std::string_view filename +) { + std::size_t id; + const auto result = std::from_chars(filename.cbegin(), filename.cend(), id); + + if (result.ec != std::errc{}) + { + return std::unexpected(std::make_error_code(result.ec)); + } + + return id; +} diff --git a/source/assets/data_loaders/kitti_pose_loader.cpp b/source/assets/data_loaders/kitti_pose_loader.cpp new file mode 100644 index 0000000..d256b8a --- /dev/null +++ b/source/assets/data_loaders/kitti_pose_loader.cpp @@ -0,0 +1,163 @@ +#include "assets/data_loaders/kitti_pose_loader.hpp" + +#include "assets/dynamic_read_buffers/dynamic_pose_buffer.hpp" +#include +#include +#include "util/logger.hpp" + +inline std::error_code kitti_pose_loader::parse_pose( + std::ifstream& in, + dynamic_pose_buffer& pose +) { + for (dynamic_pose_buffer::length_type row{}; row != 3; ++row) + { + for (dynamic_pose_buffer::length_type col{}; col != 4; ++col) + { + if (not (in >> pose[row][col])) + { + return std::make_error_code(std::errc::result_out_of_range); + } + } + } + + return {}; +} + + +std::error_code kitti_pose_loader::prefetch( + const file_dir_list& paths, + prefetch_queue& queue +) { + // Nothing to be done here +} + +std::error_code kitti_pose_loader::load( + dynamic_pose_buffer& buffer, + const file_dir_list& paths, + prefetch_lookup& id_lookup, + dynamic_data_store& store, + bool pedantic +) { + namespace fs = std::filesystem; + + auto path_buffer = fs::path{}; + auto in = std::ifstream{}; // TODO disable exceptions (for other loaders as well) + auto pose_buffer = dynamic_pose_buffer{}; + + pose_buffer = glm::identity(); + + auto processed_filenames = ztu::string_list{}; + + processed_filenames.reserve( + paths.files.character_count() + + paths.directories.character_count() + + paths.directories.size() * pose_filename.size(), + paths.files.size() + paths.directories.size() + ); + + const auto preprocess_file = [&]() + { + if (not fs::is_regular_file(path_buffer)) + { + ztu::logger::error("Kitti pose file does not exist: %", path_buffer); + return; + } + + processed_filenames.push_back(path_buffer.c_str()); + }; + + for (const auto directory : paths.directories) { + path_buffer.assign(directory.begin(), directory.end()); + path_buffer /= "pose.txt"; + preprocess_file(); + } + + for (const auto file : paths.files) { + path_buffer.assign(file.begin(), file.end()); + preprocess_file(); + } + + for (const auto filename : processed_filenames) + { + in.open(filename.data()); // Safe because string list adds null terminator + if (not in.is_open()) + { + ztu::logger::error("Cannot open kitti pose file %", path_buffer); + continue; + } + + in >> std::skipws; + + for (auto i = pose_prefetch_lookup::index_type{}; in.peek() != std::ifstream::traits_type::eof(); ++i) + { + if (const auto error = parse_pose(in, pose_buffer)) + { + ztu::logger::error( + "Error occurred while parsing kitti pose % in file %: [%] %", + i, + path_buffer, + error.category().name(), + error.message() + ); + continue; + } + + const auto id = store.poses.add(pose_buffer); + + // TODO if (not) removing the path separator creates issues. + const auto directory = filename.substr(0, filename.length() - pose_filename.length()); + + id_lookup.poses.emplace(directory, i, id); + } + + in.close(); + } +} + +void kitti_pose_loader::load( + const ztu::string_list& directories, + dynamic_pose_store& store, + pose_prefetch_lookup& id_lookup +) { + + auto filename_buffer = std::filesystem::path{}; + auto in = std::ifstream{}; // TODO disable exceptions (for other loaders as well) + auto pose_buffer = dynamic_pose_buffer{}; + + pose_buffer = glm::identity(); + + for (const auto directory : directories) + { + filename_buffer = directory; + filename_buffer /= "pose.txt"; + + in.open(filename_buffer); + if (not in.is_open()) + { + ztu::logger::error("Cannot open kitti pose file %", filename_buffer); + continue; + } + + in >> std::skipws; + + for (auto i = pose_prefetch_lookup::index_type{}; in.peek() != std::ifstream::traits_type::eof(); ++i) + { + if (const auto error = parse_pose(in, pose_buffer)) + { + ztu::logger::error( + "Error occurred while parsing kitti pose % in file %: [%] %", + i, + filename_buffer, + error.category().name(), + error.message() + ); + continue; + } + + const auto id = store.add(pose_buffer); + id_lookup.emplace(directory, i, id); + } + + in.close(); + } +} \ No newline at end of file diff --git a/source/assets/data_loaders/mtl_loader.cpp b/source/assets/data_loaders/mtl_loader.cpp new file mode 100644 index 0000000..040d411 --- /dev/null +++ b/source/assets/data_loaders/mtl_loader.cpp @@ -0,0 +1,398 @@ +#include "assets/data_loaders/mtl_loader.hpp" + +#include +#include +#include + +#include "util/logger.hpp" +#include "util/for_each.hpp" +#include "util/line_parser.hpp" + +#include "assets/dynamic_data_loaders/dynamic_texture_loader.hpp" + +namespace mtl_loader_error { + +struct category : std::error_category { + [[nodiscard]] const char* name() const noexcept override { + return "connector"; + } + + [[nodiscard]] std::string message(int ev) const override { + switch (static_cast(ev)) { + using enum codes; + case mtl_cannot_open_file: + return "Cannot open mtl file."; + case mtl_cannot_open_texture: + return "Cannot open texture file."; + case mtl_malformed_ambient_color: + return "File contains malformed 'Ka' statement."; + case mtl_malformed_diffuse_color: + return "File contains malformed 'Kd' statement."; + case mtl_malformed_specular_color: + return "File contains malformed 'Ks' statement."; + case mtl_malformed_specular_exponent: + return "File contains malformed 'Ns' statement."; + case mtl_malformed_dissolve: + return "File contains malformed 'd' statement."; + case mlt_unknown_line_begin: + return "Unknown mtl line begin"; + default: + using namespace std::string_literals; + return "unrecognized error ("s + std::to_string(ev) + ")"; + } + } +}; + +} // namespace mesh_loader_error + +inline std::error_category& connector_error_category() { + static mtl_loader_error::category category; + return category; +} + +namespace mtl_loader_error { + +inline std::error_code make_error_code(codes e) { + return { static_cast(e), connector_error_category() }; +} + +} // namespace mtl_loader_error + + +template +std::errc parse_numeric_vector(std::string_view param, std::array& values) { + auto it = param.begin(), end = param.end(); + + for (auto& value : values) + { + if (it >= end) + { + return std::errc::invalid_argument; + } + + const auto [ptr, ec] = std::from_chars(it, end, value); + + if (ec != std::errc{}) + { + return ec; + } + + it = ptr + 1; // skip space in between components + } + + return {}; +}; + +std::optional mtl_loader::find_id(std::string_view name) +{ + const auto it = m_id_lookup.find(name); + + if (it == m_id_lookup.end()) + { + return it->second; + } + + return std::nullopt; +} + +void mtl_loader::clear_name_lookup() { + m_id_lookup.clear(); +} + +std::error_code mtl_loader::load_directory( + dynamic_data_loader_ctx& ctx, + dynamic_material_store& store, + components::material::flags enabled_components, + const std::filesystem::path& path, + const bool pedantic +) { + namespace fs = std::filesystem; + + if (not fs::exists(path)) + { + return make_error_code(std::errc::no_such_file_or_directory); + } + + for (const auto& file : fs::directory_iterator{ path }) + { + const auto& file_path = file.path(); + + if (file_path.extension() != ".obj") + { + continue; + } + + if (const auto e = load( + ctx, + store, + enabled_components, + path, + pedantic + )) { + ztu::logger::error( + "Error while loading obj file '%': [%] %", + file_path, + e.category().name(), + e.message() + ); + } + } + + return {}; +} + +std::error_code mtl_loader::load( + dynamic_data_loader_ctx& ctx, + dynamic_material_store& store, + components::material::flags enabled_components, + const std::filesystem::path& filename, + const bool pedantic +) { + using mtl_loader_error::codes; + using mtl_loader_error::make_error_code; + + using flags = components::material::flags; + + const auto component_disabled = [&](const components::material::flags component) { + return (enabled_components & component) == flags::none; + }; + + // TODO unroll stuff + const auto textures_disabled = component_disabled(flags::ambient_filter_texture); + const auto surface_properties_disabled = component_disabled(flags::surface_properties); + const auto transparencies_disabled = component_disabled(flags::transparency); + + auto in = std::ifstream{ filename }; + if (not in.is_open()) { + return make_error_code(codes::mtl_cannot_open_file); + } + + namespace fs = std::filesystem; + const auto directory = fs::canonical(fs::path(filename).parent_path()); + + auto name = std::string{}; + auto material = dynamic_material_data{}; + + const auto push_material = [&]() + { + if (not name.empty()) + { + const auto id = store.add(std::move(material)); + m_id_lookup.emplace(std::move(name), id); + } + name = std::string{}; + material = dynamic_material_data{}; + }; + + const auto load_texture = [&]( + const std::string_view path, + std::string_view texture_type_name, + auto&& f + ) { + auto texture_filename = fs::path(path); + if (texture_filename.is_relative()) + { + texture_filename = directory / texture_filename; + } + + const auto extension = texture_filename.extension().string(); + + auto texture_type = std::string_view{ extension }; + if (not texture_type.empty() and texture_type.front() == '.') + { + texture_type = texture_type.substr(1); + } + + if (const auto loader_id = ctx.texture_loader.find_loader(texture_type)) + { + if (auto res = ctx.texture_loader.read( + ctx, + *loader_id, + texture_filename, + pedantic + )) { + f(*res); + } + else + { + const auto error = res.error(); + ztu::logger::warn( + "Error while loading % texture '%': [%] %", + texture_type_name, + path, + error.category().name(), + error.message() + ); + } + } + else + { + ztu::logger::warn( + "Failed to load % texture '%' because extension is not supported.", + texture_type_name, + path + ); + } + }; + + const auto ec = ztu::parse_lines( + in, + pedantic, + ztu::make_line_parser("newmtl ", ztu::is_not_repeating, [&](const auto& param) + { + push_material(); + name = param; + + return codes::ok; + }), + ztu::make_line_parser("Ka ", ztu::is_not_repeating, [&](const auto& param) + { + if (surface_properties_disabled) return codes::ok; + + auto& properties = material.initialized_surface_properties(); + if (parse_numeric_vector(param, properties.ambient_filter) != std::errc{}) [[unlikely]] + { + return codes::mtl_malformed_ambient_color; + } + + material.components() |= flags::surface_properties; + + return codes::ok; + }), + ztu::make_line_parser("Kd ", ztu::is_not_repeating, [&](const auto& param) + { + if (surface_properties_disabled) return codes::ok; + + auto& properties = material.initialized_surface_properties(); + if (parse_numeric_vector(param, properties.diffuse_filter) != std::errc{}) [[unlikely]] + { + return codes::mtl_malformed_diffuse_color; + } + + material.components() |= flags::surface_properties; + + return codes::ok; + }), + ztu::make_line_parser("Ks ", ztu::is_not_repeating, [&](const auto& param) + { + if (surface_properties_disabled) return codes::ok; + + auto& properties = material.initialized_surface_properties(); + if (parse_numeric_vector(param, properties.specular_filter) != std::errc{}) [[unlikely]] + { + return codes::mtl_malformed_specular_color; + } + + material.components() |= flags::surface_properties; + + return codes::ok; + }), + ztu::make_line_parser("Ns ", ztu::is_not_repeating, [&](const auto& param) + { + if (surface_properties_disabled) return codes::ok; + + auto& properties = material.initialized_surface_properties(); + std::array shininess{}; + if (parse_numeric_vector(param, shininess) != std::errc{}) [[unlikely]] + { + return codes::mtl_malformed_specular_exponent; + } + + properties.shininess = shininess.front(); + material.components() |= flags::surface_properties; + + return codes::ok; + }), + ztu::make_line_parser("d ", ztu::is_not_repeating, [&](const auto& param) + { + if (transparencies_disabled) return codes::ok; + + std::array transparency{}; + if (parse_numeric_vector(param, transparency) != std::errc{}) [[unlikely]] + { + return codes::mtl_malformed_dissolve; + } + + material.transparency().emplace(transparency.front()); + material.components() |= flags::transparency; + + return codes::ok; + }), + ztu::make_line_parser("map_Ka ", ztu::is_not_repeating, [&](const auto& param) + { + if (textures_disabled) return codes::ok; + + load_texture(param, "ambient color", [&](const auto id) { + material.ambient_color_texture_id() = id; + material.components() |= flags::ambient_filter_texture; + }); + + return codes::ok; + }), + ztu::make_line_parser("map_Kd ", ztu::is_not_repeating, [&](const auto& param) + { + if (textures_disabled) return codes::ok; + + load_texture(param, "diffuse color", [&](const auto id) { + material.diffuse_color_texture_id() = id; + material.components() |= flags::diffuse_filter_texture; + }); + + return codes::ok; + }), + ztu::make_line_parser("map_Ks ", ztu::is_not_repeating, [&](const auto& param) + { + if (textures_disabled) return codes::ok; + + load_texture(param, "specular color", [&](const auto id) { + material.specular_color_texture_id() = id; + material.components() |= flags::specular_filter_texture; + }); + + return codes::ok; + }), + ztu::make_line_parser("map_Ns ", ztu::is_not_repeating, [&](const auto& param) + { + if (textures_disabled) return codes::ok; + + load_texture(param, "shininess", [&](const auto id) { + material.shininess_texture_id() = id; + material.components() |= flags::shininess_texture; + }); + + return codes::ok; + }), + ztu::make_line_parser("map_d ", ztu::is_not_repeating, [&](const auto& param) + { + if (textures_disabled) return codes::ok; + + load_texture(param, "alpha", [&](const auto id) { + material.alpha_texture_id() = id; + material.components() |= flags::alpha_texture; + }); + + return codes::ok; + }), + ztu::make_line_parser("bump ", ztu::is_not_repeating, [&](const auto& param) + { + if (textures_disabled) return codes::ok; + + load_texture(param, "bump", [&](const auto id) { + material.bump_texture_id() = id; + material.components() |= flags::bump_texture; + }); + + return codes::ok; + }) + ); + + if (ec != codes::ok) + { + return make_error_code(ec); + } + + push_material(); + + return {}; +} + diff --git a/source/assets/data_loaders/obj_loader.cpp b/source/assets/data_loaders/obj_loader.cpp new file mode 100755 index 0000000..b15b2cd --- /dev/null +++ b/source/assets/data_loaders/obj_loader.cpp @@ -0,0 +1,450 @@ +#include "assets/data_loaders/obj_loader.hpp" + +#include +#include +#include + +#include "assets/components/mesh_vertex_components.hpp" +#include "assets/dynamic_data_loaders/dynamic_material_loader.hpp" + +#include "util/logger.hpp" +#include "util/for_each.hpp" +#include "util/uix.hpp" +#include + +#include "util/line_parser.hpp" + +namespace obj_loader_error { + +struct category : std::error_category { + [[nodiscard]] const char* name() const noexcept override { + return "connector"; + } + + [[nodiscard]] std::string message(int ev) const override { + switch (static_cast(ev)) { + using enum codes; + case obj_cannot_open_file: + return "Cannot open given obj file."; + case obj_malformed_vertex: + return "File contains malformed 'v' statement."; + case obj_malformed_texture_coordinate: + return "File contains malformed 'vt' statement."; + case obj_malformed_normal: + return "File contains malformed 'vn' statement."; + case obj_malformed_face: + return "File contains malformed 'f' statement."; + case obj_face_index_out_of_range: + return "Face index out of range."; + case obj_unknown_line_begin: + return "Unknown obj line begin."; + default: + using namespace std::string_literals; + return "unrecognized error ("s + std::to_string(ev) + ")"; + } + } +}; + +} // namespace mesh_loader_error + +inline std::error_category& connector_error_category() { + static obj_loader_error::category category; + return category; +} + +namespace obj_loader_error { + +inline std::error_code make_error_code(codes e) { + return { static_cast(e), connector_error_category() }; +} + +} // namespace mesh_loader_error + + +using vertex_type = std::array; + +struct indexed_vertex_type { + vertex_type vertex; + ztu::u32 buffer_index; + + friend auto operator<=>(const indexed_vertex_type& a, const indexed_vertex_type& b) { + return a.vertex <=> b.vertex; + } + + bool operator==(const indexed_vertex_type& other) const noexcept { + return other.vertex == vertex; + } +}; + + +// TODO add compile time selection and unrolling +template +std::errc parse_numeric_vector(std::string_view param, std::array& values) { + auto it = param.begin(), end = param.end(); + + for (auto& value : values) + { + if (it >= end) + { + return std::errc::invalid_argument; + } + + const auto [ptr, ec] = std::from_chars(it, end, value); + + if (ec != std::errc{}) + { + return ec; + } + + it = ptr + 1; // skip space in between components + } + + return {}; +}; + +std::error_code obj_loader::load_directory( + dynamic_data_loader_ctx& ctx, + dynamic_mesh_store& store, + components::mesh_vertex::flags enabled_components, + const std::filesystem::path& path, + const bool pedantic +) { + namespace fs = std::filesystem; + + if (not fs::exists(path)) + { + return make_error_code(std::errc::no_such_file_or_directory); + } + + for (const auto& file : fs::directory_iterator{ path }) + { + const auto& file_path = file.path(); + + if (file_path.extension() != ".obj") + { + continue; + } + + if (const auto e = load( + ctx, + store, + enabled_components, + path, + pedantic + )) { + ztu::logger::error( + "Error while loading obj file '%': [%] %", + file_path, + e.category().name(), + e.message() + ); + } + } + + return {}; +} + + +// TODO refactor so there is a function like parse_normals etc. + +std::error_code obj_loader::load( + dynamic_data_loader_ctx& ctx, + dynamic_mesh_store& store, + components::mesh_vertex::flags enabled_components, + const std::filesystem::path& filename, + const bool pedantic +) { + using obj_loader_error::codes; + using obj_loader_error::make_error_code; + + auto in = std::ifstream{ filename }; + if (not in.is_open()) { + return make_error_code(codes::obj_cannot_open_file); + } + + namespace fs = std::filesystem; + const auto directory = fs::path(filename).parent_path(); + + // Each vertex of a face can represent a unique combination of vertex-/texture-/normal-coordinates. + // But some combinations may occur more than once, for example on every corner of a cube 3 triangles will + // reference the exact same corner vertex. + // To get the best rendering performance and lowest final memory footprint these duplicates + // need to be removed. So this std::set lookup is used to identify the aforementioned duplicates + // and only push unique combinations to the buffers. + std::set vertex_ids; + + auto mesh = dynamic_mesh_data{}; + + // Buffers + auto position_buffer = mesh.positions(); + auto normal_buffer = mesh.normals(); + auto tex_coord_buffer = mesh.tex_coords(); + + std::unordered_map material_name_lookup; + + + constexpr auto mtl_loader_id = *ctx.material_loader.find_loader_static("mtl"); + mtl_loader& material_loader = ctx.material_loader.get_loader(); + + material_loader.clear_name_lookup(); + + std::string material_name; + + const auto push_mesh = [&](const bool clear_buffers = false) { + + if (not mesh.positions().empty()) + { + // Copy buffers instead of moving to keep capacity for further parsing + // and have the final buffers be shrunk to size. + if (not material_name.empty()) { + if (const auto id = material_loader.find_id(material_name)) + { + mesh.material_id() = *id; + } + else + { + ztu::logger::warn( + "Could not find material '%'.", + material_name + ); + } + } + + ztu::logger::debug("Parsed % positions.", mesh.positions().size()); + ztu::logger::debug("Parsed % normals.", mesh.normals().size()); + ztu::logger::debug("Parsed % tex_coords.", mesh.tex_coords().size()); + + if (not mesh.positions().empty()) + { + mesh.components() |= components::mesh_vertex::flags::position; + } + + if (not mesh.normals().empty()) + { + mesh.components() |= components::mesh_vertex::flags::normal; + } + + if (not mesh.tex_coords().empty()) + { + mesh.components() |= components::mesh_vertex::flags::tex_coord; + } + + ztu::logger::debug("Pushing obj mesh with % triangles.", mesh.triangles().size()); + + store.add(std::move(mesh)); + } + + if (clear_buffers) + { + position_buffer.clear(); + normal_buffer.clear(); + tex_coord_buffer.clear(); + } + + mesh = dynamic_mesh_data{}; + + vertex_ids.clear(); + material_name.clear(); + }; + + const auto find_or_push_vertex = [&](const vertex_type& vertex) -> ztu::u32 { + + auto indexed_vid = indexed_vertex_type{ + .vertex = vertex, + .buffer_index = static_cast(mesh.positions().size()) + }; + + // Search through sorted lookup to check if index combination is unique + const auto [ id_it, unique ] = vertex_ids.insert(indexed_vid); + + if (unique) + { + const auto& [ position_index, tex_coord_index, normal_index ] = vertex; + + if (position_index < position_buffer.size()) + { + mesh.positions().emplace_back(position_buffer[position_index]); + } + + if (normal_index < normal_buffer.size()) + { + mesh.normals().emplace_back(normal_buffer[normal_index]); + } + + if (tex_coord_index < tex_coord_buffer.size()) + { + mesh.tex_coords().emplace_back(tex_coord_buffer[tex_coord_index]); + } + } + + return id_it->buffer_index; + }; + + using flags = components::mesh_vertex::flags; + + const auto component_disabled = [&](const flags component) { + return (enabled_components & component) == flags::none; + }; + + const auto positions_disabled = component_disabled(flags::position); + const auto normals_disabled = component_disabled(flags::normal); + const auto tex_coords_disabled = component_disabled(flags::tex_coord); + + const auto ec = ztu::parse_lines( + in, + pedantic, + ztu::make_line_parser("v ", ztu::is_repeating, [&](const auto& param) + { + if (positions_disabled) return codes::ok; + + components::mesh_vertex::position position; + if (parse_numeric_vector(param, position) != std::errc{}) [[unlikely]] + { + return codes::obj_malformed_vertex; + } + + position_buffer.push_back(position); + + return codes::ok; + }), + ztu::make_line_parser("vt ", ztu::is_repeating, [&](const auto& param) { + if (tex_coords_disabled) return codes::ok; + + components::mesh_vertex::tex_coord coord; + if (parse_numeric_vector(param, coord) != std::errc{}) [[unlikely]] + { + return codes::obj_malformed_texture_coordinate; + } + + tex_coord_buffer.push_back(coord); + + return codes::ok; + }), + ztu::make_line_parser("vn ", ztu::is_repeating, [&](const auto& param) + { + if (normals_disabled) return codes::ok; + + components::mesh_vertex::normal normal; + if (parse_numeric_vector(param, normal) != std::errc{}) [[unlikely]] + { + return codes::obj_malformed_normal; + } + + normal_buffer.push_back(normal); + + return codes::ok; + }), + ztu::make_line_parser("o ", ztu::is_not_repeating, [&](const auto&) + { + push_mesh(); // Name is currently ignored + return codes::ok; + }), + ztu::make_line_parser("f ", ztu::is_repeating, [&](const auto& param) + { + const auto begin = param.begin().base(); + const auto end = param.end().base(); + + auto vertex = vertex_type{}; + + ztu::u32 first_index{}, prev_index{}; + + auto vertex_count = std::size_t{}; + + for (auto it = begin; it <= end; ++it) + { + + for (auto& component_index : vertex) + { + if (it != end and *it == '/') + { + ++it; + continue; + } + + const auto [ptr, ec] = std::from_chars(it, end, component_index); + if (ec != std::errc()) [[unlikely]] + { + // Discard whole face if one index is malformed + return codes::obj_malformed_face; + } + + --component_index; // Indices start at one + it = ptr; + + if (it == end or *it != '/') + { + break; + } + + ++it; + } + + ++vertex_count; + + if (it != end and *it != ' ') [[unlikely]] + { + return codes::obj_malformed_face; + } + + const auto curr_index = find_or_push_vertex(vertex); + + if (vertex_count >= 3) + { + auto& triangle = mesh.triangles().emplace_back(); + triangle[0] = first_index; + triangle[1] = prev_index; + triangle[2] = curr_index; + } + else if (vertex_count == 1) + { + first_index = curr_index; + } + + prev_index = curr_index; + } + + return codes::ok; + }), + ztu::make_line_parser("usemtl ", ztu::is_not_repeating, [&](const auto& param) + { + push_mesh(false); + + material_name = param; + + return codes::ok; + }), + ztu::make_line_parser("mtllib ", ztu::is_not_repeating, [&](const auto& param) + { + auto material_filename = fs::path(param); + if (material_filename.is_relative()) + { + material_filename = directory / material_filename; + } + + if (const auto error = ctx.material_loader.read( + ctx, + mtl_loader_id, + material_filename, + pedantic + )) { + ztu::logger::warn( + "Error occurred while loading mtl files '%': [%] %", + material_filename, + error.category().name(), + error.message() + ); + } + }) + ); + + material_loader.clear_name_lookup(); + + if (ec != codes::ok) + { + return make_error_code(ec); + } + + push_mesh(); + + return {}; +} diff --git a/source/assets/data_loaders/stl_loader.cpp b/source/assets/data_loaders/stl_loader.cpp new file mode 100644 index 0000000..ec35eaf --- /dev/null +++ b/source/assets/data_loaders/stl_loader.cpp @@ -0,0 +1,253 @@ +#include "assets/data_loaders/stl_loader.hpp" + +#include "util/binary_ifstream.hpp" +#include "util/unroll_bool_template.hpp" +#include "util/logger.hpp" + +template +std::error_code read_body( + binary_ifstream& in, + const std::uint32_t expected_triangle_count, + std::vector& positions, + std::vector& normals, + std::vector>& triangles +) { + + const auto read_vector = [&in](auto& vector) -> std::error_code + { + for (auto& component : vector) + { + float component32; + if (const auto e = in.read_ieee754(component32)) + { + return e; + } + component = component32; + } + return {}; + }; + + for (std::uint32_t i{}; i != expected_triangle_count; ++i) { + + auto normal = components::mesh_vertex::normal{}; + if constexpr (Normals) + { + if (const auto e = read_vector(normal)) + { + return e; + } + } + + auto triangle = std::array{}; + + for (auto& index : triangle) { + + auto position = components::mesh_vertex::position{}; + if (const auto e = read_vector(position)) + { + return e; + } + + // TODO implement unique insert correctly + /*// Insert vertices sorted, to efficiently remove duplicates. + const auto it = std::ranges::upper_bound(positions, position); + + // Set index before `it` is invalidated by insert. + index = it - positions.begin(); + + if (it != positions.begin() and *std::prev(it) == position) + { + --index; + } + else + { + positions.insert(it, position); + if constexpr (Normals) + { + normals.insert(normals.begin() + index, normal); + } + }*/ + + index = positions.size(); + positions.push_back(position); + + if constexpr (Normals) + { + normals.push_back(normal); + } + } + + triangles.push_back(triangle); + + // Skip attribute bytes + if (const auto e = in.skip()) + { + return e; + } + } + + return {}; +} + +std::error_code stl_loader::read_directory( + const std::filesystem::path& path, + std::vector& meshes, + components::mesh_vertex::flags enabled_components::mesh_vertexs, + std::vector& materials, + material_component::flags enabled_material_components, + const ztu::u32 base_material_id, + bool pedantic +) { + namespace fs = std::filesystem; + + if (not fs::exists(path)) { + return make_error_code(std::errc::no_such_file_or_directory); + } + + for (const auto& file : fs::directory_iterator{ path / "frames" }) + { + const auto& file_path = file.path(); + + if (file_path.extension() != ".stl") + { + continue; + } + + if (const auto e = read( + file_path, + meshes, + enabled_components::mesh_vertexs, + materials, + enabled_material_components, + base_material_id, + pedantic + )) { + ztu::logger::error( + "Error while loading stl file '%': [%] %", + file_path, + e.category().name(), + e.message() + ); + } + } + + return {}; +} + + +std::error_code stl_loader::read( + const std::filesystem::path& filename, + std::vector& meshes, + components::mesh_vertex::flags enabled_components::mesh_vertexs, + std::vector&, + material_component::flags, + ztu::u32, + const bool pedantic +) { + auto error = std::error_code{}; + + auto in = binary_ifstream{}; + + if ((error = in.open(filename, true))) + { + return error; + } + + auto header_bytes_left = static_cast(80); + + if (pedantic) + { + // Check if ASCII file was provided, these start with a specific character sequence. + + static constexpr auto ascii_magic_string = std::string_view("solid"); + + auto magic_bytes = std::array{}; + + if ((error = in.read(magic_bytes))) + { + return error; + } + + const auto magic_string = std::string_view( + reinterpret_cast(magic_bytes.data()), + magic_bytes.size() + ); + + if (magic_string == ascii_magic_string) + { + return std::make_error_code(std::errc::illegal_byte_sequence); + } + + header_bytes_left -= ascii_magic_string.size(); + } + + // Ignore (rest of) header. + if ((error = in.skip(header_bytes_left))) + { + return error; + } + + // Read number of bytes + auto expected_triangle_count = std::uint32_t{}; + + if ((error = in.read(expected_triangle_count))) + { + return error; + } + + // Use separate mesh for parsing, so original mesh is only overwritten + // if no errors occurred. This also guarantees unused reserved memory + // is freed immediately in case of an error. + auto mesh = dynamic_mesh_data{}; + + auto& positions = mesh.positions(); + auto& normals = mesh.normals(); + auto& triangles = mesh.triangles(); + auto& material_id = mesh.material_id(); + + material_id = 0; // Set to default material + + positions.reserve(expected_triangle_count * 3); + normals.reserve(expected_triangle_count); + triangles.reserve(expected_triangle_count); + + const auto normals_enabled = ( + (enabled_components::mesh_vertexs & components::mesh_vertex::flags::normal) != components::mesh_vertex::flags::none + ); + + error = unroll_bool_function_template([&]() { + return read_body( + in,expected_triangle_count, + positions, + normals, + triangles + ); + }, normals_enabled); + + // Free any unused reserved memory + positions.shrink_to_fit(); + normals.shrink_to_fit(); + triangles.shrink_to_fit(); + + if (error) + { + return error; + } + + ztu::logger::debug("Normal count: %", normals.size()); + + if (not positions.empty()) + { + mesh.components() |= components::mesh_vertex::flags::position; + } + + if (not normals.empty()) + { + ztu::logger::debug("Enabling normals!!!"); + mesh.components() |= components::mesh_vertex::flags::normal; + } + + meshes.emplace_back(std::move(mesh)); + + return {}; +} \ No newline at end of file diff --git a/source/assets/data_loaders/threedtk_pose_loader.cpp b/source/assets/data_loaders/threedtk_pose_loader.cpp new file mode 100644 index 0000000..724acb0 --- /dev/null +++ b/source/assets/data_loaders/threedtk_pose_loader.cpp @@ -0,0 +1,136 @@ +#include "assets/data_loaders/threedtk_pose_loader.hpp" + +#include "assets/dynamic_read_buffers/dynamic_pose_buffer.hpp" + +#include +#include +#include + +#include "util/logger.hpp" + + + +inline std::error_code threedtk_pose_loader::parse_transform_info( + std::ifstream& in, + std::string& line, + std::array& transform_info +) { + + for (std::size_t row{}; row != 2; ++row) { + + std::getline(in, line); + + auto it = line.cbegin().base(); + auto end = line.cend().base(); + + for (glm::vec3::length_type col{}; col != 3; ++col) { + + const auto [ ptr, ec ] = std::from_chars( + it, end, + transform_info[row][col], + std::chars_format::general + ); + + if (ec != std::errc{}) { + return std::make_error_code(ec); + } + + it = ptr + 1; // skip space in between components + } + } +} + +inline ztu::result threedtk_pose_loader::parse_index( + const std::string_view filename +) { + static constexpr auto prefix = std::string_view{ "scan" }; + + auto name_view = filename.substr(0, name_view.find('.')); + + if (name_view.length() <= prefix.length()) [[unlikely]] + { + return std::make_error_code(std::errc::invalid_argument); + } + + name_view = name_view.substr(prefix.length()); + + pose_prefetch_lookup::index_type index; + + const auto res = std::from_chars(name_view.begin(), name_view.end(), index); + + if (res.ec != std::errc{}) [[unlikely]] + { + return std::make_error_code(res.ec); + } + + return index; +} + + +void threedtk_pose_loader::load( + const ztu::string_list& filenames, + dynamic_pose_store& store, + pose_prefetch_lookup& id_lookup +) { + + auto filename_buffer = std::string{}; + auto in = std::ifstream{}; + auto line = std::string{}; + auto pose_buffer = dynamic_pose_buffer{}; + + for (const auto filename : filenames) + { + + pose_prefetch_lookup::index_type index; + if (const auto res = parse_index(filename)) + { + index = *res; + } + else + { + const auto error = res.error(); + ztu::logger::error( + "Error while parsing 3dtk pose file index %: [%] %", + filename, + error.category().name(), + error.message() + ); + } + + filename_buffer = filename; + in.open(filename_buffer.c_str()); + if (not in.is_open()) { + ztu::logger::error("Cannot open 3dtk pose file %", filename); + continue; + } + + std::array transform_info{}; + + const auto error = parse_transform_info(in, line, transform_info); + + in.close(); + + if (error) + { + ztu::logger::error( + "Error while parsing 3dtk pose file %: [%] %", + filename, + error.category().name(), + error.message() + ); + continue; + } + + const auto& translation = transform_info[0]; + auto& angles = transform_info[1]; + angles *= static_cast(M_PI / 180.0); + + pose_buffer = ( + glm::translate(glm::identity(), translation) * + glm::eulerAngleXYZ(angles[0], angles[1], angles[2]) + ); + + const auto id = store.add(pose_buffer); + id_lookup.emplace(filename, index, id); + } +} diff --git a/source/assets/dynamic_data_loaders/dynamic_mesh_loader.cpp b/source/assets/dynamic_data_loaders/dynamic_mesh_loader.cpp new file mode 100644 index 0000000..32641aa --- /dev/null +++ b/source/assets/dynamic_data_loaders/dynamic_mesh_loader.cpp @@ -0,0 +1,41 @@ +#include "assets/dynamic_data_loaders/dynamic_mesh_loader.hpp" + +std::error_code dynamic_mesh_loader::prefetch( + loader_id_type loader_id, + const ztu::string_list& directories, + prefetch_queue& queue +) { + return this->invoke_with_matching_loader( + loader_id, + [&](auto& loader) + { + return loader.prefetch( + directories, + queue + ); + } + ); +} + +std::error_code dynamic_mesh_loader::load( + loader_id_type loader_id, + const ztu::string_list& directories, + dynamic_mesh_store& store, + mesh_prefetch_lookup& id_lookup, + const bool pedantic +) { + return this->invoke_with_matching_loader( + loader_id, + [&](auto& loader) + { + return loader.load( + m_buffer, + directories, + store, + id_lookup, + pedantic + ); + } + ); +} + diff --git a/source/assets/dynamic_data_loaders/dynamic_point_cloud_loader.cpp b/source/assets/dynamic_data_loaders/dynamic_point_cloud_loader.cpp new file mode 100644 index 0000000..ceadafb --- /dev/null +++ b/source/assets/dynamic_data_loaders/dynamic_point_cloud_loader.cpp @@ -0,0 +1,40 @@ +#include "assets/dynamic_data_loaders/dynamic_point_cloud_loader.hpp" + +std::error_code dynamic_point_cloud_loader::prefetch( + const loader_id_type loader_id, + const ztu::string_list& directories, + prefetch_queue& queue +) { + return this->invoke_with_matching_loader( + loader_id, + [&](auto& loader) + { + return loader.prefetch( + directories, + queue + ); + } + ); +} + +std::error_code dynamic_point_cloud_loader::load( + const loader_id_type loader_id, + const ztu::string_list& directories, + dynamic_point_cloud_store& store, + point_cloud_prefetch_lookup& id_lookup, + const bool pedantic +) { + return this->invoke_with_matching_loader( + loader_id, + [&](auto& loader) + { + return loader.load( + m_buffer, + directories, + store, + id_lookup, + pedantic + ); + } + ); +} \ No newline at end of file diff --git a/source/assets/dynamic_data_loaders/dynamic_texture_loader.cpp b/source/assets/dynamic_data_loaders/dynamic_texture_loader.cpp new file mode 100644 index 0000000..a001239 --- /dev/null +++ b/source/assets/dynamic_data_loaders/dynamic_texture_loader.cpp @@ -0,0 +1,122 @@ +#include "assets/dynamic_data_loaders/dynamic_texture_loader.hpp" + +#if defined(__GNUC__) || defined(__GNUG__) +#pragma GCC diagnostic push +#pragma GCC system_header +#elif defined(_MSC_VER) +#pragma warning(push, 0) +#endif + +#define STB_IMAGE_IMPLEMENTATION +#define STB_IMAGE_STATIC +#include "stb_image.h" + +#if defined(__GNUC__) || defined(__GNUG__) +#pragma GCC diagnostic pop +#elif defined(_MSC_VER) +#pragma warning(pop) +#endif + +#include "util/logger.hpp" + +dynamic_texture_loader::dynamic_texture_loader(components::texture::flags enabled_components) : + m_enabled_components{ enabled_components }, + m_loader_id_lookup{ + { "jpg", loader_id_type{ 0 } }, + { "png", loader_id_type{ 1 } }, + { "tga", loader_id_type{ 2 } }, + { "bmp", loader_id_type{ 3 } }, + { "psd", loader_id_type{ 4 } }, + { "gif", loader_id_type{ 5 } }, + { "hdr", loader_id_type{ 6 } }, + { "pic", loader_id_type{ 7 } } + } {} + +std::optional dynamic_texture_loader::find_loader(const std::string_view& name) +{ + const auto it = m_loader_id_lookup.find(name); + + if (it != m_loader_id_lookup.end()) + { + return it->second; + } + + return std::nullopt; +} + +std::error_code dynamic_texture_loader::prefetch( + const loader_id_type loader_id, + const ztu::string_list& directories, + prefetch_queue& queue +) { + // Nothing to prefetch... + return {}; +} + +std::error_code dynamic_texture_loader::load( + const loader_id_type loader_id, + const ztu::string_list& directories, + dynamic_texture_store& store, + texture_prefetch_lookup& id_lookup, + const bool pedantic +) { + stbi_set_flip_vertically_on_load(true); + + int width, height, channels; + + for (const auto filename : directories) + { + const auto id_it = id_lookup.find(filename); + + if (id_it != id_lookup.end()) [[unlikely]] + { + continue; + } + + auto ptr = reinterpret_cast(stbi_load( + filename.data(), // Null terminated by string_list + &width, + &height, + &channels, + 0 + )); + + if (ptr == nullptr) { + return std::make_error_code(std::errc::no_such_file_or_directory); + } + + auto data = std::unique_ptr(ptr); + + using flags = components::texture::flags; + + auto components = flags{}; + switch (channels) + { + case 1: + components = flags::luminance; + break; + case 2: + components = flags::luminance | flags::alpha; + break; + case 3: + components = flags::red | flags::green | flags::blue; + break; + case 4: + components = flags::red | flags::green | flags::blue | flags::alpha; + break; + default: [[unlikely]] + ztu::logger::error("Unsupported pixel component composition %", static_cast(components)); + continue; + } + + const auto id = store.add(dynamic_texture_buffer( + std::move(data), + width, + height, + components + )); + id_lookup.emplace_hint(id_it, filename, id); + } + + return {}; +} diff --git a/source/assets/dynamic_data_loaders/generic/base_dynamic_loader.ipp b/source/assets/dynamic_data_loaders/generic/base_dynamic_loader.ipp new file mode 100644 index 0000000..c3835e8 --- /dev/null +++ b/source/assets/dynamic_data_loaders/generic/base_dynamic_loader.ipp @@ -0,0 +1,92 @@ +#ifndef INCLUDE_BASE_DYNAMIC_LOADER_IMPLEMENTATION +# error Never include this file directly include 'base_dynamic_loader.hpp' +#endif + +#include "util/for_each.hpp" + +template +base_dynamic_loader::base_dynamic_loader(const C enabled_components) : + m_enabled_components{ enabled_components } +{ + [&](std::index_sequence) { + m_loader_id_lookup = { { std::string{ Loaders::name }, { Is } }... }; + }(std::index_sequence_for()); +} + +template +std::optional::loader_id_type> base_dynamic_loader::find_loader( + std::string_view name +) { + const auto it = m_loader_id_lookup.find(name); + + if (it != m_loader_id_lookup.end()) + { + return it->second; + } + + return std::nullopt; +} + +template +consteval std::optional::loader_id_type> base_dynamic_loader::find_loader_static( + std::string_view name +) { + constexpr auto invalid_index = std::numeric_limits::max(); + + auto index = invalid_index; + ztu::for_each::indexed_type([&]() + { + if (name == Loader::name) + { + index = Index; + return true; + } + return false; + }); + + return index == invalid_index ? std::nullopt : loader_id_type{ index }; +} + +template +template::loader_id_type ID> +auto& base_dynamic_loader::get_loader() +{ + return std::get(m_loaders); +} + +template +template +ztu::result base_dynamic_loader::invoke_with_matching_loader( + const loader_id_type loader_id, F&& f +) { + return std::apply( + [&](Loaders&... loaders) + { + return [&](std::index_sequence) + { + std::error_code error; + + const auto found_parser = ( + [&](auto& loader, const std::size_t index) + { + if (loader_id == index) + { + error = f(loader); + return true; + } + return false; + } (loaders, Is) + or ... + ); + + if (not found_parser) + { + error = std::make_error_code(std::errc::invalid_argument); + } + + return error; + }(std::index_sequence_for()); + }, + m_loaders + ); +} diff --git a/source/assets/dynamic_data_stores/dynamic_material_store.cpp b/source/assets/dynamic_data_stores/dynamic_material_store.cpp new file mode 100644 index 0000000..70ce9ee --- /dev/null +++ b/source/assets/dynamic_data_stores/dynamic_material_store.cpp @@ -0,0 +1,26 @@ +#include "assets/dynamic_data_stores/dynamic_material_store.hpp" + +dynamic_material_store::id_type dynamic_material_store::add(const dynamic_material_buffer& buffer) +{ + return m_store.add(buffer.data); +} + +std::pair dynamic_material_store::find(const id_type id) +{ + return m_store.find(id); +} + +std::pair dynamic_material_store::find(const id_type id) const +{ + return m_store.find(id); +} + +void dynamic_material_store::remove(const iterator_type& it) +{ + m_store.remove(it); +} + +void dynamic_material_store::clear() +{ + m_store.clear(); +} diff --git a/source/assets/dynamic_data_stores/dynamic_mesh_store.cpp b/source/assets/dynamic_data_stores/dynamic_mesh_store.cpp new file mode 100644 index 0000000..6eb6c3d --- /dev/null +++ b/source/assets/dynamic_data_stores/dynamic_mesh_store.cpp @@ -0,0 +1,31 @@ +#include "assets/dynamic_data_stores/dynamic_mesh_store.hpp" + + +dynamic_mesh_store::id_type dynamic_mesh_store::add(const dynamic_mesh_buffer& mesh_buffer) +{ + const auto& triangles = mesh_buffer.triangles(); + return m_store.add( + std::span{ triangles.front().data(), triangles.size() * 3 }, + mesh_buffer.vertices + ); +} + +std::pair dynamic_mesh_store::find(id_type id) +{ + return m_store.find(id); +} + +std::pair dynamic_mesh_store::find(id_type id) const +{ + return m_store.find(id); +} + +void dynamic_mesh_store::remove(const iterator_type& it) +{ + m_store.remove(it); +} + +void dynamic_mesh_store::clear() +{ + m_store.clear(); +} diff --git a/source/assets/dynamic_data_stores/dynamic_point_cloud_store.cpp b/source/assets/dynamic_data_stores/dynamic_point_cloud_store.cpp new file mode 100644 index 0000000..25860b2 --- /dev/null +++ b/source/assets/dynamic_data_stores/dynamic_point_cloud_store.cpp @@ -0,0 +1,27 @@ +#include "assets/dynamic_data_stores/dynamic_point_cloud_store.hpp" + + +dynamic_point_cloud_store::id_type dynamic_point_cloud_store::add(const dynamic_point_cloud_buffer& mesh_buffer) +{ + return m_store.add(mesh_buffer.vertices); +} + +std::pair dynamic_point_cloud_store::find(id_type id) +{ + return m_store.find(id); +} + +std::pair dynamic_point_cloud_store::find(id_type id) const +{ + return m_store.find(id); +} + +void dynamic_point_cloud_store::remove(const iterator_type& it) +{ + m_store.remove(it); +} + +void dynamic_point_cloud_store::clear() +{ + m_store.clear(); +} diff --git a/source/assets/dynamic_data_stores/generic/generic_dynamic_component_array_store.ipp b/source/assets/dynamic_data_stores/generic/generic_dynamic_component_array_store.ipp new file mode 100644 index 0000000..bc83c1c --- /dev/null +++ b/source/assets/dynamic_data_stores/generic/generic_dynamic_component_array_store.ipp @@ -0,0 +1,387 @@ +#ifndef INCLUDE_GENERIC_DYNAMIC_COMPONENT_ARRAY_STORE_IMPLEMENTATION +# error Never include this file directly include 'generic_dynamic_component_array_store.hpp' +#endif +#include + +template +component_array_iterator::component_array_iterator( + component_array_pointer_type components, + flag_count_pointer_type flags, + std::size_t index, + const offsets_type& offsets +) : + m_components{ components }, + m_flag_counts{ flags }, + m_index{ index }, + m_offsets{ offsets } {} + + +template +typename component_array_iterator::reference component_array_iterator::operator*() const { + return dereference(std::index_sequence_for{}); +} + +template +component_array_iterator& component_array_iterator::operator++() { + adjust_offsets(std::index_sequence_for{}, 1); + ++m_index; + return *this; +} + +template +component_array_iterator component_array_iterator::operator++(int) { + component_array_iterator tmp = *this; + ++(*this); + return tmp; +} + +template +component_array_iterator& component_array_iterator::operator--() { + adjust_offsets(std::index_sequence_for{}, -1); + --m_index; + return *this; +} + +template +component_array_iterator component_array_iterator::operator--(int) { + auto tmp = *this; + --(*this); + return tmp; +} + +template +component_array_iterator& component_array_iterator::operator+=(const difference_type n) +{ + adjust_offsets(std::index_sequence_for{}, n); + m_index += n; + return *this; +} + +template +component_array_iterator& component_array_iterator::operator-=(const difference_type n) +{ + return (*this) += -n; +} + +template +component_array_iterator component_array_iterator::operator+(const difference_type n) const +{ + auto tmp = *this; + return tmp += n; +} + +template +component_array_iterator component_array_iterator::operator-(const difference_type n) const +{ + auto tmp = *this; + return tmp -= n; +} + +template +typename component_array_iterator::difference_type +component_array_iterator::operator-(const component_array_iterator& other) const +{ + return static_cast(m_index) - static_cast(other.m_index); +} + +template +typename component_array_iterator::reference component_array_iterator::operator[]( + const difference_type n +) const { + return *((*this) + n); +} + +template +bool component_array_iterator::operator==(const component_array_iterator& other) const +{ + return m_components == other.m_components and m_index == other.m_index; +} + +template +bool component_array_iterator::operator!=(const component_array_iterator& other) const +{ + return not (*this == other); +} + +template +bool component_array_iterator::operator<(const component_array_iterator& other) const +{ + return m_index < other.m_index; +} + +template +bool component_array_iterator::operator<=(const component_array_iterator& other) const +{ + return m_index <= other.m_index; +} + +template +bool component_array_iterator::operator>(const component_array_iterator& other) const +{ + return m_index > other.m_index; +} + +template +bool component_array_iterator::operator>=(const component_array_iterator& other) const +{ + return m_index >= other.m_index; +} + +template +template +bool component_array_iterator::is_component_enabled(C flag) +{ + return (flag & (C{1} << I)) != C{}; +} + +template +template +void component_array_iterator::calc_offsets(std::index_sequence, difference_type n) +{ + + const auto negative = n < difference_type{ 0 }; + const auto positive = n > difference_type{ 0 }; + const auto step = difference_type{ positive } - difference_type{ negative }; + n = negative ? -n : n; + + // TODO template optimize for single steps + + while (n--) + { + const auto& [ flags, count ] = m_flag_counts[m_index + n]; + + ([&] { + if (is_component_enabled(flags)) { + std::get(m_offsets) += step * count; + } + }(), ...); + + m_index += step; + } +} + +template +template +typename component_array_iterator::reference +component_array_iterator::dereference(std::index_sequence) const +{ + return std::make_tuple(get_span()...); +} + +template +template +std::tuple_element_t::value_type> +component_array_iterator::get_span() const +{ + const auto& [ flags, count ] = m_flag_counts[m_index]; + + if (is_component_enabled(flags)) + { + return { &std::get(m_components)[m_offsets[N]], count }; + } + return nullptr; +} + + +template +std::tuple...> generic_dynamic_component_array_store::data_ptrs() +{ + return [&](std::index_sequence) + { + return std::make_tuple(std::get(m_component_arrays).data()...); + } + (std::index_sequence_for{}); +} + +template +std::tuple>...> generic_dynamic_component_array_store::data_ptrs() const +{ + return [&](std::index_sequence) + { + return std::make_tuple(std::get(m_component_arrays).data()...); + } + (std::index_sequence_for{}); +} + +template +std::array generic_dynamic_component_array_store::data_counts() const +{ + return [&](std::index_sequence) + { + return std::array{ std::get(m_component_arrays).size()... }; + } + (std::index_sequence_for{}); +} + +template +typename generic_dynamic_component_array_store::id_type generic_dynamic_component_array_store::add( + const std::tuple...>& component_arrays +) { + + auto component_flags = C{}; + auto count = count_type{}; + + [&](std::integer_sequence) + { + const auto& array = std::get(component_arrays); + if (not array.empty()) + { + const auto array_count = static_cast(array.size()); + count = count ? std::min(array_count, count) : array_count; + component_flags |= C{ 1 } << Is; + } + } + (std::index_sequence_for{}); + + + [&](std::integer_sequence) + { + const auto& src_array = std::get(component_arrays); + auto& dst_array = std::get(m_component_arrays); + + if (not src_array.empty()) + { + dst_array.insert(dst_array.end(), src_array.begin(), src_array.begin() + count); + } + } + (std::index_sequence_for{}); + + m_component_flag_counts.emplace_back(component_flags, count); + + const auto id = id_type{ m_next_data_id.index++ }; + m_ids.push_back(id); + + return id; +} + +template +std::pair::iterator_type, bool> generic_dynamic_component_array_store::find(id_type id) +{ + const auto id_it = std::ranges::upper_bound(m_ids, id); + + const auto match = ( + id_it != m_ids.begin() and + *std::prev(id_it) == id + ); + + const auto index = id_it - m_ids.begin() - match; + + auto it = begin(); + it += index; + + return { it, match }; +} + +template +std::pair::const_iterator, bool> generic_dynamic_component_array_store::find(id_type id) const +{ + const auto id_it = std::ranges::upper_bound(m_ids, id); + + const auto match = ( + id_it != m_ids.begin() and + *std::prev(id_it) == id + ); + + const auto index = id_it - m_ids.begin() - match; + + auto it = begin(); + it += index; + + return { it, match }; +} + + +template +void generic_dynamic_component_array_store::remove(const iterator_type& it) +{ + [&](std::index_sequence) + { + ([&]{ + auto& component_vector = std::get(m_component_arrays); + const auto begin = component_vector.begin() + it.m_offsets[Is]; + const auto end = begin + it.m_flag_counts[it.m_index]; + component_vector.erase(begin, end); + }(), ...); + } (std::index_sequence_for{}); + + m_component_flag_counts.erase(m_component_flag_counts.begin() + it.m_index); + m_ids.erase(m_ids.begin() + it.m_index); +} + +template +void generic_dynamic_component_array_store::clear() +{ + [&](std::index_sequence) + { + std::get(m_component_arrays).clear(); + } (std::index_sequence_for{}); + m_component_flag_counts.clear(); + m_ids.clear(); +} + +template +typename generic_dynamic_component_array_store::iterator_type generic_dynamic_component_array_store::begin() +{ + return iterator_type{ + data_ptrs(), + m_component_flag_counts.data(), + 0, + {} + }; +} + +template +typename generic_dynamic_component_array_store::iterator_type generic_dynamic_component_array_store::end() +{ + return iterator_type{ + data_ptrs(), + m_component_flag_counts.data(), + m_component_flag_counts.size(), + data_counts() + }; +} + +template +typename generic_dynamic_component_array_store::const_iterator generic_dynamic_component_array_store::begin() const +{ + return iterator_type{ + data_ptrs(), + m_component_flag_counts.data(), + 0, + {} + }; +} + +template +typename generic_dynamic_component_array_store::const_iterator generic_dynamic_component_array_store::end() const +{ + return iterator_type{ + data_ptrs(), + m_component_flag_counts.data(), + m_component_flag_counts.size(), + data_counts() + }; +} + +template +typename generic_dynamic_component_array_store::const_iterator generic_dynamic_component_array_store::cbegin() const +{ + return const_cast(this)->begin(); +} + +template +typename generic_dynamic_component_array_store::const_iterator generic_dynamic_component_array_store::cend() const +{ + return const_cast(this)->end(); +} + +template +typename generic_dynamic_component_array_store::view_type generic_dynamic_component_array_store::view() +{ + return { begin(), end() }; +} +template +typename generic_dynamic_component_array_store::const_view_type generic_dynamic_component_array_store::view() const +{ + return { begin(), end() }; +} diff --git a/source/assets/dynamic_data_stores/generic/generic_dynamic_component_store.ipp b/source/assets/dynamic_data_stores/generic/generic_dynamic_component_store.ipp new file mode 100644 index 0000000..05447cd --- /dev/null +++ b/source/assets/dynamic_data_stores/generic/generic_dynamic_component_store.ipp @@ -0,0 +1,373 @@ +#ifndef INCLUDE_GENERIC_DYNAMIC_COMPONENT_STORE_IMPLEMENTATION +# error Never include this file directly include 'basic_dynamic_component_store.hpp' +#endif + +#pragma once + +#include +#include + +#include "util/uix.hpp" +#include "util/id_type.hpp" + +#include +#include +#include +#include + +template +component_iterator::component_iterator( + const value_type components, + const C* flags, + std::size_t index, + const offsets_type& offsets +) : m_components{ components }, m_flags{ flags }, m_index{ index }, m_offsets{ offsets } {} + + +template +typename component_iterator::reference component_iterator::operator*() const { + return dereference(std::index_sequence_for{}); +} + +template +component_iterator& component_iterator::operator++() { + adjust_offsets(std::index_sequence_for{}, 1); + ++m_index; + return *this; +} + +template +component_iterator component_iterator::operator++(int) { + component_iterator tmp = *this; + ++(*this); + return tmp; +} + +template +component_iterator& component_iterator::operator--() { + adjust_offsets(std::index_sequence_for{}, -1); + --m_index; + return *this; +} + +template +component_iterator component_iterator::operator--(int) { + auto tmp = *this; + --(*this); + return tmp; +} + +template +component_iterator& component_iterator::operator+=(const difference_type n) +{ + adjust_offsets(std::index_sequence_for{}, n); + m_index += n; + return *this; +} + +template +component_iterator& component_iterator::operator-=(const difference_type n) +{ + return (*this) += -n; +} + +template +component_iterator component_iterator::operator+(const difference_type n) const +{ + auto tmp = *this; + return tmp += n; +} + +template +component_iterator component_iterator::operator-(const difference_type n) const +{ + auto tmp = *this; + return tmp -= n; +} + +template +typename component_iterator::difference_type +component_iterator::operator-(const component_iterator& other) const +{ + return static_cast(m_index) - static_cast(other.m_index); +} + +template +typename component_iterator::reference component_iterator::operator[]( + const difference_type n +) const { + return *((*this) + n); +} + +template +bool component_iterator::operator==(const component_iterator& other) const +{ + return m_components == other.m_components and m_index == other.m_index; +} + +template +bool component_iterator::operator!=(const component_iterator& other) const +{ + return not (*this == other); +} + +template +bool component_iterator::operator<(const component_iterator& other) const +{ + return m_index < other.m_index; +} + +template +bool component_iterator::operator<=(const component_iterator& other) const +{ + return m_index <= other.m_index; +} + +template +bool component_iterator::operator>(const component_iterator& other) const +{ + return m_index > other.m_index; +} + +template +bool component_iterator::operator>=(const component_iterator& other) const +{ + return m_index >= other.m_index; +} + +template +template +bool component_iterator::is_component_enabled(C flag) +{ + return (flag & (C{1} << I)) != C{}; +} + +template +template +void component_iterator::calc_offsets(std::index_sequence, difference_type n) +{ + + const auto negative = n < difference_type{ 0 }; + const auto positive = n > difference_type{ 0 }; + const auto step = difference_type{ positive } - difference_type{ negative }; + n = negative ? -n : n; + + // TODO template optimize for single steps + + while (n--) + { + const C& flag = m_flags[m_index + n]; + + ([&] { + if (is_component_enabled(flag)) { + std::get(m_offsets) += step; + } + }(), ...); + + m_index += step; + } +} + +template +template +typename component_iterator::reference +component_iterator::dereference(std::index_sequence) const +{ + return std::make_tuple(get_pointer()...); +} + +template +template +std::tuple_element_t::value_type> +component_iterator::get_pointer() const +{ + if (is_component_enabled(m_flags[m_index])) + { + return &std::get(m_components)[m_offsets[N]]; + } + return nullptr; +} + + +template +std::tuple...> component_iterator::data_ptrs() +{ + return [&](std::index_sequence) + { + return std::make_tuple(std::get(m_components).data()...); + } + (std::index_sequence_for{}); +} + +template +std::tuple>...> component_iterator::data_ptrs() const +{ + return [&](std::index_sequence) + { + return std::make_tuple(std::get(m_components).data()...); + } + (std::index_sequence_for{}); +} + +template +std::array component_iterator::data_counts() const +{ + return [&](std::index_sequence) + { + return std::array{ std::get(m_components).size()... }; + } + (std::index_sequence_for{}); +} + +template +typename generic_dynamic_component_store::id_type generic_dynamic_component_store::add( + const std::tuple...>& data +) { + + auto component_flags = C{}; + + [&](std::integer_sequence) + { + if (const auto& component_opt = std::get(data)) + { + std::get(m_components).push_back(*component_opt); + component_flags |= C{ 1 } << Is; + } + } + (std::index_sequence_for{}); + + m_component_flags.push_back(component_flags); + + const auto id = id_type{ m_next_data_id.index++ }; + m_ids.push_back(id); + + return id; +} + +template +std::pair::iterator_type, bool> generic_dynamic_component_store::find(id_type id) +{ + const auto id_it = std::ranges::upper_bound(m_ids, id); + + const auto match = ( + id_it != m_ids.begin() and + *std::prev(id_it) == id + ); + + const auto index = id_it - m_ids.begin() - match; + + auto it = begin(); + it += index; + + return { it, match }; +} + +template +std::pair::const_iterator, bool> generic_dynamic_component_store::find(id_type id) const +{ + const auto id_it = std::ranges::upper_bound(m_ids, id); + + const auto match = ( + id_it != m_ids.begin() and + *std::prev(id_it) == id + ); + + const auto index = id_it - m_ids.begin() - match; + + auto it = begin(); + it += index; + + return { it, match }; +} + + +template +void generic_dynamic_component_store::remove(const iterator_type& it) +{ + return [&](std::index_sequence) + { + auto& component_vector = std::get(m_components); + (component_vector.erase(component_vector.begin() + it.m_offsets[Is]), ...); + } (std::index_sequence_for{}); + + m_component_flags.erase(m_component_flags.begin() + it.m_index); + m_ids.erase(m_ids.begin() + it.m_index); +} + +template +void generic_dynamic_component_store::clear() +{ + return [&](std::index_sequence) + { + std::get(m_component_counts).clear(); + } (std::index_sequence_for{}); + m_component_flags.clear(); + m_ids.clear(); +} + +template +generic_dynamic_component_store::iterator_type generic_dynamic_component_store::begin() +{ + return iterator_type{ + data_ptrs(), + m_component_flags.data(), + 0, + {} + }; +} + +template +generic_dynamic_component_store::iterator_type generic_dynamic_component_store::end() +{ + return iterator_type{ + data_ptrs(), + m_component_flags.data(), + m_component_flags.size(), + data_counts() + }; +} + +template +generic_dynamic_component_store::const_iterator generic_dynamic_component_store::begin() const +{ + return iterator_type{ + data_ptrs(), + m_component_flags.data(), + 0, + {} + }; +} + +template +generic_dynamic_component_store::const_iterator generic_dynamic_component_store::end() const +{ + return iterator_type{ + data_ptrs(), + m_component_flags.data(), + m_component_flags.size(), + data_counts() + }; +} + +template +generic_dynamic_component_store::const_iterator generic_dynamic_component_store::cbegin() const +{ + return const_cast(this)->begin(); +} + +template +generic_dynamic_component_store::const_iterator generic_dynamic_component_store::cend() const +{ + return const_cast(this)->end(); +} + +template +generic_dynamic_component_store::view_type generic_dynamic_component_store::view() +{ + return { begin(), end() }; +} +template +generic_dynamic_component_store::const_view_type generic_dynamic_component_store::view() const +{ + return { begin(), end() }; +} diff --git a/source/assets/dynamic_data_stores/generic/generic_dynamic_indexed_component_array_store.ipp b/source/assets/dynamic_data_stores/generic/generic_dynamic_indexed_component_array_store.ipp new file mode 100644 index 0000000..dd4f067 --- /dev/null +++ b/source/assets/dynamic_data_stores/generic/generic_dynamic_indexed_component_array_store.ipp @@ -0,0 +1,418 @@ +#ifndef INCLUDE_GENERIC_DYNAMIC_INDEXED_COMPONENT_ARRAY_STORE_IMPLEMENTATION +# error Never include this file directly include 'generic_dynamic_indexed_component_array_store.hpp' +#endif + +#pragma once + +#include +#include +#include +#include +#include +#include + +template +indexed_component_array_iterator::indexed_component_array_iterator( + const index_array_pointer_type indices, + const component_array_pointer_type& components, + const flag_count_pointer_type flag_counts, + std::size_t index, + const offsets_type& offsets +) : + m_indices{ indices }, + m_components{ components }, + m_flag_counts{ flag_counts }, + m_index{ index }, + m_offsets{ offsets } {} + + +template +typename indexed_component_array_iterator::reference indexed_component_array_iterator::operator*() const { + return dereference(std::index_sequence_for{}); +} + +template +indexed_component_array_iterator& indexed_component_array_iterator::operator++() { + adjust_offsets(std::index_sequence_for{}, 1); + ++m_index; + return *this; +} + +template +indexed_component_array_iterator indexed_component_array_iterator::operator++(int) { + indexed_component_array_iterator tmp = *this; + ++(*this); + return tmp; +} + +template +indexed_component_array_iterator& indexed_component_array_iterator::operator--() { + adjust_offsets(std::index_sequence_for{}, -1); + --m_index; + return *this; +} + +template +indexed_component_array_iterator indexed_component_array_iterator::operator--(int) { + auto tmp = *this; + --(*this); + return tmp; +} + +template +indexed_component_array_iterator& indexed_component_array_iterator::operator+=(const difference_type n) +{ + adjust_offsets(std::index_sequence_for{}, n); + m_index += n; + return *this; +} + +template +indexed_component_array_iterator& indexed_component_array_iterator::operator-=(const difference_type n) +{ + return (*this) += -n; +} + +template +indexed_component_array_iterator indexed_component_array_iterator::operator+(const difference_type n) const +{ + auto tmp = *this; + return tmp += n; +} + +template +indexed_component_array_iterator indexed_component_array_iterator::operator-(const difference_type n) const +{ + auto tmp = *this; + return tmp -= n; +} + +template +typename indexed_component_array_iterator::difference_type +indexed_component_array_iterator::operator-(const indexed_component_array_iterator& other) const +{ + return static_cast(m_index) - static_cast(other.m_index); +} + +template +typename indexed_component_array_iterator::reference indexed_component_array_iterator::operator[]( + const difference_type n +) const { + return *((*this) + n); +} + +template +bool indexed_component_array_iterator::operator==(const indexed_component_array_iterator& other) const +{ + return m_components == other.m_components and m_index == other.m_index; +} + +template +bool indexed_component_array_iterator::operator!=(const indexed_component_array_iterator& other) const +{ + return not (*this == other); +} + +template +bool indexed_component_array_iterator::operator<(const indexed_component_array_iterator& other) const +{ + return m_index < other.m_index; +} + +template +bool indexed_component_array_iterator::operator<=(const indexed_component_array_iterator& other) const +{ + return m_index <= other.m_index; +} + +template +bool indexed_component_array_iterator::operator>(const indexed_component_array_iterator& other) const +{ + return m_index > other.m_index; +} + +template +bool indexed_component_array_iterator::operator>=(const indexed_component_array_iterator& other) const +{ + return m_index >= other.m_index; +} + +template +template +bool indexed_component_array_iterator::is_component_enabled(C flag) +{ + return (flag & (C{1} << N)) != C{}; +} + +template +template +void indexed_component_array_iterator::calc_offsets( + std::index_sequence, + difference_type n +) { + + const auto negative = n < difference_type{ 0 }; + const auto positive = n > difference_type{ 0 }; + const auto step = difference_type{ positive } - difference_type{ negative }; + n = negative ? -n : n; + + // TODO template optimize for single steps + + while (n--) + { + const auto& [ flags, index_count, component_count ] = m_flag_counts[m_index + n]; + + std::get<0>(m_offsets) += step * index_count; + + ([&] { + if (is_component_enabled(flags)) { + std::get<1 + Is>(m_offsets) += step * component_count; + } + }(), ...); + + m_index += step; + } +} + +template +template +typename indexed_component_array_iterator::reference +indexed_component_array_iterator::dereference(std::index_sequence) const +{ + const auto& [ flags, index_count, component_count ] = m_flag_counts[m_index]; + + return std::make_tuple( + std::span( + m_indices[m_offsets[0]], + index_count + ), + std::span( + ( + is_component_enabled(flags) + ? &std::get(m_components)[m_offsets[1 + Is]] + : nullptr + ), + component_count + )... + ); +} + + + +template +std::tuple...> generic_dynamic_indexed_component_array_store::component_array_ptrs() +{ + return [&](std::index_sequence) + { + return std::make_tuple(std::get(m_component_arrays).data()...); + } + (std::index_sequence_for{}); +} + +template +std::tuple>...> generic_dynamic_indexed_component_array_store::component_array_ptrs() const +{ + return [&](std::index_sequence) + { + return std::make_tuple(std::get(m_component_arrays).data()...); + } + (std::index_sequence_for{}); +} + +template +std::array generic_dynamic_indexed_component_array_store::array_counts() const +{ + return [&](std::index_sequence) + { + return std::array{ + m_indices.size(), + std::get(m_component_arrays).size()... + }; + } + (std::index_sequence_for{}); +} + +template +typename generic_dynamic_indexed_component_array_store::id_type generic_dynamic_indexed_component_array_store::add( + std::span indices, + const std::tuple...>& component_arrays +) { + + auto component_flags = C{}; + auto min_component_count = count_type{}; + + [&](std::integer_sequence) + { + const auto& component_array = std::get(component_arrays); + if (not component_array.empty()) + { + const auto component_count = static_cast(component_array.size()); + if (min_component_count != 0 and component_count < min_component_count) + { + min_component_count = component_count; + } + component_flags |= C{ 1 } << Is; + } + } + (std::index_sequence_for{}); + + m_indices.insert(m_indices.end(), indices.begin(), indices.end()); + + [&](std::integer_sequence) + { + const auto& src_array = std::get(component_arrays); + auto& dst_array = std::get(m_component_arrays); + + if (not src_array.empty()) + { + dst_array.insert(dst_array.end(), src_array.begin(), src_array.begin() + min_component_count); + } + } + (std::index_sequence_for{}); + + m_component_flag_counts.emplace_back(component_flags, indices.size(), min_component_count); + + const auto id = id_type{ m_next_data_id.index++ }; + m_ids.push_back(id); + + return id; +} + +template +std::pair::iterator_type, bool> generic_dynamic_indexed_component_array_store::find(id_type id) +{ + const auto id_it = std::ranges::upper_bound(m_ids, id); + + const auto match = ( + id_it != m_ids.begin() and + *std::prev(id_it) == id + ); + + const auto index = id_it - m_ids.begin() - match; + + auto it = begin(); + it += index; + + return { it, match }; +} + +template +std::pair::const_iterator, bool> generic_dynamic_indexed_component_array_store::find(id_type id) const +{ + const auto id_it = std::ranges::upper_bound(m_ids, id); + + const auto match = ( + id_it != m_ids.begin() and + *std::prev(id_it) == id + ); + + const auto index = id_it - m_ids.begin() - match; + + auto it = begin(); + it += index; + + return { it, match }; +} + + +template +void generic_dynamic_indexed_component_array_store::remove(const iterator_type& it) +{ + m_indices.erase(m_indices.begin() + it.m_offsets[0]); + + [&](std::index_sequence) + { + ([&]{ + auto& component_vector = std::get(m_component_arrays); + const auto begin = component_vector.begin() + it.m_offsets[1 + Is]; + const auto end = begin + it.m_flag_counts[it.m_index]; + component_vector.erase(begin, end); + }(), ...); + } (std::index_sequence_for{}); + + m_component_flag_counts.erase(m_component_flag_counts.begin() + it.m_index); + m_ids.erase(m_ids.begin() + it.m_index); +} + +template +void generic_dynamic_indexed_component_array_store::clear() +{ + m_indices.clear(); + [&](std::index_sequence) + { + std::get(m_component_arrays).clear(); + } (std::index_sequence_for{}); + m_component_flag_counts.clear(); + m_ids.clear(); +} + +template +typename generic_dynamic_indexed_component_array_store::iterator_type generic_dynamic_indexed_component_array_store::begin() +{ + return iterator_type{ + m_indices.data(), + component_array_ptrs(), + m_component_flag_counts.data(), + 0, + {} + }; +} + +template +typename generic_dynamic_indexed_component_array_store::iterator_type generic_dynamic_indexed_component_array_store::end() +{ + return iterator_type{ + m_indices.data(), + component_array_ptrs(), + m_component_flag_counts.data(), + m_component_flag_counts.size(), + array_counts() + }; +} + +template +typename generic_dynamic_indexed_component_array_store::const_iterator generic_dynamic_indexed_component_array_store::begin() const +{ + return iterator_type{ + m_indices.data(), + component_array_ptrs(), + m_component_flag_counts.data(), + 0, + {} + }; +} + +template +typename generic_dynamic_indexed_component_array_store::const_iterator generic_dynamic_indexed_component_array_store::end() const +{ + return iterator_type{ + m_indices.data(), + component_array_ptrs(), + m_component_flag_counts.data(), + m_component_flag_counts.size(), + array_counts() + }; +} + +template +typename generic_dynamic_indexed_component_array_store::const_iterator generic_dynamic_indexed_component_array_store::cbegin() const +{ + return const_cast(this)->begin(); +} + +template +typename generic_dynamic_indexed_component_array_store::const_iterator generic_dynamic_indexed_component_array_store::cend() const +{ + return const_cast(this)->end(); +} + +template +typename generic_dynamic_indexed_component_array_store::view_type generic_dynamic_indexed_component_array_store::view() +{ + return { begin(), end() }; +} +template +typename generic_dynamic_indexed_component_array_store::const_view_type generic_dynamic_indexed_component_array_store::view() const +{ + return { begin(), end() }; +} diff --git a/source/assets/dynamic_data_stores/generic/generic_dynamic_store.ipp b/source/assets/dynamic_data_stores/generic/generic_dynamic_store.ipp new file mode 100644 index 0000000..25380c3 --- /dev/null +++ b/source/assets/dynamic_data_stores/generic/generic_dynamic_store.ipp @@ -0,0 +1,61 @@ +#ifndef INCLUDE_GENERIC_DYNAMIC_STORE_IMPLEMENTATION +# error Never include this file directly include 'basic_dynamic_store.hpp' +#endif + +#include + +template +typename generic_dynamic_store::id_type generic_dynamic_store::add(const T& data) +{ + auto id = id_type{ m_next_data_id.index++ }; + m_data.emplace_back(data); + m_ids.emplace_back(id); + return id; +} + +template +std::pair::iterator_type, bool> generic_dynamic_store::find(id_type id) +{ + const auto it = std::ranges::upper_bound(m_ids, id); + const auto found = it != m_ids.begin() and *std::prev(it) == id; + const auto index = it - m_ids.begin() - found; + + return { m_data.begin() + index, found }; +} + +template +std::pair::const_iterator, bool> generic_dynamic_store::find(id_type id) const +{ + const auto it = std::ranges::upper_bound(m_ids, id); + const auto found = it != m_ids.begin() and *std::prev(it) == id; + const auto index = it - m_ids.begin() - found; + + return { m_data.begin() + index, found }; +} + +template +void generic_dynamic_store::remove(iterator_type it) +{ + const auto index = it - m_data.begin(); + m_data.erase(it); + m_ids.erase(m_ids.begin() + index); +} + +template +void generic_dynamic_store::clear() +{ + m_data.clear(); + m_ids.clear(); +} + +template +std::span generic_dynamic_store::data() +{ + return m_data; +} + +template +std::span generic_dynamic_store::data() const +{ + return m_data; +} diff --git a/source/assets/dynamic_read_buffers/dynamic_material_buffer.ipp b/source/assets/dynamic_read_buffers/dynamic_material_buffer.ipp new file mode 100644 index 0000000..8363407 --- /dev/null +++ b/source/assets/dynamic_read_buffers/dynamic_material_buffer.ipp @@ -0,0 +1,88 @@ +#ifndef INCLUDE_DYNAMIC_MATERIAL_DATA_IMPLEMENTATION +# error Never include this file directly include 'dynamic_material_buffer.hpp' +#endif + + +inline std::optional& dynamic_material_buffer::surface_properties() +{ + return std::get(data); +} +inline std::optional& dynamic_material_buffer::transparency() +{ + return std::get(data); +} +inline std::optional& dynamic_material_buffer::ambient_color_texture_id() +{ + return std::get(data); +} +inline std::optional& dynamic_material_buffer::diffuse_color_texture_id() +{ + return std::get(data); +} +inline std::optional& dynamic_material_buffer::specular_color_texture_id() +{ + return std::get(data); +} +inline std::optional& dynamic_material_buffer::shininess_texture_id() +{ + return std::get(data); +} +inline std::optional& dynamic_material_buffer::alpha_texture_id() +{ + return std::get(data); +} +inline std::optional& dynamic_material_buffer::bump_texture_id() +{ + return std::get& dynamic_material_buffer::surface_properties() const +{ + return std::get(data); +} + +inline const std::optional& dynamic_material_buffer::transparency() const +{ + return std::get(data); +} + +inline const std::optional& dynamic_material_buffer::ambient_color_texture_id() const +{ + return std::get(data); +} + +inline const std::optional& dynamic_material_buffer::diffuse_color_texture_id() const +{ + return std::get(data); +} + +inline const std::optional& dynamic_material_buffer::specular_color_texture_id() const +{ + return std::get(data); +} + +inline const std::optional& dynamic_material_buffer::shininess_texture_id() const +{ + return std::get(data); +} + +inline const std::optional& dynamic_material_buffer::alpha_texture_id() const +{ + return std::get(data); +} + +inline const std::optional& dynamic_material_buffer::bump_texture_id() const +{ + return std::get(data); +} + + +inline components::material::surface_properties& dynamic_material_buffer::initialized_surface_properties() +{ + auto& surface_properties_opt = surface_properties(); + if (not surface_properties_opt) + { + surface_properties_opt = components::material::surface_properties{}; + } + return *surface_properties_opt; +} diff --git a/source/assets/dynamic_read_buffers/dynamic_mesh_buffer.ipp b/source/assets/dynamic_read_buffers/dynamic_mesh_buffer.ipp new file mode 100644 index 0000000..7b1178e --- /dev/null +++ b/source/assets/dynamic_read_buffers/dynamic_mesh_buffer.ipp @@ -0,0 +1,73 @@ +#ifndef INCLUDE_DYNAMIC_MESH_DATA_IMPLEMENTATION +# error Never include this file directly include 'dynamic_mesh_buffer.hpp' +#endif + +inline std::vector& dynamic_mesh_buffer::positions() +{ + return std::get(vertices); +} + +inline std::vector& dynamic_mesh_buffer::normals() +{ + return std::get(vertices); +} + +inline std::vector& dynamic_mesh_buffer::tex_coords() +{ + return std::get(vertices); +} + +inline std::vector& dynamic_mesh_buffer::colors() +{ + return std::get(vertices); +} + +inline std::vector& dynamic_mesh_buffer::reflectances() +{ + return std::get(vertices); +} + +inline std::vector& dynamic_mesh_buffer::triangles() +{ + return m_triangles; +} + +inline auto& dynamic_mesh_buffer::material_id() +{ + return m_material_id; +} + +inline const std::vector& dynamic_mesh_buffer::positions() const +{ + return std::get(vertices); +} + +inline const std::vector& dynamic_mesh_buffer::normals() const +{ + return std::get(vertices); +} + +inline const std::vector& dynamic_mesh_buffer::tex_coords() const +{ + return std::get(vertices); +} + +inline const std::vector& dynamic_mesh_buffer::colors() const +{ + return std::get(vertices); +} + +inline const std::vector& dynamic_mesh_buffer::reflectances() const +{ + return std::get(vertices); +} + +inline const std::vector& dynamic_mesh_buffer::triangles() const +{ + return m_triangles; +} + +inline const auto& dynamic_mesh_buffer::material_id() const +{ + return m_material_id; +} diff --git a/source/assets/dynamic_read_buffers/dynamic_model_buffer.ipp b/source/assets/dynamic_read_buffers/dynamic_model_buffer.ipp new file mode 100644 index 0000000..f30be4c --- /dev/null +++ b/source/assets/dynamic_read_buffers/dynamic_model_buffer.ipp @@ -0,0 +1,148 @@ +#ifndef INCLUDE_DYNAMIC_MODEL_DATA_IMPLEMENTATION +# error Never include this file directly include 'dynamic_vertex_buffer.hpp' +#endif + +#include "util/specialised_lambda.hpp" +#include "opengl/type_utils.hpp" +#include +#include +#include + +template +C& dynamic_vertex_buffer::components() +{ + return m_components; +} + +template +const C& dynamic_vertex_buffer::components() const +{ + return m_components; +} + +template +concept numeric_type = std::integral or std::floating_point; + +template +void dynamic_vertex_buffer::build_vertex_buffer( + std::vector& vertex_buffer, + std::size_t& component_count, + std::array& component_types, + std::array& component_lengths, + GLsizei& stride +) const { + const auto for_all_components = [&](auto&& f, const T default_value) + { + return std::apply( + [&](const auto&... component_buffer) + { + std::array results{}; + auto i = std::size_t{}; + ( + ( + results[i] = [&](const auto& buffer, const auto index) -> T + { + if ((m_components & C{ 1 << index }) != C{}) + { + return f(buffer, index); + } + return default_value; + }(component_buffer, i), + ++i + ), + ... + ); + return results; + }, + m_component_buffers + ); + }; + + component_count = 0; + component_types = for_all_components( + ztu::specialised_lambda + { + [&component_count](const std::vector>&, std::size_t) + { + ++component_count; + return zgl::type_utils::to_gl_type(); + }, + [&component_count](const std::vector&, std::size_t) + { + ++component_count; + return zgl::type_utils::to_gl_type(); + } + }, + GLenum{ GL_INVALID_VALUE } + ); + + const auto element_counts = for_all_components( + [](const std::vector& buffer, std::size_t) + { + return buffer.size(); + }, + std::numeric_limits::max() + ); + + const auto minimum_element_count = std::ranges::min(element_counts); + + component_lengths = for_all_components( + ztu::specialised_lambda + { + [](const std::vector&, std::size_t) + { + return 1; + }, + [](const std::vector>&, std::size_t) + { + return Count; + } + }, + GLsizei{ 0 } + ); + + auto component_sizes = std::array{}; + for (std::size_t i{}; i != component_sizes.size(); ++i) + { + component_sizes[i] = component_lengths[i] * zgl::type_utils::size_of(component_types[i]); + } + + const auto total_size = minimum_element_count * std::accumulate( + component_sizes.begin(), + component_sizes.end(), + GLsizei{ 0 } + ); + + vertex_buffer.resize(total_size); + + // Calculate offsets and stride + auto component_offsets = component_sizes; + stride = 0; + for (std::size_t i{}; i != component_offsets.size(); ++i) { + component_offsets[i] = stride; + stride += component_sizes[i]; + } + + // Copy all the components over one by one + for_all_components( + [&](const std::vector& buffer, std::size_t index) + { + std::size_t pos = component_offsets[index]; + for (std::size_t i{}; i != minimum_element_count; ++i) + { + std::memcpy( + &vertex_buffer[pos], + buffer[i].data(), + component_sizes[index] + ); + pos += stride; + } + return 0; + }, + 0 + ); + + // remove values of unused components + std::ignore = std::ranges::remove(component_lengths, 0); + std::ignore = std::ranges::remove(component_types, GL_INVALID_VALUE); +} diff --git a/source/assets/dynamic_read_buffers/dynamic_point_cloud_buffer.ipp b/source/assets/dynamic_read_buffers/dynamic_point_cloud_buffer.ipp new file mode 100644 index 0000000..685aba8 --- /dev/null +++ b/source/assets/dynamic_read_buffers/dynamic_point_cloud_buffer.ipp @@ -0,0 +1,44 @@ +#ifndef INCLUDE_DYNAMIC_TEXTURE_DATA_IMPLEMENTATION +# error Never include this file directly include 'dynamic_point_cloud_buffer.hpp' +#endif +#include "assets/components/point_cloud_vertex_components.hpp" + +inline std::vector& dynamic_point_cloud_buffer::positions() +{ + return std::get(vertices); +} + +inline std::vector& dynamic_point_cloud_buffer::normals() +{ + return std::get(vertices); +} + +inline std::vector& dynamic_point_cloud_buffer::colors() +{ + return std::get(vertices); +} + +inline std::vector& dynamic_point_cloud_buffer::reflectances() +{ + return std::get(vertices); +} + +inline const std::vector& dynamic_point_cloud_buffer::positions() const +{ + return std::get(vertices); +} + +inline const std::vector& dynamic_point_cloud_buffer::normals() const +{ + return std::get(vertices); +} + +inline const std::vector& dynamic_point_cloud_buffer::colors() const +{ + return std::get(vertices); +} + +inline const std::vector& dynamic_point_cloud_buffer::reflectances() const +{ + return std::get(vertices); +} diff --git a/source/assets/dynamic_read_buffers/dynamic_texture_buffer.ipp b/source/assets/dynamic_read_buffers/dynamic_texture_buffer.ipp new file mode 100644 index 0000000..855117d --- /dev/null +++ b/source/assets/dynamic_read_buffers/dynamic_texture_buffer.ipp @@ -0,0 +1,151 @@ +#ifndef INCLUDE_DYNAMIC_TEXTURE_DATA_IMPLEMENTATION +# error Never include this file directly include 'dynamic_texture_buffer.hpp' +#endif + + +inline dynamic_texture_buffer::dynamic_texture_buffer( + std::unique_ptr&& data, + const dim_type width, + const dim_type height, + const components::texture::flags components +) : + m_data{ std::move(data) }, + m_width{ width }, + m_height{ height }, + m_components{ components } +{}; + +inline dynamic_texture_buffer::dynamic_texture_buffer(const dynamic_texture_buffer& other) : + m_data{ new value_type[other.component_count()] }, + m_width{ other.m_width }, + m_height{ other.m_height }, + m_components{ other.m_components } +{ + std::copy_n(other.m_data.get(), other.m_width * other.m_height, this->m_data.get()); +} + +inline dynamic_texture_buffer::dynamic_texture_buffer(dynamic_texture_buffer&& other) noexcept : + m_data{ std::move(other.m_data) }, + m_width{ other.m_width }, + m_height{ other.m_height }, + m_components{ other.m_components } +{ + other.m_width = 0; + other.m_height = 0; + other.m_components = components::texture::flags::none; +} + +inline dynamic_texture_buffer& dynamic_texture_buffer::operator=(const dynamic_texture_buffer& other) +{ + if (this != &other) [[likely]] + { + + const auto m_size = this->component_count(); + const auto o_size = other.component_count(); + + if (o_size > m_size) { + this->~dynamic_texture_buffer(); + this->m_data.reset(new value_type[o_size]); + } + + std::copy_n(other.m_data.get(), o_size, this->m_data.get()); + + this->m_width = other.m_width; + this->m_height = other.m_height; + this->m_components = other.m_components; + } + + return *this; +} + +inline dynamic_texture_buffer& dynamic_texture_buffer::operator=(dynamic_texture_buffer&& other) noexcept +{ + if (this != &other) [[likely]] + { + this->~dynamic_texture_buffer(); + + this->m_data = std::move(other.m_data); + this->m_width = other.m_width; + this->m_height = other.m_height; + + other.m_width = 0; + other.m_height = 0; + other.m_components = components::texture::flags::none; + } + return *this; +} + +inline components::texture::flags dynamic_texture_buffer::components() const +{ + return m_components; +} + +inline dynamic_texture_buffer::dim_type dynamic_texture_buffer::width() const +{ + return m_width; +} + +inline dynamic_texture_buffer::dim_type dynamic_texture_buffer::height() const +{ + return m_height; +} + +inline std::pair dynamic_texture_buffer::dimensions() const +{ + return { m_width, m_height }; +} + +inline dynamic_texture_buffer::size_type dynamic_texture_buffer::pixel_count() const +{ + return static_cast(m_width) * static_cast(m_height); +} + +inline dynamic_texture_buffer::size_type dynamic_texture_buffer::component_count() const +{ + return std::popcount(static_cast>(m_components)); +} + +inline dynamic_texture_buffer::size_type dynamic_texture_buffer::size() const +{ + return pixel_count() * component_count(); +} + +inline dynamic_texture_buffer::const_pointer dynamic_texture_buffer::data() const +{ + return m_data.get(); +} + +inline dynamic_texture_buffer::pointer dynamic_texture_buffer::data() +{ + return m_data.get(); +} + +inline dynamic_texture_buffer::const_iterator dynamic_texture_buffer::begin() const +{ + return data(); +} + +inline dynamic_texture_buffer::iterator dynamic_texture_buffer::begin() +{ + return data(); +} + +inline dynamic_texture_buffer::const_iterator dynamic_texture_buffer::end() const +{ + return begin() + component_count(); +} + +inline dynamic_texture_buffer::iterator dynamic_texture_buffer::end() +{ + return begin() + component_count(); +} + +inline dynamic_texture_buffer::const_iterator dynamic_texture_buffer::cbegin() const +{ + return const_cast(begin()); +} + +inline dynamic_texture_buffer::const_iterator dynamic_texture_buffer::cend() const +{ + return const_cast(begin()); +} diff --git a/source/assets/prefetch_lookups/pose_prefetch_lookup.cpp b/source/assets/prefetch_lookups/pose_prefetch_lookup.cpp new file mode 100644 index 0000000..df551c6 --- /dev/null +++ b/source/assets/prefetch_lookups/pose_prefetch_lookup.cpp @@ -0,0 +1,139 @@ +#include "assets/prefetch_lookups/pose_prefetch_lookup.hpp" + +#include + + pose_prefetch_lookup::find_directory( + const std::filesystem::path& directory +) { + const auto dir_it = m_directory_lookup.find(directory); + const auto dir_match = dir_it != m_directory_lookup.end(); + auto offset = index_type{}; + + if (dir_match) + { + for (index_type i{}; i != dir_match; ++i) + { + offset += m_directory_indices[offset]; + } + } + + return { std::make_pair(dir_it, offset), dir_match }; +} + +std::pair pose_prefetch_lookup::find_index( + directory_iterator directory_it, + index_type index +) { + const auto [ dir_it, dir_offset ] = directory_it; + + const auto index_count = m_directory_indices[dir_offset]; + const auto indices_begin = m_directory_indices.begin() + dir_offset + 1; + const auto indices_end = indices_begin + index_count; + + const auto it = std::upper_bound(indices_begin, indices_end, index); + const auto is_match = it != indices_begin and *std::prev(it) == index; + + auto match = dynamic_pose_store::id_type{}; + + if (is_match) + { + const auto dir_index = dir_it->second; + const auto id_index = it - m_directory_indices.begin() - match - dir_index; + match = m_pose_ids[id_index]; + } + + return { it, match }; +} + +pose_prefetch_lookup::directory_iterator pose_prefetch_lookup::emplace_dir( + directory_iterator directory_it, + const std::filesystem::path& directory +) { + auto [ dir_it, dir_offset ] = directory_it; + + dir_it = m_directory_lookup.emplace_hint(dir_it, directory, m_directory_lookup.size()); + dir_offset = m_directory_indices.size(); + m_directory_indices.push_back(1); + + return { dir_it, dir_offset }; +} + +void pose_prefetch_lookup::emplace( + const std::filesystem::path& directory, + index_type index, + dynamic_pose_store::id_type id +) { + auto [ directory_it, dir_match ] = find_directory(directory); + + if (not dir_match) [[unlikely]] + { + directory_it = emplace_dir(directory_it, directory); + } + + const auto [ index_it, match ] = find_index( + directory_it, + index + ); + + if (not match) + { + + } + + emplace_hint_dir_index( + directory_it, + index_it, + index, + id + ); +} + + +void pose_prefetch_lookup::emplace_hint_dir( + directory_iterator directory_it, + const std::filesystem::path& directory, + const index_type index, + const dynamic_pose_store::id_type id +) { + + directory_it = emplace_dir(directory_it, directory); + + const auto [ index_it, match ] = find_index( + directory_it, + index + ); + + if (match) [[unlikely]] + { + const auto dir_index = dir_it->second; + const auto id_index = index_it - m_directory_indices.begin() - dir_index; + m_pose_ids[id_index] = match; // TODO I guess I should warn?!? + } + else [[likely]] + { + emplace_hint_dir_index( + directory_it, + index_it, + index, + id + ); + } +} + +void pose_prefetch_lookup::emplace_hint_dir_index( + directory_iterator directory_it, + index_iterator index_it, + index_type index, + const dynamic_pose_store::id_type id +) { + const auto [ dir_it, dir_offset ] = directory_it; + const auto dir_index = dir_it->second; + + index_it = m_directory_indices.emplace(index_it, index); + ++m_directory_indices[dir_offset]; + + const auto id_index = index_it - m_directory_indices.begin() - dir_index; + const auto id_it = m_pose_ids.begin() + id_index; + + m_pose_ids.insert(id_it, id); +} \ No newline at end of file diff --git a/source/geometry/normal_estimation.cpp b/source/geometry/normal_estimation.cpp new file mode 100644 index 0000000..4b279d9 --- /dev/null +++ b/source/geometry/normal_estimation.cpp @@ -0,0 +1,82 @@ +#include "geometry/normal_estimation.hpp" + +#include +#include + +void estimate_normals( + std::span vertices, + std::span> triangles, + std::vector& normals +) { + + normals.resize(vertices.size()); + + std::ranges::fill(normals, std::array{ 0.0f, 0.0f, 0.0f }); + + for (const auto& triangle : triangles) + { + auto abc = std::array{}; + + std::ranges::transform( + triangle, + abc.begin(), + [&](const auto& index) + { + const auto& [ x, y, z ] = vertices[index]; + return glm::vec3{ x, y, z }; + } + ); + + const auto [ A, B, C ] = abc; + + // TODO normalization can be done more efficiently + const auto normal = glm::normalize(glm::vec3{ + A.y * B.z - A.z * B.y, + A.z * B.x - A.x * B.z, + A.x * B.y - A.y * B.x + }); + + const auto a_length = glm::length(B - C); + const auto b_length = glm::length(C - A); + const auto c_length = glm::length(A - B); + + const auto area = 0.25f * std::sqrt( + (a_length + b_length + c_length) * + (-a_length + b_length + c_length) * + (a_length - b_length + c_length) * + (a_length + b_length - c_length) + ); + + const auto weighted_normal = normal * area; + + for (const auto& index : triangle) + { + auto& normal_avg = normals[index]; + for (int i{}; i != 3; ++i) + { + normal_avg[i] += weighted_normal[i]; + } + } + } + + using normal_component_type = components::mesh_vertex::normal::component_type; + constexpr auto epsilon = std::numeric_limits::epsilon(); + + for (auto& [ x, y, z ] : normals) + { + const auto length = glm::length(glm::vec3{ x, y, z }); + if (length <= epsilon) + { + x = 0; + y = 1; + z = 0; + } + else + { + const auto scale = 1.0f / length; + x *= scale; + y *= scale; + z *= scale; + } + } +} diff --git a/source/opengl/data/material_data.ipp b/source/opengl/data/material_data.ipp new file mode 100644 index 0000000..a02d205 --- /dev/null +++ b/source/opengl/data/material_data.ipp @@ -0,0 +1,129 @@ +#ifndef INCLUDE_MATERIAL_DATA_IMPLEMENTATION +# error Never include this file directly include 'material_data.hpp' +#endif + +namespace zgl +{ +inline material_data::material_data( + const std::optional& texture_handle, + const std::optional& surface_properties_handle, + const std::optional& alpha_handle, + std::optional&& texture_data, + const material_component::flags components +) : + m_handle{ + .texture = texture_handle, + .surface_properties = surface_properties_handle, + .alpha = alpha_handle + }, + m_texture_data{ std::move(texture_data) }, + m_component_types{ components } {} + +inline material_data::material_data(material_data&& other) noexcept +{ + m_handle = other.m_handle; + m_texture_data = std::move(other.m_texture_data); + m_component_types = other.m_component_types; + + other.m_handle.texture = std::nullopt; + other.m_handle.surface_properties = std::nullopt; + other.m_handle.alpha = std::nullopt; + other.m_component_types = material_component::flags::none; +} + +inline material_data& material_data::operator=(material_data&& other) noexcept +{ + if (&other != this) + { + this->~material_data(); + + m_handle = other.m_handle; + m_texture_data = std::move(other.m_texture_data); + m_component_types = other.m_component_types; + + other.m_handle.texture = std::nullopt; + other.m_handle.surface_properties = std::nullopt; + other.m_handle.alpha = std::nullopt; + other.m_component_types = material_component::flags::none; + } + + return *this; +} + + +inline material_handle material_data::handle() const +{ + return m_handle; +} + +inline material_component::flags material_data::components() const +{ + return m_component_types; +} + + +inline std::error_code material_data::build_from( + const std::optional& texture_opt, + const std::optional& surface_properties_opt, + const std::optional& transparency_opt, + const material_component::flags components, + material_data& dst_data +) { + + auto texture_data_opt = std::optional{ std::nullopt }; + auto texture_handle_opt = std::optional{ std::nullopt }; + if (texture_opt) + { + const auto& texture = *texture_opt; + auto texture_data = zgl::texture_data{}; + + if (const auto e = texture_data::build_from( + std::span(texture.cbegin(), texture.cend()), + GL_RGBA, GL_UNSIGNED_BYTE, + texture.width(), + texture.height(), + texture_data + )) { + return e; + } + + texture_handle_opt.emplace(texture_data.handle()); + texture_data_opt.emplace(std::move(texture_data)); + } + + auto surface_properties_data_opt = std::optional{ std::nullopt }; + if (surface_properties_opt) + { + const auto& [ ambient, diffuse, specular, shininess ] = *surface_properties_opt; + surface_properties_data_opt.emplace( + glm::vec3{ ambient[0], ambient[1], ambient[2] }, + glm::vec3{ diffuse[0], diffuse[1], diffuse[2] }, + glm::vec3{ specular[0], specular[1], specular[2] }, + shininess + ); + } + + auto alpha_data_opt = std::optional{ std::nullopt }; + if (transparency_opt) + { + alpha_data_opt.emplace(1.0f - *transparency_opt); + } + + + /*dst_data = material_data{ + texture_handle_opt, + surface_properties_data_opt, + alpha_data_opt, + std::move(texture_data_opt), + components + };*/ + + dst_data.m_handle.texture = texture_handle_opt; + dst_data.m_handle.surface_properties = surface_properties_data_opt; + dst_data.m_handle.alpha = alpha_data_opt; + dst_data.m_texture_data = std::move(texture_data_opt); + dst_data.m_component_types = components; + + return {}; +} +} diff --git a/source/opengl/data/mesh_data.cpp b/source/opengl/data/mesh_data.cpp new file mode 100644 index 0000000..48e1075 --- /dev/null +++ b/source/opengl/data/mesh_data.cpp @@ -0,0 +1,119 @@ +#include "opengl/data/mesh_data.hpp" + +#include "opengl/type_utils.hpp" +#include "opengl/error.hpp" + +#include "GL/glew.h" +#include +#include "util/logger.hpp" // TODO remove + + +std::error_code zgl::mesh_data::build_from( + const std::span vertex_buffer, + const std::span component_types, + const std::span component_lengths, + const GLsizei stride, + const std::span index_buffer, + const ztu::u32 material_id, + const components::mesh_vertex::flags components, + mesh_data& data +) { + if (not std::ranges::all_of(component_types, type_utils::is_valid_type)) + { + ztu::logger::debug("not all types valid."); + return std::make_error_code(std::errc::invalid_argument); + } + + const auto vertices_byte_count = vertex_buffer.size() * sizeof(std::uint8_t); + if (vertices_byte_count > std::numeric_limits::max()) + { + return std::make_error_code(std::errc::value_too_large); + } + + const auto indices_byte_count = index_buffer.size() * sizeof(ztu::u32); + if (indices_byte_count > std::numeric_limits::max()) + { + return std::make_error_code(std::errc::value_too_large); + } + + auto error = std::error_code{}; + + auto check_error = [&error, ec = static_cast(GL_NO_ERROR)]() mutable -> const std::error_code& + { + ec = glGetError(); + if (ec != GL_NO_ERROR) + { + error = make_error_code(ec); + } + return error; + }; + + GLuint vao_id; + glGenVertexArrays(1, &vao_id); + glBindVertexArray(vao_id); + if (check_error()) return error; + + ztu::logger::debug("Created mesh vao: % valid: %", vao_id, (bool)glIsVertexArray(vao_id)); + + GLuint vertex_buffer_id; + glGenBuffers(1, &vertex_buffer_id); + glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_id); + glBufferData( + GL_ARRAY_BUFFER, + static_cast(vertices_byte_count), + vertex_buffer.data(), + GL_STATIC_DRAW + ); + if (check_error()) return error; + + GLuint index_buffer_id; + glGenBuffers(1, &index_buffer_id); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer_id); + glBufferData( + GL_ELEMENT_ARRAY_BUFFER, + static_cast(indices_byte_count), + index_buffer.data(), + GL_STATIC_DRAW + ); + if (check_error()) return error; + + glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_id); + if (check_error()) return error; + + auto offset = GLsizei{ 0 }; + + for (std::size_t i{}; i != component_types.size(); ++i) + { + const auto type = component_types[i]; + const auto length = component_lengths[i]; + const auto byte_count = type_utils::size_of(type); + + glVertexAttribPointer( + i, + length, + type, + GL_FALSE, + stride, + reinterpret_cast(offset) + ); + + glEnableVertexAttribArray(i); + if (check_error()) return error; + + offset += length * byte_count; + } + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + + data = mesh_data( + vertex_buffer_id, + index_buffer_id, + vao_id, + material_id, + components, + index_buffer.size() + ); + + return {}; +} diff --git a/source/opengl/data/mesh_data.ipp b/source/opengl/data/mesh_data.ipp new file mode 100644 index 0000000..152112f --- /dev/null +++ b/source/opengl/data/mesh_data.ipp @@ -0,0 +1,90 @@ +#ifndef INCLUDE_MESH_DATA_IMPLEMENTATION +# error Never include this file directly include 'mesh_data.hpp' +#endif + +namespace zgl +{ +inline mesh_data::mesh_data( + const GLuint vertex_vbo_id, + const GLuint index_vbo_id, + const GLuint vao_id, + const ztu::u32 material_id, + const components::mesh_vertex::flags components, + const GLsizei index_count +) : + m_handle{ + .vao_id = vao_id, + .index_count = index_count + }, + m_vertex_vbo_id{ vertex_vbo_id }, + m_index_vbo_id{ index_vbo_id }, + m_material_id{ material_id }, + m_component_types{ components } {} + +inline mesh_data& mesh_data::operator=(mesh_data&& other) noexcept +{ + if (&other != this) + { + this->~mesh_data(); + + m_handle = other.m_handle; + m_vertex_vbo_id = other.m_vertex_vbo_id; + m_index_vbo_id = other.m_index_vbo_id; + m_material_id = other.m_material_id; + m_component_types = other.m_component_types; + + other.m_handle.vao_id = 0; + other.m_handle.index_count = 0; + other.m_vertex_vbo_id = 0; + other.m_index_vbo_id = 0; + other.m_material_id = 0; + other.m_component_types = components::mesh_vertex::flags::none; + } + + return *this; +} + +inline mesh_data::mesh_data(mesh_data&& other) noexcept : + m_handle{ other.m_handle }, + m_vertex_vbo_id{ other.m_vertex_vbo_id }, + m_index_vbo_id{ other.m_index_vbo_id }, + m_material_id{ other.m_material_id }, + m_component_types{ other.m_component_types } +{ + other.m_handle.vao_id = 0; + other.m_handle.index_count = 0; + other.m_vertex_vbo_id = 0; + other.m_index_vbo_id = 0; + other.m_material_id = 0; + other.m_component_types = components::mesh_vertex::flags::none; +} + +inline mesh_data::~mesh_data() { + if (m_vertex_vbo_id) { + glDeleteBuffers(1, &m_vertex_vbo_id); + } + if (m_index_vbo_id) { + glDeleteBuffers(1, &m_index_vbo_id); + } + if (m_handle.vao_id) { + glDeleteVertexArrays(1, &m_handle.vao_id); + } +} + + +inline mesh_handle mesh_data::handle() const +{ + return m_handle; +} + +inline components::mesh_vertex::flags mesh_data::components() const +{ + return m_component_types; +} + +inline ztu::u32 mesh_data::material_id() const +{ + return m_material_id; +} + +} diff --git a/source/opengl/data/point_cloud_data.cpp b/source/opengl/data/point_cloud_data.cpp new file mode 100644 index 0000000..73cfa98 --- /dev/null +++ b/source/opengl/data/point_cloud_data.cpp @@ -0,0 +1,99 @@ +#include "opengl/data/point_cloud_data.hpp" + +#include "opengl/type_utils.hpp" +#include "opengl/error.hpp" + +#include +#include "GL/glew.h" + +std::error_code zgl::point_cloud_data::build_from( + std::span point_buffer, + std::span component_types, + std::span component_lengths, + GLsizei stride, + point_cloud_data& data +) { + if (not std::ranges::all_of(component_types, type_utils::is_valid_type)) + { + return std::make_error_code(std::errc::invalid_argument); + } + + const auto points_byte_count = point_buffer.size() * sizeof(std::uint8_t); + if (points_byte_count > std::numeric_limits::max()) + { + return std::make_error_code(std::errc::value_too_large); + } + + auto error = std::error_code{}; + + auto check_error = [&error, ec = static_cast(GL_NO_ERROR)]() mutable -> const std::error_code& + { + ec = glGetError(); + if (ec != GL_NO_ERROR) { + error = make_error_code(ec); + } + return error; + }; + + ztu::u32 vao_id; + glGenVertexArrays(1, &vao_id); + if (check_error()) return error; + + glBindVertexArray(vao_id); + if (check_error()) return error; + + ztu::u32 vertex_buffer_id; + glGenBuffers(1, &vertex_buffer_id); + if (check_error()) return error; + + glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_id); + if (check_error()) return error; + + glBufferData( + GL_ARRAY_BUFFER, + static_cast(points_byte_count), + point_buffer.data(), + GL_STATIC_DRAW + ); + if (check_error()) return error; + + + glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_id); + if (check_error()) return error; + + auto offset = GLsizei{ 0 }; + + for (std::size_t i{}; i != component_types.size(); ++i) + { + const auto type = component_types[i]; + const auto length = component_lengths[i]; + const auto byte_count = type_utils::size_of(type); + + glVertexAttribPointer( + i, + length, + type, + GL_FALSE, + stride, + reinterpret_cast(offset) + ); + if (check_error()) return error; + glEnableVertexAttribArray(i); + if (check_error()) return error; + + offset += length * byte_count; + } + + glBindBuffer(GL_ARRAY_BUFFER, 0); + if (check_error()) return error; + glBindVertexArray(0); + if (check_error()) return error; + + data = point_cloud_data( + vertex_buffer_id, + vao_id, + point_buffer.size() + ); + + return {}; +} diff --git a/source/opengl/data/point_cloud_data.ipp b/source/opengl/data/point_cloud_data.ipp new file mode 100644 index 0000000..6268a54 --- /dev/null +++ b/source/opengl/data/point_cloud_data.ipp @@ -0,0 +1,66 @@ +#ifndef INCLUDE_POINT_CLOUD_DATA_IMPLEMENTATION +# error Never include this file directly include 'point_cloud_data.hpp' +#endif + + +namespace zgl +{ +inline point_cloud_data::point_cloud_data( + const GLuint vertex_vbo_id, + const GLuint vao_id, + const GLsizei point_count +) : + m_handle{ + .vao_id = vao_id, + .point_count = point_count + }, + m_vertex_vbo_id{ vertex_vbo_id } {} + +inline point_cloud_data::point_cloud_data(point_cloud_data&& other) noexcept : + m_handle{ other.m_handle }, + m_vertex_vbo_id{ other.m_vertex_vbo_id } +{ + other.m_handle.vao_id = 0; + other.m_handle.point_count = 0; + other.m_vertex_vbo_id = 0; +} + +inline point_cloud_data& point_cloud_data::operator=(point_cloud_data&& other) noexcept +{ + if (&other != this) + { + this->~point_cloud_data(); + + m_handle = other.m_handle; + m_vertex_vbo_id = other.m_vertex_vbo_id; + + other.m_handle.vao_id = 0; + other.m_handle.point_count = 0; + other.m_vertex_vbo_id = 0; + } + + return *this; +} + +inline point_cloud_data::~point_cloud_data() +{ + if (m_vertex_vbo_id) + { + glDeleteBuffers(1, &m_vertex_vbo_id); + } + if (m_handle.vao_id) + { + glDeleteVertexArrays(1, &m_handle.vao_id); + } +} + +inline point_cloud_handle point_cloud_data::handle() const +{ + return m_handle; +} + +inline components::point_cloud_vertex::flags point_cloud_data::components() const +{ + return m_component_types; +} +} diff --git a/source/opengl/data/shader_data.cpp b/source/opengl/data/shader_data.cpp new file mode 100755 index 0000000..48afaa3 --- /dev/null +++ b/source/opengl/data/shader_data.cpp @@ -0,0 +1,46 @@ +#include "opengl/data/shader_data.hpp" + +#include "GL/glew.h" +#include "opengl/error.hpp" +#include "util/logger.hpp" + +namespace zgl +{ +std::error_code shader_data::build_from( + const GLenum type, + const std::string& source, + shader_data& data +) { + auto shader_id = GLuint{ 0 }; + + if (not source.empty()) + { + shader_id = glCreateShader(type); + + // Curious choice to take lists as parameters... + const auto first_list_element = source.c_str(); + const auto first_list_element_len = static_cast(source.length()); + glShaderSource(shader_id, 1, &first_list_element, &first_list_element_len); + glCompileShader(shader_id); + + GLint success; + glGetShaderiv(shader_id, GL_COMPILE_STATUS, &success); + if (not success) + { + GLint log_length{}; + glGetShaderiv(shader_id, GL_INFO_LOG_LENGTH, &log_length); + + auto log = std::string(log_length, ' '); + glGetShaderInfoLog(shader_id, log_length, nullptr, log.data()); + + ztu::logger::warn("Error while compiling shader:\n%", log); + + return std::make_error_code(std::errc::invalid_argument); + } + } + + data = shader_data{ shader_id, type }; + + return {}; +} +} diff --git a/source/opengl/data/shader_data.ipp b/source/opengl/data/shader_data.ipp new file mode 100644 index 0000000..d9fb7b6 --- /dev/null +++ b/source/opengl/data/shader_data.ipp @@ -0,0 +1,47 @@ +#ifndef INCLUDE_SHADER_DATA_IMPLEMENTATION +# error Never include this file directly include 'shader_data.hpp' +#endif + +namespace zgl +{ +inline shader_data::shader_data(const GLuint shader_id, const GLenum type) + : m_handle{ shader_id }, m_type{ type } {} + + +inline shader_data::shader_data(shader_data&& other) noexcept +{ + m_handle = other.m_handle; + m_type = other.m_type; + other.m_handle.shader_id = 0; + other.m_type = GL_INVALID_ENUM; +} + +inline shader_data& shader_data::operator=(shader_data&& other) noexcept +{ + if (&other != this) + { + this->~shader_data(); + + m_handle = other.m_handle; + m_type = other.m_type; + other.m_handle.shader_id = 0; + other.m_type = GL_INVALID_ENUM; + } + return *this; +} + +inline shader_data::~shader_data() +{ + if (m_handle.shader_id) + { + glDeleteShader(m_handle.shader_id); + } + m_type = GL_INVALID_ENUM; +} + +inline shader_handle shader_data::handle() const +{ + return m_handle; +} + +} diff --git a/source/opengl/data/shader_program_data.cpp b/source/opengl/data/shader_program_data.cpp new file mode 100755 index 0000000..84c4ebb --- /dev/null +++ b/source/opengl/data/shader_program_data.cpp @@ -0,0 +1,90 @@ + +#include "opengl/data/shader_program_data.hpp" + +#include +#include "GL/glew.h" +#include + +#include "util/for_each.hpp" +#include "util/logger.hpp" +#include "opengl/error.hpp" + +namespace zgl +{ + +std::error_code shader_program_data::build_from( + const shader_handle& vertex_shader, + const shader_handle& geometry_shader, + const shader_handle& fragment_shader, + shader_program_data& data +) { + auto error = std::error_code{}; + + auto check_error = [&error, ec = static_cast(GL_NO_ERROR)]() mutable -> std::error_code& + { + ec = glGetError(); + if (ec != GL_NO_ERROR) { + error = make_error_code(ec); + } + return error; + }; + + const auto program_id = glCreateProgram(); + if (check_error()) return error; + + using namespace std::string_view_literals; + + constexpr auto shader_names = std::array{ + "vertex"sv, "geometry"sv, "fragment"sv + }; + + for (const auto& [shader, name] : { + std::tie(vertex_shader, shader_names[0]), + std::tie(geometry_shader, shader_names[1]), + std::tie(fragment_shader, shader_names[2]) + }) { + if (shader.shader_id) { + glAttachShader(program_id, shader.shader_id); + } else { + ztu::logger::warn("Using default % shader", name); + } + } + + glLinkProgram(program_id); + if (check_error()) return error; + + auto status = GLint{ GL_FALSE }; + glGetProgramiv(program_id, GL_LINK_STATUS, &status); + if (check_error()) return error; + + if (status == GL_FALSE) { + GLint log_length{}; + glGetShaderiv(program_id, GL_INFO_LOG_LENGTH, &log_length); + + auto log = std::string(log_length, ' '); + glGetProgramInfoLog(program_id, log_length, nullptr, log.data()); + + ztu::logger::warn("Error while linking program: [%] %", log_length, log); + + return std::make_error_code(std::errc::io_error); + } + + + glUseProgram(0); + + ztu::for_each::argument( + [&](const auto& shader) + { + if (shader.shader_id) { + glDetachShader(program_id, shader.shader_id); + } + return true; + }, + vertex_shader, geometry_shader, fragment_shader + ); + + data = shader_program_data{ program_id }; + + return {}; +} +} diff --git a/source/opengl/data/shader_program_data.ipp b/source/opengl/data/shader_program_data.ipp new file mode 100755 index 0000000..3389ca7 --- /dev/null +++ b/source/opengl/data/shader_program_data.ipp @@ -0,0 +1,45 @@ +#ifndef INCLUDE_SHADER_PROGRAM_DATA_IMPLEMENTATION +# error Never include this file directly include 'shader_program_data.hpp' +#endif + +#include + +#include "util/for_each.hpp" +#include "util/logger.hpp" +#include "opengl/error.hpp" + +namespace zgl +{ +inline shader_program_data::shader_program_data(GLuint program_id) + : m_handle{ program_id } {} + + +inline shader_program_data::shader_program_data(shader_program_data&& other) noexcept +{ + m_handle = other.m_handle; + other.m_handle.program_id = 0; +} + +inline shader_program_data& shader_program_data::operator=(shader_program_data&& other) noexcept +{ + if (&other != this) + { + this->~shader_program_data(); + m_handle = other.m_handle; + other.m_handle.program_id = 0; + } + return *this; +} + +inline shader_program_data::~shader_program_data() +{ + if (m_handle.program_id) { + glDeleteProgram(m_handle.program_id); + } +} + +[[nodiscard]] inline shader_program_handle shader_program_data::handle() const +{ + return m_handle; +} +} diff --git a/source/opengl/data/texture_data.ipp b/source/opengl/data/texture_data.ipp new file mode 100644 index 0000000..c1d44c8 --- /dev/null +++ b/source/opengl/data/texture_data.ipp @@ -0,0 +1,79 @@ +#ifndef INCLUDE_TEXTURE_DATA_IMPLEMENTATION +# error Never include this file directly include 'texture_data.hpp' +#endif + +namespace zgl +{ +inline texture_data::texture_data(const GLuint texture_id) + : m_handle{ texture_id } {} + +inline texture_data::texture_data(texture_data&& other) noexcept + : m_handle{ other.m_handle } +{ + other.m_handle.texture_id = 0; +} + +inline texture_data& texture_data::operator=(texture_data&& other) noexcept +{ + if (&other != this) + { + this->~texture_data(); + + m_handle = other.m_handle; + + other.m_handle.texture_id = 0; + } + + return *this; +} + +template +std::error_code texture_data::build_from( + std::span buffer, + const GLenum format, + const GLenum type, + const GLsizei width, + const GLsizei height, + texture_data& data +) { + GLuint texture_id; + + glGenTextures(1, &texture_id); + glBindTexture(GL_TEXTURE_2D, texture_id); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glTexImage2D( + GL_TEXTURE_2D, 0, + GL_RGBA8, + width, + height, + 0, + format, type, + buffer.data() + ); + glGenerateMipmap(GL_TEXTURE_2D); + + glBindTexture(GL_TEXTURE_2D, 0); + + data = texture_data(texture_id); + + return {}; +} + +inline texture_data::~texture_data() +{ + if (m_handle.texture_id) + { + glDeleteTextures(1, &m_handle.texture_id); + } +} + +inline texture_handle texture_data::handle() const +{ + return { m_handle.texture_id }; +} +} diff --git a/source/opengl/handles/shader_program_handle.cpp b/source/opengl/handles/shader_program_handle.cpp new file mode 100644 index 0000000..589c813 --- /dev/null +++ b/source/opengl/handles/shader_program_handle.cpp @@ -0,0 +1,152 @@ +#include "opengl/handles/shader_program_handle.hpp" + +#include +// TODO remove +#include "opengl/error.hpp" +#include "util/logger.hpp" + +namespace zgl +{ + +shader_program_handle::attribute_support_type shader_program_handle::check_attribute_support( + const std::span attributes +) const { + auto attribute_candidates = attribute_support_type{}; + + auto error = std::error_code{}; + auto check_error = [&error, ec = static_cast(GL_NO_ERROR)]() mutable -> std::error_code& + { + ec = glGetError(); + if (ec != GL_NO_ERROR) { + error = make_error_code(ec); + } + return error; + }; + + auto curr_attribute_flag = attribute_support_type{ 1 }; + for (const auto& attribute : attributes) { + const auto location = glGetAttribLocation(program_id, attribute.name); + if (location == attribute.info.location) + { + attribute_candidates |= curr_attribute_flag; + } + curr_attribute_flag <<= 1; + } + + auto supported_attributes = attribute_support_type{}; + + GLint count; + glGetProgramiv(program_id, GL_ACTIVE_ATTRIBUTES, &count); + if (check_error()) ztu::logger::error("GL_err: %", error.message()); + + for (GLint i{}; i != count and attribute_candidates; ++i) + { + GLenum type; + GLint size; + GLsizei name_length; + auto name = std::array{}; + glGetActiveAttrib( + program_id, i, + name.size(), + &name_length, + &size, &type, + name.data() + ); + if (check_error()) ztu::logger::error("GL_err: %", error.message()); + + const auto name_view = std::string_view(name.data(), name_length); + + auto attribute_index = attribute_support_type{}; + for (auto candidates = attribute_candidates; candidates; candidates >>= 1) { + if (candidates & 1) + { + const auto& attribute = attributes[attribute_index]; + if (type == attribute.info.type and name_view == attribute.name) + { + const auto new_uniform_flag = attribute_support_type{ 1 } << attribute_index; + supported_attributes |= new_uniform_flag; + attribute_candidates ^= new_uniform_flag; + } + } + ++attribute_index; + } + } + + return supported_attributes; +} + +shader_program_handle::uniform_support_type shader_program_handle::check_uniform_support( + const std::span uniforms +) const { + auto uniform_candidates = uniform_support_type{}; + + auto error = std::error_code{}; + auto check_error = [&error, ec = static_cast(GL_NO_ERROR)]() mutable -> std::error_code& + { + ec = glGetError(); + if (ec != GL_NO_ERROR) { + error = make_error_code(ec); + } + return error; + }; + + auto curr_uniform_flag = uniform_support_type{ 1 }; + for (const auto& uniform : uniforms) + { + const auto location = glGetUniformLocation(program_id, uniform.name); + + if (location == uniform.info.location) + { + uniform_candidates |= curr_uniform_flag; + ztu::logger::debug("[%] '%': %.", program_id, uniform.name, location); + } + else + { + ztu::logger::debug("Expected '%' at % but was %.", uniform.name, uniform.info.location, location); + } + curr_uniform_flag <<= 1; + } + + auto supported_uniforms = uniform_support_type{}; + + GLint count; + glGetProgramiv(program_id, GL_ACTIVE_UNIFORMS, &count); + if (check_error()) ztu::logger::error("GL_err: %", error.message()); + + for (GLint i{}; i != count and uniform_candidates; ++i) + { + GLenum type; + GLint size; + GLsizei name_length; + auto name = std::array{}; + glGetActiveUniform( + program_id, i, + name.size(), + &name_length, + &size, &type, + name.data() + ); + if (check_error()) ztu::logger::error("GL_err: %", error.message()); + + const auto name_view = std::string_view(name.data(), name_length); + + auto uniform_index = uniform_support_type{}; + for (auto candidates = uniform_candidates; candidates; candidates >>= 1) + { + if (candidates & 1) + { + const auto& uniform = uniforms[uniform_index]; + if (type == uniform.info.type and name_view == uniform.name) + { + const auto new_uniform_flag = uniform_support_type{ 1 } << uniform_index; + supported_uniforms |= new_uniform_flag; + uniform_candidates ^= new_uniform_flag; + } + } + ++uniform_index; + } + } + + return supported_uniforms; +} +} diff --git a/source/opengl/shader_program_lookup.cpp b/source/opengl/shader_program_lookup.cpp new file mode 100644 index 0000000..fd9633a --- /dev/null +++ b/source/opengl/shader_program_lookup.cpp @@ -0,0 +1,195 @@ +#include "opengl/shader_program_lookup.hpp" + +#include +#include + +#include "util/logger.hpp" // TODO remove + +namespace zgl +{ + + +void shader_program_lookup::add( + const shader_program_handle& shader_program_handle, + const std::span all_attributes, + const std::span all_uniforms +) { + const auto attributes = shader_program_handle.check_attribute_support(all_attributes); + const auto uniforms = shader_program_handle.check_uniform_support(all_uniforms); + + ztu::logger::debug("add [%] uniforms: % attributes: %", + shader_program_handle.program_id, + std::bitset<32>(uniforms), + std::bitset<32>(attributes) + ); + + const auto lower_uniform = std::ranges::lower_bound( + m_mesh_shader_program_uniforms, + uniforms + ); + + const auto upper_uniform = std::find_if( + lower_uniform, m_mesh_shader_program_uniforms.end(), + [&](const auto& curr_uniforms) { + return curr_uniforms > uniforms; + } + ); + + const auto lower_index = lower_uniform - m_mesh_shader_program_uniforms.begin(); + const auto upper_index = upper_uniform - m_mesh_shader_program_uniforms.begin(); + + const auto lower_attribute = m_mesh_shader_program_attributes.begin() + lower_index; + const auto upper_attribute = m_mesh_shader_program_attributes.begin() + upper_index; + + const auto attribute_it = std::upper_bound( + lower_attribute, upper_attribute, + attributes, + [](const auto& attributes, const auto& entry) { + return attributes < entry.attributes; + } + ); + + const auto index = attribute_it - m_mesh_shader_program_attributes.begin(); + + const auto attribute_locations = attribute_location_flags( + attributes, all_attributes + ); + + m_mesh_shader_program_uniforms.insert( + m_mesh_shader_program_uniforms.begin() + index, uniforms + ); + + m_mesh_shader_program_attributes.emplace( + attribute_it, attributes, attribute_locations + ); + + m_mesh_shader_programs.insert( + m_mesh_shader_programs.begin() + index, shader_program_handle + ); +} + + +std::optional shader_program_lookup::find( + shader_program_handle::attribute_support_type attributes, + shader_program_handle::uniform_support_type uniforms, + const std::span all_attributes +) const { + + const auto lower_uniform = std::ranges::lower_bound( + m_mesh_shader_program_uniforms, + uniforms + ); + + if ( + lower_uniform == m_mesh_shader_program_uniforms.end() or + *lower_uniform != uniforms + ) { + return std::nullopt; + } + + const auto upper_uniform = std::find_if( + lower_uniform, m_mesh_shader_program_uniforms.end(), + [&](const auto& curr_uniforms) { + return curr_uniforms > uniforms; + } + ); + + const auto lower_index = lower_uniform - m_mesh_shader_program_uniforms.begin(); + const auto upper_index = upper_uniform - m_mesh_shader_program_uniforms.begin(); + + const auto relevant_attributes = std::span( + m_mesh_shader_program_attributes.begin() + lower_index, + m_mesh_shader_program_attributes.begin() + upper_index + ); + + const auto upper_attribute = std::upper_bound( + relevant_attributes.begin(), relevant_attributes.end(), + attributes, + [](const auto& attributes, const auto& entry) { + return attributes < entry.attributes; + } + ); + + auto entry_it = std::prev(upper_attribute); + + if ( + upper_attribute != relevant_attributes.begin() and + entry_it->attributes != attributes + ) { + + const auto locations = attribute_location_flags(attributes, all_attributes); + + auto found_match = false; + + while (entry_it != relevant_attributes.begin()) + { + --entry_it; + + const auto& [ curr_attributes, curr_locations ] = *entry_it; + + // The candidate may not support additional attributes + if (curr_attributes & ~attributes) + { + continue; + } + + // The candidate may not be missing 'inner' attributes, + // as this creates location alignment problems. + const auto higher_neighbour_matched = (curr_locations & locations) >> 1; + const auto bubbles = higher_neighbour_matched & ~curr_locations; + + if (not bubbles) + { + found_match = true; + break; + } + } + + if (not found_match) + { + return std::nullopt; + } + } + + const auto shader_program_index = entry_it.base() - m_mesh_shader_program_attributes.begin().base(); + + return m_mesh_shader_programs[shader_program_index]; +} + +shader_program_lookup::attribute_locations_type shader_program_lookup::attribute_location_flags( + shader_program_handle::attribute_support_type attributes, + std::span all_attributes +) { + auto location_flags = ztu::u32{ 0 }; + + auto index = std::size_t{}; + + while (attributes) + { + if (attributes & 1) + { + const auto location = all_attributes[index].info.location; + location_flags |= attribute_locations_type{ 1 } << location; + } + + attributes >>= 1; + ++index; + } + + return location_flags; +} + +void shader_program_lookup::print() { + for (std::size_t i{}; i != m_mesh_shader_program_uniforms.size(); ++i) { + const auto shader = m_mesh_shader_programs[i]; + const auto uniforms = m_mesh_shader_program_uniforms[i]; + const auto [ attributes, locations ] = m_mesh_shader_program_attributes[i]; + ztu::logger::debug("[%] uniforms: % attributes: % locations: %", + shader.program_id, + std::bitset<32>(uniforms), + std::bitset<32>(attributes), + std::bitset<32>(locations) + ); + } +} +} diff --git a/source/rendering/batch_renderers/mesh_batch_renderer.cpp b/source/rendering/batch_renderers/mesh_batch_renderer.cpp new file mode 100644 index 0000000..aabedd1 --- /dev/null +++ b/source/rendering/batch_renderers/mesh_batch_renderer.cpp @@ -0,0 +1,341 @@ +#include "rendering/batch_renderers/mesh_batch_renderer.hpp" + +#include "shader_program/uniforms/mesh_uniforms.hpp" +#include "util/unroll_bool_template.hpp" +#include "util/logger.hpp" // TODO remove +#include // TODOE remove + +namespace rendering +{ + +mesh_batch_renderer::mesh_batch_renderer(int render_mode_count) + : m_render_mode_count{ render_mode_count } {}; + +std::pair mesh_batch_renderer::lookup_batch( + const batch_components_type& batch_components +) const { + + const auto component_it = std::upper_bound( + m_component_lookup.begin(), m_component_lookup.end(), + batch_components, + [](const auto& components, const auto& entry) + { + return components < entry.first; + } + ); + + const auto index = component_it - m_component_lookup.begin(); + + const auto match = ( + index != 0 and m_component_lookup[index - 1].first == batch_components + ); + + return { index - static_cast(match), match }; +} + +std::optional mesh_batch_renderer::add( + const batch_components_type& batch_component, + const zgl::mesh_handle& mesh, + const aabb& bounding_box, + const zgl::model_matrix_handle& transform, + const zgl::material_handle& material, + const shader_program_lookups::mesh_lookup& shader_program_lookup +) { + const auto [ lookup_index, lookup_match ] = lookup_batch(batch_component); + + std::size_t batch_index; + batch_id_type batch_id; + + if (lookup_match) + { + batch_index = m_component_lookup[lookup_index].second; + batch_id = m_id_lookup[batch_index]; + } + else + { + auto base_requirements = requirements::mesh::flags::position; + + const auto [ vertex_comps, material_comps ] = batch_component; + + // If no texture is provided, the uniform color is provided by ambient light. + if ((material_comps & material_component::flags::texture) == material_component::flags::none) + { + base_requirements |= requirements::mesh::flags::uniform_color; + } + + ztu::logger::debug("vertex_comps: %", std::bitset<32>{ static_cast(static_cast(vertex_comps)) }); + ztu::logger::debug("material_comps: %", std::bitset<32>{ static_cast(static_cast(material_comps)) }); + ztu::logger::debug("lit reqs: %", std::bitset<32>{ static_cast(static_cast(shader_program::capabilities::mesh::lit.uniforms)) }); + + + for (std::size_t i{}; i != requirements::mesh::all.size(); ++i) + { + const auto& requirement = requirements::mesh::all[i]; + + if ( + ( + requirement.vertex_requirements != components::mesh_vertex::flags::none and + (vertex_comps & requirement.vertex_requirements) == requirement.vertex_requirements + ) + and + ( + requirement.material_requirements != material_component::flags::none and + (material_comps & requirement.material_requirements) == requirement.material_requirements + ) + ) { + base_requirements |= requirements::mesh::flags{ 1 << i }; + } + } + + ztu::logger::debug("base reqs: %", std::bitset<32>{ static_cast(static_cast(base_requirements)) }); + + + const auto base_shader = shader_program_lookup.find(base_requirements); + if (not base_shader) + { + ztu::logger::warn("Could not find base shader!"); + return std::nullopt; + } + + const auto point_shader = shader_program_lookup.find(base_requirements | requirements::mesh::flags::point); + if (not point_shader) + { + ztu::logger::warn("Could not find point shader!"); + return std::nullopt; + } + + const auto lit_shader = shader_program_lookup.find(base_requirements | requirements::mesh::flags::lit); + if (not lit_shader) + { + ztu::logger::warn("Could not find lit shader!"); + return std::nullopt; + } + + auto shader_programs = std::array{ + *base_shader, + *point_shader, + *base_shader, + *lit_shader + }; + + ztu::logger::debug( + "shaders: % % %", + base_shader->program_id, + point_shader->program_id, + lit_shader->program_id + ); + + batch_index = m_batches.size(); + batch_id = m_next_batch_id++; + m_batches.emplace_back(batch_type{}, batch_component); + m_id_lookup.push_back(batch_id); + m_component_lookup.emplace(m_component_lookup.begin() + lookup_index, batch_component, batch_index); + m_shader_programs.insert( + m_shader_programs.begin() + lookup_index, + shader_programs.begin(), shader_programs.end() + ); + } + + auto& batch = m_batches[batch_index].first; + + const auto mesh_id = batch.add(mesh, bounding_box, transform, material); + + return id_type{ batch_id, mesh_id }; +} + +std::optional mesh_batch_renderer::bounding_box(id_type id) +{ + const auto lookup_it = std::ranges::find(m_id_lookup, id.first); + + if (lookup_it == m_id_lookup.end()) + { + return std::nullopt; + } + + const auto batch_index = lookup_it - m_id_lookup.begin(); + auto& batch = m_batches[batch_index].first; + + return batch.bounding_box(id.second); +} + +bool mesh_batch_renderer::remove(const id_type id) +{ + const auto lookup_it = std::ranges::find(m_id_lookup, id.first); + + if (lookup_it == m_id_lookup.end()) + { + return false; + } + + const auto batch_index = lookup_it - m_id_lookup.begin(); + auto& batch = m_batches[batch_index].first; + + // If batches can be removed the indices in m_component_lookup need to be changed. + return batch.remove(id.second); +} + + + +template +void render_mesh_batch( + const zgl::shader_program_handle& shader_program, + const mesh_batch& batch, + const glm::mat4& vp_matrix, + const glm::mat4& view_matrix, + const GLenum draw_mode +) { + const auto meshes = batch.meshes(); + const auto transforms = batch.transforms(); + const auto textures = batch.textures(); + const auto surface_properties = batch.surface_properties(); + const auto alphas = batch.alphas(); + + /*ztu::logger::debug("meshes: %", meshes.size()); + ztu::logger::debug("transforms: %", transforms.size()); + ztu::logger::debug("textures: %", textures.size()); + ztu::logger::debug("surface_properties: %", surface_properties.size()); + ztu::logger::debug("alphas: %", alphas.size()); + + ztu::logger::debug("textured: % alpha: % lit: %", Textured, Alpha, Lit);*/ + + namespace uniforms = shader_program::uniforms::mesh; + + for (std::size_t i{}; i != meshes.size(); ++i) + { + //ztu::logger::debug("Mesh: %", i); + + const auto& mesh = meshes[i]; + const auto& model_matrix = transforms[i]; + + const auto mvp_matrix = vp_matrix * model_matrix; + + shader_program.set_uniform(mvp_matrix); + + if constexpr (Textured) + { + textures[i].bind(); + } + + if constexpr (Lit) + { + shader_program.set_uniform(model_matrix); + + // TODO more efficient set + const auto& properties = surface_properties[i]; + shader_program.set_uniform(properties.ambient_filter); + shader_program.set_uniform(properties.diffuse_filter); + shader_program.set_uniform(properties.specular_filter); + shader_program.set_uniform(properties.shininess); + } + + if constexpr (Alpha) + { + + shader_program.set_uniform(alphas[i]); + } + + //ztu::logger::debug("vao: % valid: %%", mesh.vao_id, std::boolalpha, (bool)glIsVertexArray(mesh.vao_id)); + + mesh.bind(); + + //ztu::logger::debug("glDrawElements(%, %)", draw_mode, mesh.index_count); + + glDrawElements(draw_mode, mesh.index_count, GL_UNSIGNED_INT, nullptr); + + //ztu::logger::debug("done"); + } +} + +void mesh_batch_renderer::render( + const modes::mesh render_mode, + const glm::mat4& vp_matrix, + const glm::mat4& view_matrix, + const glm::vec3& view_pos, + const lighting_setup& lights +) { + + namespace uniforms = shader_program::uniforms::mesh; + + const auto render_mode_index = static_cast(render_mode); + + const auto lines = render_mode == modes::mesh::wire_frame; + const auto points = render_mode == modes::mesh::points; + const auto lit = render_mode == modes::mesh::lit_faces; + + for (std::size_t i{}; i != m_batches.size(); ++i) { + + //ztu::logger::debug("batch: %", i); + + const auto& [ batch, batch_components ] = m_batches[i]; + + const auto [ vertex_components, material_components ] = batch_components; + + const auto textured = ( + (vertex_components & components::mesh_vertex::flags::tex_coord) != components::mesh_vertex::flags::none + and (material_components & material_component::flags::texture) != material_component::flags::none + ); + + const auto alpha = ( + (material_components & material_component::flags::transparency) != material_component::flags::none + ); + + const auto draw_mode = points ? GLenum{ GL_POINTS } : GLenum{ GL_TRIANGLES }; + + const auto& shader_program = m_shader_programs[i * m_render_mode_count + render_mode_index]; + + //ztu::logger::debug("shader_program: % valid: %%", shader_program.program_id, std::boolalpha, (bool)glIsProgram(shader_program.program_id)); + + shader_program.bind(); + + if (lit) + { + // TODO set more efficiently + shader_program.set_uniform(view_pos); + shader_program.set_uniform(lights.point_light_direction); + shader_program.set_uniform(lights.point_light_color); + shader_program.set_uniform(lights.ambient_light_color); + } + + if (textured) + { + constexpr auto texture_unit = 0; + glActiveTexture(GL_TEXTURE0 + texture_unit); + shader_program.set_uniform(texture_unit); + } + else + { + shader_program.set_uniform(glm::vec4(lights.ambient_light_color, 1.0f)); + } + + if (lines) + { + glPolygonMode(GL_FRONT, GL_LINE); + glPolygonMode(GL_BACK, GL_LINE); + } + + unroll_bool_function_template( + [&]() { + render_mesh_batch( + shader_program, + batch, + vp_matrix, + view_matrix, + draw_mode + ); + }, + textured, lit, alpha + ); + + if (lines) + { + glPolygonMode(GL_FRONT, GL_FILL); + glPolygonMode(GL_BACK, GL_FILL); + } + } + + zgl::texture_handle::unbind(); + zgl::mesh_handle::unbind(); + zgl::shader_program_handle::unbind(); +} + +} diff --git a/source/rendering/batch_renderers/point_cloud_batch_renderer.cpp b/source/rendering/batch_renderers/point_cloud_batch_renderer.cpp new file mode 100644 index 0000000..f5b4c6f --- /dev/null +++ b/source/rendering/batch_renderers/point_cloud_batch_renderer.cpp @@ -0,0 +1,243 @@ +#include "rendering/batch_renderers/point_cloud_batch_renderer.hpp" + +#include + +#include "rendering/requirements/point_cloud_requirements.hpp" +#include "shader_program/uniforms/point_cloud_uniforms.hpp" +#include "util/unroll_bool_template.hpp" + +namespace rendering +{ + +point_cloud_batch_renderer::point_cloud_batch_renderer(int render_mode_count) + : m_render_mode_count{ render_mode_count } {}; + +std::pair point_cloud_batch_renderer::lookup_batch( + const batch_components_type& batch_component +) const { + const auto component_it = std::upper_bound( + m_component_lookup.begin(), m_component_lookup.end(), + batch_component, + [](const auto& batch_component, const auto& entry) + { + return batch_component < entry.first; + } + ); + + const auto index = component_it - m_component_lookup.begin(); + + const auto match = ( + index == 0 or m_component_lookup[index - 1].first == batch_component + ); + + return { index - static_cast(match), match }; +} + +std::optional point_cloud_batch_renderer::add( + batch_components_type batch_components, + const zgl::point_cloud_handle& point_cloud, + const aabb& bounding_box, + const zgl::model_matrix_handle& transform, + const shader_program_lookups::point_cloud_lookup& shader_program_lookup +) { + const auto [ lookup_index, lookup_match ] = lookup_batch(batch_components); + + std::size_t batch_index; + batch_id_type batch_id; + + if (lookup_match) { + batch_index = m_component_lookup[lookup_index].second; + batch_id = m_id_lookup[batch_index]; + } + else + { + auto base_requirements = requirements::point_cloud::flags{}; + + const auto vertex_comps = batch_components; + + for (std::size_t i{}; i != requirements::point_cloud::all.size(); ++i) + { + const auto& requirement = requirements::point_cloud::all[i]; + + if ( + (vertex_comps & requirement.vertex_requirements) != components::point_cloud_vertex::flags::none + ) { + base_requirements |= requirements::point_cloud::flags{ 1 << i }; + } + } + + const auto uniform_color_shader = shader_program_lookup.find( + base_requirements | requirements::point_cloud::flags::uniform_color + ); + + if (not uniform_color_shader) + { + return std::nullopt; + } + + const auto rainbow_shader = shader_program_lookup.find( + base_requirements | requirements::point_cloud::flags::rainbow + ); + + if (not rainbow_shader) + { + return std::nullopt; + } + + auto shader_programs = std::array{ + *uniform_color_shader, + *rainbow_shader + }; + + batch_index = m_batches.size(); + batch_id = m_next_batch_id++; + m_batches.emplace_back(batch_type{}, batch_components); + m_id_lookup.push_back(batch_id); + m_component_lookup.emplace(m_component_lookup.begin() + lookup_index, batch_components, batch_index); + m_shader_programs.insert( + m_shader_programs.begin() + lookup_index, + shader_programs.begin(), shader_programs.end() + ); + } + + auto& batch = m_batches[batch_index].first; + + const auto mesh_id = batch.add(point_cloud, bounding_box, transform); + + return id_type{ batch_id, mesh_id }; +} + +std::optional point_cloud_batch_renderer::bounding_box(id_type id) +{ + const auto lookup_it = std::ranges::find(m_id_lookup, id.first); + + if (lookup_it == m_id_lookup.end()) + { + return std::nullopt; + } + + const auto batch_index = lookup_it - m_id_lookup.begin(); + auto& batch = m_batches[batch_index].first; + + return batch.bounding_box(id.second); +} + +bool point_cloud_batch_renderer::remove(const id_type id) +{ + const auto lookup_it = std::ranges::find(m_id_lookup, id.first); + + if (lookup_it == m_id_lookup.end()) + { + return false; + } + + const auto batch_index = lookup_it - m_id_lookup.begin(); + auto& batch = m_batches[batch_index].first; + + // If batches can be removed the indices in m_component_lookup need to be changed. + return batch.remove(id.second); +} + + +template +void render_point_cloud_batch( + const zgl::shader_program_handle& shader_program, + const point_cloud_batch& batch, + const glm::mat4& vp_matrix, + const glm::vec3& camera_position +) { + const auto point_clouds = batch.point_clouds(); + const auto transforms = batch.transforms(); + + namespace uniforms = shader_program::uniforms::point_cloud; + + for (std::size_t i{}; i != point_clouds.size(); ++i) + { + const auto& point_cloud = point_clouds[i]; + const auto& model_matrix = transforms[i]; + + // TODO check order + const auto mvp_matrix = vp_matrix * model_matrix; + + shader_program.set_uniform(mvp_matrix); + + if constexpr (Normals) + { + shader_program.set_uniform(model_matrix); + shader_program.set_uniform(camera_position); + } + + point_cloud.bind(); + + using block_index_type = ztu::u16; + static constexpr auto block_size = static_cast( + std::numeric_limits::max() + ); + + for (GLsizei j{}; j < point_cloud.point_count; j += block_size) + { + const auto points_left = static_cast(point_cloud.point_count) - j; + const auto points_in_block = std::min(points_left, block_size); + glDrawArrays( + GL_POINTS, + j, + static_cast(points_in_block) + ); + } + } +} + +void point_cloud_batch_renderer::render( + const modes::point_cloud render_mode, + const glm::mat4& vp_matrix, + const glm::vec3& camera_position, + const lighting_setup& +) { + + namespace uniforms = shader_program::uniforms::point_cloud; + + const auto render_mode_index = static_cast(render_mode); + + glEnable(GL_PROGRAM_POINT_SIZE); + glEnable(GL_POINT_SMOOTH); + + const auto rainbow = render_mode == modes::point_cloud::rainbow; + + + for (std::size_t i{}; i != m_batches.size(); ++i) { + + const auto& [ batch, vertex_components ] = m_batches[i]; + + const auto normals = static_cast(vertex_components & components::point_cloud_vertex::flags::normal); + + const auto& shader_program = m_shader_programs[i * m_render_mode_count + render_mode_index]; + + shader_program.bind(); + + if (rainbow) + { + shader_program.set_uniform(0.0f); // TODO fix + shader_program.set_uniform(0.0f); // TODO fix + } + + unroll_bool_function_template( + [&]() { + render_point_cloud_batch( + shader_program, + batch, + vp_matrix, + camera_position + ); + }, + normals + ); + } + + glDisable(GL_PROGRAM_POINT_SIZE); + glDisable(GL_POINT_SMOOTH); + + zgl::point_cloud_handle::unbind(); + zgl::shader_program_handle::unbind(); +} + +} diff --git a/source/rendering/batches/mesh_batch.ipp b/source/rendering/batches/mesh_batch.ipp new file mode 100644 index 0000000..e7e0d68 --- /dev/null +++ b/source/rendering/batches/mesh_batch.ipp @@ -0,0 +1,147 @@ +#ifndef INCLUDE_MESH_BATCH_IMPLEMENTATION +# error Never include this file directly include 'mesh_batch.hpp' +#endif + +#include + + +inline mesh_batch::id_type mesh_batch::add( + const zgl::mesh_handle& mesh, + const aabb& bounding_box, + const zgl::model_matrix_handle& transform, + const zgl::material_handle& material +) { + std::size_t index; + if (material.texture) + { + // Sort by texture id if possible, so meshes the same texture are rendered consecutively. + const auto texture_it = std::ranges::upper_bound( + m_textures, + *material.texture, + [](const auto& lhs, const auto& rhs) + { + return lhs.texture_id < rhs.texture_id; + } + ); + index = texture_it - m_textures.begin(); + } + else + { + // TODO inserting by vao might split up texture sequence, this needs more attention + // Otherwise, sort by vao, so meshes with the same vertices are rendered consecutively. + const auto mesh_it = std::ranges::upper_bound( + m_meshes, + mesh, + [](const auto& lhs, const auto& rhs) + { + return lhs.vao_id < rhs.vao_id; + } + ); + index = mesh_it - m_meshes.begin(); + } + + m_meshes.insert(m_meshes.begin() + index, mesh); + m_bounding_boxes.insert(m_bounding_boxes.begin() + index, bounding_box); + m_transforms.insert(m_transforms.begin() + index, transform); + + if (material.texture) + { + m_textures.insert(m_textures.begin() + index, *material.texture); + } + + if (material.surface_properties) + { + m_surface_properties.insert(m_surface_properties.begin() + index, *material.surface_properties); + } + + if (material.alpha) + { + m_alphas.insert(m_alphas.begin() + index, *material.alpha); + } + + const auto mesh_id = m_next_mesh_id++; + + m_id_lookup.insert(m_id_lookup.begin() + index, mesh_id); + + return mesh_id; +} + +std::optional mesh_batch::bounding_box(id_type id) +{ + const auto lookup_it = std::ranges::find(m_id_lookup, id); + + if (lookup_it == m_id_lookup.end()) + { + return std::nullopt; + } + + const auto index = lookup_it - m_id_lookup.begin(); + + const auto& base_bounding_box = m_bounding_boxes[index]; + const auto& transform = m_transforms[index]; + + return base_bounding_box.transformed(transform); +} + +inline bool mesh_batch::remove(const id_type id) +{ + const auto lookup_it = std::ranges::find(m_id_lookup, id); + + if (lookup_it == m_id_lookup.end()) { + return false; + } + + const auto index = lookup_it - m_id_lookup.begin(); + + m_id_lookup.erase(m_id_lookup.begin() + index); + m_meshes.erase(m_meshes.begin() + index); + m_bounding_boxes.erase(m_bounding_boxes.begin() + index); + m_transforms.erase(m_transforms.begin() + index); + + if (not m_textures.empty()) + { + m_textures.erase(m_textures.begin() + index); + } + + if (not m_surface_properties.empty()) + { + m_surface_properties.erase(m_surface_properties.begin() + index); + } + + if (not m_alphas.empty()) + { + m_alphas.erase(m_alphas.begin() + index); + } + + return true; +} + +inline std::span mesh_batch::meshes() const +{ + return m_meshes; +} + +inline std::span mesh_batch::bounding_boxes() const +{ + return m_bounding_boxes; +} + +inline std::span mesh_batch::transforms() const +{ + return m_transforms; +} + +inline std::span mesh_batch::textures() const +{ + return m_textures; +} + +inline std::span mesh_batch::surface_properties() const +{ + return m_surface_properties; +} + +inline std::span mesh_batch::alphas() const +{ + return m_alphas; +} diff --git a/source/rendering/batches/point_cloud_batch.ipp b/source/rendering/batches/point_cloud_batch.ipp new file mode 100644 index 0000000..20f5bf3 --- /dev/null +++ b/source/rendering/batches/point_cloud_batch.ipp @@ -0,0 +1,69 @@ +#ifndef INCLUDE_POINT_CLOUD_BATCH_IMPLEMENTATION +# error Never include this file directly include 'point_cloud_batch.hpp' +#endif + + +inline point_cloud_batch::id_type point_cloud_batch::add( + const zgl::point_cloud_handle& point_cloud, + const aabb& bounding_box, + const zgl::model_matrix_handle& transform +) { + const auto new_id = m_next_id++; + + m_point_clouds.push_back(point_cloud); + m_transforms.push_back(transform); + m_bounding_boxes.push_back(bounding_box); + m_id_lookup.push_back(new_id); + + return new_id; +} + +std::optional point_cloud_batch::bounding_box(id_type id) +{ + const auto lookup_it = std::ranges::find(m_id_lookup, id); + + if (lookup_it == m_id_lookup.end()) + { + return std::nullopt; + } + + const auto index = lookup_it - m_id_lookup.begin(); + + const auto& base_bounding_box = m_bounding_boxes[index]; + const auto& transform = m_transforms[index]; + + return base_bounding_box.transformed(transform); +} + +inline bool point_cloud_batch::remove(id_type id) +{ + const auto lookup_it = std::ranges::find(m_id_lookup, id); + + if (lookup_it == m_id_lookup.end()) + { + return false; + } + + const auto index = lookup_it - m_id_lookup.begin(); + + m_id_lookup.erase(m_id_lookup.begin() + index); + m_point_clouds.erase(m_point_clouds.begin() + index); + m_transforms.erase(m_transforms.begin() + index); + + return true; +} + +inline std::span point_cloud_batch::point_clouds() const +{ + return m_point_clouds; +} + +inline std::span point_cloud_batch::transforms() const +{ + return m_transforms; +} + +inline std::span point_cloud_batch::bounding_boxes() const +{ + return m_bounding_boxes; +} diff --git a/source/rendering/shader_program_lookups/mesh_lookup.cpp b/source/rendering/shader_program_lookups/mesh_lookup.cpp new file mode 100644 index 0000000..5566db7 --- /dev/null +++ b/source/rendering/shader_program_lookups/mesh_lookup.cpp @@ -0,0 +1,58 @@ +#include "rendering/shader_program_lookups/mesh_lookup.hpp" + +#include "util/logger.hpp" // TODO remove +#include // TODO remove + +namespace rendering::shader_program_lookups +{ + +void mesh_lookup::add( + const zgl::shader_program_handle& shader_program_handle +) { + m_shader_program_lookup.add( + shader_program_handle, + shader_program::attributes::mesh::all, + shader_program::uniforms::mesh::all + ); +} + +std::optional mesh_lookup::find( + requirements::mesh::flags requirements +) const { + auto capability = shader_program::capabilities::mesh::type{}; + + auto index = std::size_t{}; + + auto requirement_flags = static_cast(requirements); + + while (requirement_flags) + { + if (requirement_flags & 1) + { + const auto shader_requirements_index = requirements::mesh::all[index].shader_program_requirement_index; + const auto& [ attributes, uniforms ] = shader_program::capabilities::mesh::all[shader_requirements_index]; + capability.attributes |= attributes; + capability.uniforms |= uniforms; + } + + requirement_flags >>= 1; + ++index; + } + + // TODO if not textured and not colored add ucolor "for free" + + ztu::logger::debug("attributes reqs: %", std::bitset<32>{ static_cast(static_cast(capability.attributes)) }); + ztu::logger::debug("uniforms reqs: %", std::bitset<32>{ static_cast(static_cast(capability.uniforms)) }); + + return m_shader_program_lookup.find( + static_cast(capability.attributes), + static_cast(capability.uniforms), + shader_program::attributes::mesh::all + ); +} + +void mesh_lookup::print() { + m_shader_program_lookup.print(); +} + +} diff --git a/source/rendering/shader_program_lookups/point_cloud_lookup.cpp b/source/rendering/shader_program_lookups/point_cloud_lookup.cpp new file mode 100644 index 0000000..d5ebff4 --- /dev/null +++ b/source/rendering/shader_program_lookups/point_cloud_lookup.cpp @@ -0,0 +1,47 @@ +#include "rendering/shader_program_lookups/point_cloud_lookup.hpp" + +namespace rendering::shader_program_lookups +{ + +void point_cloud_lookup::add( + const zgl::shader_program_handle& shader_program_handle +) { + m_program_lookup.add( + shader_program_handle, + shader_program::attributes::point_cloud::all, + shader_program::uniforms::point_cloud::all + ); +} + + +std::optional point_cloud_lookup::find( + requirements::point_cloud::flags requirements +) const { + auto capability = shader_program::capabilities::point_cloud::type{}; + + auto index = std::size_t{}; + + auto requirement_flags = static_cast(requirements); + + while (requirement_flags) + { + if (requirement_flags & 1) + { + const auto shader_requirements_index = requirements::point_cloud::all[index].shader_program_requirement_index; + const auto& [ attributes, uniforms ] = shader_program::capabilities::point_cloud::all[shader_requirements_index]; + capability.attributes |= attributes; + capability.uniforms |= uniforms; + } + + requirement_flags >>= 1; + ++index; + } + + return m_program_lookup.find( + static_cast(capability.attributes), + static_cast(capability.uniforms), + shader_program::attributes::point_cloud::all + ); +} + +} diff --git a/source/scene/flying_camera.cpp b/source/scene/flying_camera.cpp new file mode 100755 index 0000000..79ec883 --- /dev/null +++ b/source/scene/flying_camera.cpp @@ -0,0 +1,117 @@ +#include "scene/flying_camera.hpp" + +#include +#include +#include +#include "glm/gtx/string_cast.hpp" +#include "glm/gtx/euler_angles.hpp" + +#include "util/logger.hpp" // TODO remove +#include "glm/gtx/vector_angle.hpp" + +flying_camera::flying_camera(const float yaw, const float pitch, const float roll) { + m_world_rotation = glm::mat3(glm::eulerAngleYXZ(yaw, pitch, roll)); + m_world_up = m_world_rotation * glm::vec3(0, 1, 0); + + m_velocity = glm::vec3(0.f, 0.f, 0.f); + m_pitch = 0.f; + m_yaw = glm::radians(-90.f); + m_roll = 0.f; +} + + +void flying_camera::update( + const float time_delta, + const glm::vec2 mouse_pos_delta, + const float mouse_wheel_delta, + camera_view& view +) { + static constexpr auto pi = std::numbers::pi_v; + static constexpr auto epsilon = std::numeric_limits::epsilon(); + static constexpr auto world_up = glm::vec3(0.0f, 1.0f, 0.0f); + + static constexpr auto friction_coefficient = 25.0f; + static constexpr auto walk_acceleration = 3000.0f; + static constexpr auto max_velocity = 4000.0f; + + static constexpr float max_pitch = (pi / 2.0f) - epsilon; + static constexpr float min_fov = 0.01f * pi; + static constexpr float max_fov = 0.8f * pi; + + m_yaw += mouse_pos_delta.x; + m_pitch -= mouse_pos_delta.y; + + m_yaw = std::fmod(m_yaw, 2.0f * pi); + m_pitch = std::clamp(m_pitch, -max_pitch, max_pitch); + + view.front.x = std::cos(m_yaw) * std::cos(m_pitch); + view.front.y = std::sin(m_pitch); + view.front.z = std::sin(m_yaw) * std::cos(m_pitch); + view.front = glm::normalize(view.front); + + view.right = glm::normalize(glm::cross(view.front, world_up)); + view.up = glm::normalize(glm::cross(view.right, view.front)); + + view.front = m_world_rotation * view.front; + view.up = m_world_rotation * view.up; + view.right = m_world_rotation * view.right; + + view.fov *= 1.0f + mouse_wheel_delta; + view.fov = std::clamp(view.fov, min_fov, max_fov); + + using kb = sf::Keyboard; + auto acceleration = glm::vec3{ 0.0f }; + + if (kb::isKeyPressed(kb::W)) acceleration += view.front; + if (kb::isKeyPressed(kb::S)) acceleration -= view.front; + + if (kb::isKeyPressed(kb::A)) acceleration -= view.right; + if (kb::isKeyPressed(kb::D)) acceleration += view.right; + + if (kb::isKeyPressed(kb::Space)) acceleration += m_world_up; // TODO fix + if (kb::isKeyPressed(kb::LControl)) acceleration -= m_world_up; + + acceleration *= walk_acceleration; + + const auto acc = glm::length(acceleration); + if (acc > epsilon) + { + acceleration *= walk_acceleration / acc; + } + + if (kb::isKeyPressed(kb::LShift)) acceleration *= 2.0f; + + m_velocity += acceleration * time_delta; + + const float drag = std::exp(-friction_coefficient * time_delta); + m_velocity *= drag; + + + const auto speed = time_delta * glm::length(m_velocity); + if (speed > max_velocity) { + m_velocity *= (max_velocity / speed) * max_velocity; + } + + view.position += m_velocity * time_delta; +} + +void flying_camera::look_at( + const glm::vec3& origin, + const glm::vec3& target, + camera_view& view +) { + static constexpr auto world_up = glm::vec3(0.0f, 1.0f, 0.0f); + // TODO inverted matrix + + view.position = origin; + view.front = glm::normalize(target - origin); + view.right = glm::normalize(glm::cross(view.front, world_up)); + view.up = glm::normalize(glm::cross(view.right, view.front)); + view.fov = std::numbers::pi_v / 2.0f; + + m_velocity = { 0.0f, 0.0f, 0.0f }; + + m_pitch = std::asin(glm::dot(view.front, world_up)); + m_yaw = std::atan2(view.front.z, view.front.x); + m_roll = std::atan2(view.up.x, view.up.y); +} diff --git a/source/viewer/asset_loader.cpp b/source/viewer/asset_loader.cpp new file mode 100644 index 0000000..0f0bfc9 --- /dev/null +++ b/source/viewer/asset_loader.cpp @@ -0,0 +1,636 @@ +#include "viewer/asset_loader.hpp" + +#include + +#include "geometry/aabb.hpp" +#include "util/logger.hpp" +#include "glm/gtx/string_cast.hpp" // TODO remove + +namespace viewer +{ +std::error_code asset_loader::init( + components::mesh_vertex::flags enabled_mesh_components, + material_component::flags enabled_material_components, + components::point_cloud_vertex::flags enabled_point_cloud_components, + const dynamic_material_data& default_material +) { + + //m_ctx.setActive(true); + + m_enabled_mesh_components = enabled_mesh_components; + m_enabled_material_components = enabled_material_components; + m_enabled_point_cloud_components = enabled_point_cloud_components; + + m_dynamic_material_data_buffer.push_back(default_material); + + return create_gl_materials(); +} + +std::error_code asset_loader::load_shader( + const GLenum type, + const std::filesystem::path& filename, + zgl::shader_handle& shader_handle +) { + + auto& [ buffer_source, buffer_type ] = m_dynamic_shader_data_buffer; + + std::stringstream source_stream; + static constexpr auto glsl_version = 460; + + source_stream << "#version " << glsl_version << '\n'; + source_stream << "#define " << "hello" << '\n'; + source_stream << buffer_source; + + buffer_type = type; + + if (filename.empty()) + { + buffer_source = ""; + } + else + { + if (const auto e = m_shader_loader.load(filename, buffer_source)) { + ztu::logger::warn( + "Could not load shader_program_data source file %: [%] %", + filename, + e.category().name(), + e.message() + ); + } + } + + if (const auto e = create_gl_shader()) { + return e; + } + + shader_handle = m_gl_shader_data.back().handle(); + + return {}; +} + +std::error_code asset_loader::build_shader_program( + const zgl::shader_handle& vertex_shader, + const zgl::shader_handle& geometry_shader, + const zgl::shader_handle& fragment_shader, + zgl::shader_program_handle& shader_program_handle +) { + + auto program_data = zgl::shader_program_data{}; + + if (const auto e = zgl::shader_program_data::build_from( + vertex_shader, + geometry_shader, + fragment_shader, + program_data + )) { + return e; + } + + shader_program_handle = program_data.handle(); + + m_gl_shader_program_data.emplace_back(std::move(program_data)); + + return {}; +} + +std::error_code asset_loader::load_asset( + const std::string& format, + const std::filesystem::path& filename, + std::vector>& dynamic_mesh_handles, + std::vector& dynamic_point_cloud_handles +) { + std::error_code error; + + if ((error = load_mesh( + format, filename, dynamic_mesh_handles + ))) { + if ( + error.category() == std::generic_category() and + static_cast(error.value()) == std::errc::invalid_argument + ) { + error = load_point_cloud(format, filename, dynamic_point_cloud_handles); + } + } + + return error; +} + +std::error_code asset_loader::load_mesh( + const std::string& format, + const std::filesystem::path& filename, + std::vector>& dynamic_mesh_handles +) { + const auto mesh_loader_id = m_mesh_loader.find_loader(format); + + if (not mesh_loader_id) + { + return std::make_error_code(std::errc::invalid_argument); + } + + if (const auto e = m_mesh_loader.read( + *mesh_loader_id, + filename, + m_dynamic_mesh_data_buffer, + m_enabled_mesh_components, + m_dynamic_material_data_buffer, + m_enabled_material_components, + next_materials_id, + true + )) { + return e; + } + + return process_materials_and_meshes(dynamic_mesh_handles); +} + +std::error_code asset_loader::load_point_cloud( + const std::string& format, + const std::filesystem::path& filename, + std::vector& dynamic_point_cloud_handles +) { + const auto point_cloud_loader_id = m_point_cloud_loader.find_loader(format); + + if (not point_cloud_loader_id) + { + return std::make_error_code(std::errc::invalid_argument); + } + + if (const auto e = m_point_cloud_loader.read( + *point_cloud_loader_id, + filename, + m_dynamic_point_cloud_buffer, + true + )) { + return e; + } + + return process_point_clouds(dynamic_point_cloud_handles); +} + +std::error_code asset_loader::load_asset_directory( + const std::string& format, + const std::filesystem::path& path, + std::vector>& dynamic_mesh_handles, + std::vector& dynamic_point_cloud_handles +) { + std::error_code error; + + if ((error = load_mesh_directory( + format, path, dynamic_mesh_handles + ))) { + if ( + error.category() == std::generic_category() and + static_cast(error.value()) == std::errc::invalid_argument + ) { + error = load_point_cloud_directory(format, path, dynamic_point_cloud_handles); + } + } + + return error; +} +std::error_code asset_loader::load_mesh_directory( + const std::string& format, + const std::filesystem::path& path, + std::vector>& dynamic_mesh_handles +) { + const auto mesh_loader_id = m_mesh_loader.find_loader(format); + + if (not mesh_loader_id) + { + return std::make_error_code(std::errc::invalid_argument); + } + + if (const auto e = m_mesh_loader.read_directory( + *mesh_loader_id, + path, + m_dynamic_mesh_data_buffer, + m_enabled_mesh_components, + m_dynamic_material_data_buffer, + m_enabled_material_components, + next_materials_id, + true + )) { + return e; + } + + return process_materials_and_meshes(dynamic_mesh_handles); +} + + +std::error_code asset_loader::load_point_cloud_directory( + const std::string& format, + const std::filesystem::path& path, + std::vector& dynamic_point_cloud_handles +) { + const auto point_cloud_loader_id = m_point_cloud_loader.find_loader(format); + + if (not point_cloud_loader_id) + { + return std::make_error_code(std::errc::invalid_argument); + } + + if (const auto e = m_point_cloud_loader.read_directory( + *point_cloud_loader_id, + path, + m_dynamic_point_cloud_buffer, + true + )) { + return e; + } + + return process_point_clouds(dynamic_point_cloud_handles); +} + +std::error_code asset_loader::process_materials_and_meshes( + std::vector>& dynamic_mesh_handles +) { + + const auto material_count_before = m_gl_material_data_references.size(); + + if (const auto e = create_gl_materials()) + { + m_dynamic_mesh_data_buffer.clear(); + return e; + } + + const auto new_materials = std::span( + m_gl_material_data_references.begin() + material_count_before, + m_gl_material_data_references.end() + ); + + const auto mesh_count_before = m_gl_mesh_data.size(); + + create_gl_meshes(new_materials); + + const auto new_meshes = std::span( + m_gl_mesh_data.begin() + mesh_count_before, + m_gl_mesh_data.end() + ); + + const auto dynamic_mesh_count_before = dynamic_mesh_handles.size(); + dynamic_mesh_handles.resize(dynamic_mesh_handles.size() + new_meshes.size()); + + std::ranges::transform( + new_meshes, + dynamic_mesh_handles.begin() + dynamic_mesh_count_before, + [&](const auto& entry) + { + const auto& [ gl_mesh_data, bounding_box ] = entry; + const auto material_id = gl_mesh_data.material_id(); + + auto material_index = std::size_t{ 0 }; + + if (material_id != 0) + { + const auto material_reference_it = std::ranges::find_if( + new_materials, + [&material_id](const auto& entry) { + return entry.first == material_id; + } + ); + + if (material_reference_it == new_materials.end()) + { + ztu::logger::error( + "Something went horribly wrong while searching for material. Falling back to default material" + ); + } + else + { + material_index = material_reference_it.base() - m_gl_material_data_references.begin().base(); + } + } + + const auto& gl_material = m_gl_material_data[material_index]; + + //ztu::logger::debug("mesh components: %", static_cast(gl_mesh_data.components())); + //ztu::logger::debug("material components: %", static_cast(gl_material.components())); + + return std::make_pair( + dynamic_mesh_handle_type{ + .handle = gl_mesh_data.handle(), + .bounding_box = bounding_box, + .components = gl_mesh_data.components() + }, + dynamic_material_handle_type{ + .handle = gl_material.handle(), + .components = gl_material.components() + } + ); + } + ); + + return {}; +} + +std::error_code asset_loader::process_point_clouds( + std::vector& dynamic_point_cloud_handles +) { + const auto point_cloud_count_before = m_gl_point_cloud_data.size(); + + create_gl_point_clouds(); + + const auto new_point_clouds = std::span( + m_gl_point_cloud_data.begin() + point_cloud_count_before, + m_gl_point_cloud_data.end() + ); + + const auto dynamic_point_cloud_count_before = dynamic_point_cloud_handles.size(); + dynamic_point_cloud_handles.resize(dynamic_point_cloud_handles.size() + new_point_clouds.size()); + + std::ranges::transform( + new_point_clouds, + dynamic_point_cloud_handles.begin() + dynamic_point_cloud_count_before, + [&](const auto& gl_point_cloud_data) + { + const auto& [ data, box ] = gl_point_cloud_data; + return dynamic_point_cloud_handle_type{ + .handle = data.handle(), + .bounding_box = box, + .components = data.components() + }; + } + ); + + return {}; +} + +std::error_code asset_loader::create_gl_materials() +{ + auto error = std::error_code{}; + + for (const auto& material_data : m_dynamic_material_data_buffer) + { + auto gl_material_data = zgl::material_data{}; + + if ((error = zgl::material_data::build_from( + material_data.texture(), + material_data.surface_properties(), + material_data.transparency(), + material_data.components(), + gl_material_data + ))) { + ztu::logger::error( + "Error while creating material gpu handle: [%] %", + error.category().name(), + error.message() + ); + } + else + { + m_gl_material_data.emplace_back(std::move(gl_material_data)); + m_gl_material_data_references.emplace_back(next_materials_id, 0); + } + + ++next_materials_id; + } + + m_dynamic_material_data_buffer.clear(); + + return error; +} + + +void asset_loader::create_gl_meshes(std::span material_references) +{ + auto component_type_buffer = std::array(components::mesh_vertex::count)>{}; + auto component_length_buffer = std::array(components::mesh_vertex::count)>{}; + + auto component_stride = GLsizei{}; + auto component_count = ztu::u32{}; + + for (auto& mesh_data : m_dynamic_mesh_data_buffer) + { + if (mesh_data.triangles().empty()) + { + ztu::logger::warn("Skipping mesh with empty index buffer."); + continue; + } + + const auto material_id = mesh_data.material_id(); + + auto material_index = std::size_t{ 0 }; + + if (material_id != 0) // Default material is always there + { + const auto material_reference_it = std::ranges::find_if( + material_references, + [&material_id](const material_reference_entry_type& entry) { + return entry.first == material_id; + } + ); + + if (material_reference_it == material_references.end()) + { + ztu::logger::error("Skipping mesh because referenced material cannot be found"); + continue; + } + + material_index = material_reference_it - material_references.begin(); + } + + // Add normals if missing + if ((mesh_data.components() & components::mesh_vertex::flags::normal) == components::mesh_vertex::flags::none) + { + ztu::logger::warn("Model is missing normal vectors, so they are estimated!"); + estimate_normals( + mesh_data.positions(), + mesh_data.triangles(), + mesh_data.normals() + ); + mesh_data.components() |= components::mesh_vertex::flags::normal; + } + + auto mesh_box = aabb{}; + mesh_box.add_points(mesh_data.positions()); + + mesh_data.build_vertex_buffer( + m_vertex_buffer, + component_count, + component_type_buffer, + component_length_buffer, + component_stride + ); + + auto gl_mesh_data = zgl::mesh_data{}; + + const auto& first_triangle = mesh_data.triangles().front(); + + // TODO make span of size component_count + + if (const auto e = zgl::mesh_data::build_from( + m_vertex_buffer, + std::span(component_type_buffer).subspan(0, component_count), + std::span(component_length_buffer).subspan(0, component_count), + component_stride, + std::span( + first_triangle.data(), + mesh_data.triangles().size() * first_triangle.size() + ), + mesh_data.material_id(), + mesh_data.components(), + gl_mesh_data + )) { + ztu::logger::error( + "Error while creating opengl mesh data: [%] %\nMesh will be skipped.", + e.category().name(), + e.message() + ); + } + + ++m_gl_material_data_references[material_index].second; + + m_gl_mesh_data.emplace_back(std::move(gl_mesh_data), mesh_box); + } + + m_dynamic_mesh_data_buffer.clear(); +} + +void asset_loader::create_gl_point_clouds() +{ + auto component_type_buffer = std::array(components::point_cloud_vertex::count)>{}; + auto component_length_buffer = std::array(components::point_cloud_vertex::count)>{}; + auto component_stride = GLsizei{}; + auto component_count = ztu::u32{}; + + for (const auto& point_cloud_data : m_dynamic_point_cloud_buffer) + { + if (point_cloud_data.positions().empty()) + { + ztu::logger::warn("Skipping point cloud without points."); + continue; + } + + auto point_cloud_box = aabb{}; + point_cloud_box.add_points(point_cloud_data.positions()); + + point_cloud_data.build_vertex_buffer( + m_vertex_buffer, + component_count, + component_type_buffer, + component_length_buffer, + component_stride + ); + + auto gl_point_cloud_data = zgl::point_cloud_data{}; + + if (const auto e = zgl::point_cloud_data::build_from( + m_vertex_buffer, + component_type_buffer, + component_length_buffer, + component_stride, + gl_point_cloud_data + )) { + ztu::logger::error( + "Error while creating opengl point cloud data: [%] %\nPoint cloud will be skipped.", + e.category().name(), + e.message() + ); + } + + m_gl_point_cloud_data.emplace_back(std::move(gl_point_cloud_data), point_cloud_box); + } + + m_dynamic_point_cloud_buffer.clear(); +} + +std::error_code asset_loader::create_gl_shader() +{ + auto shader_data = zgl::shader_data{}; + + const auto& [source, type] = m_dynamic_shader_data_buffer; + + if (const auto e = zgl::shader_data::build_from(type, source, shader_data)) { + return e; + } + + m_gl_shader_data.emplace_back(std::move(shader_data)); + + + return {}; +} + + +bool asset_loader::unload(const zgl::shader_program_handle& shader_handle) +{ + const auto it = std::find_if( + m_gl_shader_program_data.begin(), m_gl_shader_program_data.end(), + [&shader_handle](const auto& gl_shader_data) { + return gl_shader_data.handle().program_id == shader_handle.program_id; + } + ); + + if (it == m_gl_shader_program_data.end()) + { + return false; + } + + m_gl_shader_program_data.erase(it); + + return true; +} + +bool asset_loader::unload(const zgl::mesh_handle& mesh_handle) +{ + const auto it = std::ranges::find_if( + m_gl_mesh_data, + [&mesh_handle](const auto& gl_mesh_data) { + return gl_mesh_data.first.handle().vao_id == mesh_handle.vao_id; + } + ); + + if (it == m_gl_mesh_data.end()) + { + return false; + } + + const auto material_id = it->first.material_id(); + + const auto reference_it = std::ranges::find_if( + m_gl_material_data_references, + [&material_id](const auto& entry) { + return entry.first == material_id; + } + ); + + if (reference_it != m_gl_material_data_references.end()) + { + // Do not delete default material at index 0 + if (--reference_it->second == 0 and reference_it != m_gl_material_data_references.begin()) + { + const auto index = reference_it - m_gl_material_data_references.begin(); + m_gl_material_data.erase(m_gl_material_data.begin() + index); + m_gl_material_data_references.erase(reference_it); + } + } + + m_gl_mesh_data.erase(it); + + return true; +} + +void asset_loader::unload_shader_data() +{ + m_gl_shader_data.clear(); +} + + +bool asset_loader::unload(const zgl::point_cloud_handle& point_cloud_handle) +{ + const auto it = std::ranges::find_if( + m_gl_point_cloud_data, + [&point_cloud_handle](const auto& entry) { + return entry.first.handle().vao_id == point_cloud_handle.vao_id; + } + ); + + if (it == m_gl_point_cloud_data.end()) + { + return false; + } + + m_gl_point_cloud_data.erase(it); + + return true; +} +} diff --git a/source/viewer/dynamic_shader_program_loading.cpp b/source/viewer/dynamic_shader_program_loading.cpp new file mode 100644 index 0000000..c686fd2 --- /dev/null +++ b/source/viewer/dynamic_shader_program_loading.cpp @@ -0,0 +1,275 @@ +#include "../../include/viewer/dynamic_shader_program_loading.hpp" +#include "../../include/util/string_lookup.hpp" +#include "../../include/util/logger.hpp" +#include + +std::size_t viewer::dynamic_shader_program_loading::count_shader_files( + const std::filesystem::path& path +) { + using namespace std::string_view_literals; + namespace fs = std::filesystem; + + auto shader_file_count = std::size_t{ 0 }; + + for (const auto& [ asset_type, folder ] : { + std::make_pair(asset_types::mesh, "mesh"sv), + std::make_pair(asset_types::point_cloud, "point_cloud"sv) + }) { + const auto folder_begin = fs::directory_iterator{ path / folder }; + const auto folder_end = fs::directory_iterator{}; + + shader_file_count += std::count_if( + folder_begin, folder_end, + [](auto& entry) { return entry.is_regular_file(); } + ); + } + + return shader_file_count; +} + +void viewer::dynamic_shader_program_loading::load_directory( + asset_loader& loader, + instance& z3d, + std::mutex& gl_resource_lock, + std::mutex& progress_lock, + std::string& progress_title, + float& progress_ratio, + const std::filesystem::path& path +) { + + namespace fs = std::filesystem; + using namespace std::string_view_literals; + + auto progress_builder = std::stringstream{}; + + const auto shader_file_count = static_cast(count_shader_files(path)); + constexpr auto shader_loading_progress = 0.8f; + + + auto shader_indices = ztu::string_lookup({ + { "vertex", 0 }, + { "geometry", 1 }, + { "fragment", 2 } + }); + + constexpr auto shader_types = std::array{ + GL_VERTEX_SHADER, + GL_GEOMETRY_SHADER, + GL_FRAGMENT_SHADER + }; + + auto program_capabilities = std::vector{}; + auto programs = std::vector>{}; + + auto capability_indices = ztu::string_lookup(); + auto capabilities = std::vector{}; + capabilities.reserve(8); + + constexpr auto dot_char = '.'; + constexpr auto separator_char = '_'; + constexpr auto optional_char = '?'; + + auto curr_shader_count = std::size_t{ 0 }; + + for (const auto& [ asset_type, folder ] : { + std::make_pair(asset_types::mesh, "mesh"sv), + std::make_pair(asset_types::point_cloud, "point_cloud"sv), + }) { + program_capabilities.clear(); + programs.clear(); + capability_indices.clear(); + + for (const auto& file : fs::directory_iterator{ path / folder }) + { + if (not file.is_regular_file()) + continue; + + const auto& file_path = file.path(); + + if (file_path.extension() != ".glsl") + continue; + + const auto filename = file_path.filename().string(); + + progress_lock.lock(); + + progress_builder.str(std::string{}); + progress_builder << "Loading shader '" << filename << "\'..."; + progress_title = progress_builder.str(); + progress_ratio = shader_loading_progress * static_cast(curr_shader_count) / shader_file_count; + + progress_lock.unlock(); + + const auto name = std::string_view(filename.begin(), std::ranges::find(filename, dot_char)); + + const auto type_str = std::string_view(name.begin(), std::ranges::find(name, separator_char)); + + const auto shader_type_index_it = shader_indices.find(type_str); + if (shader_type_index_it == shader_indices.end()) { + ztu::logger::warn("Unknown shader type '%'. Skipping shader.", type_str); + continue; + } + + const auto shader_type_index = shader_type_index_it->second; + const auto shader_type = shader_types[shader_type_index]; + + auto shader_handle = zgl::shader_handle{}; + if (const auto e = loader.load_shader(shader_type, file_path, shader_handle)) { + ztu::logger::error( + "Error while loading shader %: [%] %", + file_path, + e.category().name(), + e.message() + ); + continue; + } + + ztu::logger::debug("% -> %", filename, shader_handle.shader_id); + + capabilities.clear(); + capabilities.push_back(0); + + auto specifiers_str = std::string_view(type_str.end() + 1, name.end()); // skip separator_char + + while (not specifiers_str.empty()) + { + const auto pos = specifiers_str.find(separator_char); + auto specifier_str = specifiers_str.substr(0, pos); + + if (pos == std::string_view::npos) + { + specifiers_str = std::string_view{}; + } + else + { + specifiers_str = specifiers_str.substr(pos + 1); // skip separator_char + } + + if (specifier_str.empty()) + { + continue; + } + + const auto optional = specifier_str.back() == optional_char; + if (optional) + { + specifier_str = specifier_str.substr(0, specifier_str.size() - 1); + } + + const auto index_it = capability_indices.find(specifier_str); + + auto capability_index = capability_indices.size(); + if (index_it == capability_indices.end()) + { + capability_indices.emplace(specifier_str, capability_index); + } + else + { + capability_index = index_it->second; + } + + const auto capability_flag = ztu::u32{ 1 } << capability_index; + + const auto capability_count = capabilities.size(); + + if (optional) + { + capabilities.resize(2 * capability_count); + std::copy_n(capabilities.begin(), capability_count, capabilities.begin() + capability_count); + } + + for (std::size_t i{}; i != capability_count; ++i) + { + capabilities[i] |= capability_flag; + } + } + + for (const auto& capability : capabilities) + { + const auto program_capability_it = std::ranges::upper_bound(program_capabilities, capability); + + auto program_index = program_capability_it - program_capabilities.begin(); + + if ( + program_capability_it == program_capabilities.begin() or + *std::prev(program_capability_it) != capability + ) { + program_capabilities.insert(program_capability_it, capability); + programs.emplace(programs.begin() + program_index); + } + else + { + --program_index; // The element before the iterator matches. + } + + programs[program_index][shader_type_index] = shader_handle; + } + + ++curr_shader_count; + } + + progress_lock.lock(); + + progress_title = "Linking programs..."; + + progress_lock.unlock(); + + // Remove any duplicates shader combinations. + std::ranges::sort( + programs, + [](const auto& lhs, const auto& rhs) { + return std::lexicographical_compare( + lhs.begin(), lhs.end(), + rhs.begin(), rhs.end(), + [](const auto& a, const auto& b) { + return a.shader_id < b.shader_id; + } + ); + } + ); + programs.erase(std::ranges::unique(programs).begin(), programs.end()); + + ztu::logger::debug("Linking % programs.", programs.size()); + + // create shader_program + for (const auto& [vertex, geometry, fragment] : programs) + { + if (vertex.shader_id == 0 or fragment.shader_id == 0) + { + ztu::logger::warn( + "Skipping program as the combination is unlikely to be used (vertex: % geometry: % fragment: %).", + vertex.shader_id, geometry.shader_id, fragment.shader_id + ); + continue; + } + + auto program_handle = zgl::shader_program_handle{}; + if (const auto e = loader.build_shader_program(vertex, geometry, fragment, program_handle)) + { + ztu::logger::error( + "Error occurred while linking shader program: [%] %", + e.category().name(), + e.message() + ); + continue; + } + + ztu::logger::debug( + "Linked (vertex: % geometry: % fragment: %) -> %", + vertex.shader_id, geometry.shader_id, fragment.shader_id, + program_handle.program_id + ); + + + gl_resource_lock.lock(); + z3d.add_shader_program(asset_type, program_handle); + gl_resource_lock.unlock(); + } + + gl_resource_lock.lock(); + z3d.m_mesh_shader_program_lookup.print(); + gl_resource_lock.unlock(); + + loader.unload_shader_data(); + } +} diff --git a/source/viewer/instance.cpp b/source/viewer/instance.cpp new file mode 100644 index 0000000..da81b45 --- /dev/null +++ b/source/viewer/instance.cpp @@ -0,0 +1,504 @@ +#include "viewer/instance.hpp" +#include "viewer/asset_loader.hpp" + +#include "SFML/Window.hpp" +#include "SFML/Graphics/Text.hpp" +#include "SFML/Graphics/Shape.hpp" +#include "SFML/Graphics/RectangleShape.hpp" +#include "SFML/Graphics/Sprite.hpp" + +#include +#include +#include "glm/gtx/euler_angles.hpp" + +namespace viewer +{ + +instance::instance() : + m_context_settings{ 24, 8, 2, 4, 6 }, + m_mesh_renderer(static_cast(rendering::modes::mesh::count)), + m_point_cloud_renderer(static_cast(rendering::modes::point_cloud::count)), + //m_camera(0.0f, 0.0f, 0.0f) + m_camera(0.0f, -std::numbers::pi_v / 2.0f, std::numbers::pi_v) +{ +} + +std::error_code instance::init(std::string title) +{ + m_title = std::move(title); + + windowed(512, 344, true); + + if (glewInit() != GLEW_OK) + { + return std::make_error_code(std::errc::not_supported); + } + + m_settings.lighting.point_light_direction = glm::normalize(m_settings.lighting.point_light_direction); + + m_mesh_render_mode = rendering::modes::mesh::lit_faces; + m_point_cloud_render_mode = rendering::modes::point_cloud::rainbow; + + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + glFrontFace(GL_CCW); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LESS); + glClearDepth(1.f); + + set_background_color({ 0.0f, 0.0f, 0.0f, 0.0f }); + + const auto current_path = std::filesystem::current_path(); + const auto data_path = current_path / ".." / "data"; + + const auto font_filename = data_path / "fonts" / "JetBrainsMono_Medium.ttf"; + if (not m_font.loadFromFile(font_filename)) { + ztu::logger::error("Could not open font file: %", font_filename); + } + + const auto image_dir = data_path / "images"; + + const auto logo_filename = image_dir/ "logo.png"; + if (not m_logo.loadFromFile(logo_filename)) { + ztu::logger::error("Could not open image file: %", logo_filename); + } + + const auto spinner_filename = image_dir/ "spinner.png"; + if (not m_spinner.loadFromFile(spinner_filename)) { + ztu::logger::error("Could not open image file: %", spinner_filename); + } + + return {}; +} + +void instance::set_background_color(const glm::vec4& color) +{ + glClearColor(color.r, color.g, color.b, color.a); +} + +std::optional instance::add_mesh( + const zgl::mesh_handle& mesh, + const aabb& bounding_box, + const components::mesh_vertex::flags mesh_components, + const zgl::material_handle& material, + const material_component::flags material_components +) { + const auto mesh_id = m_mesh_renderer.add( + std::make_pair(mesh_components, material_components), + mesh, + bounding_box, + glm::identity(), + material, + m_mesh_shader_program_lookup + ); + + if (not mesh_id) + { + return std::nullopt; + } + + return asset_id(std::in_place_index_t{}, *mesh_id); +} + +std::optional instance::add_point_cloud( + const zgl::point_cloud_handle& point_cloud, + const aabb& bounding_box, + const components::point_cloud_vertex::flags point_cloud_components +) { + const auto point_cloud_id = m_point_cloud_renderer.add( + point_cloud_components, + point_cloud, + bounding_box, + glm::identity(), + m_point_cloud_shader_program_lookup + ); + + if (not point_cloud_id) + { + return std::nullopt; + } + + return asset_id(std::in_place_index_t{}, *point_cloud_id); +} + +void instance::add_shader_program( + asset_types type, zgl::shader_program_handle shader_program_handle +) { + switch (type) + { + case asset_types::mesh: + m_mesh_shader_program_lookup.add(shader_program_handle); + break; + case asset_types::point_cloud: + m_point_cloud_shader_program_lookup.add(shader_program_handle); + break; + default: + break; + } +} + +bool instance::remove(asset_id id) +{ + switch (id.index()) { + case id_mesh_index: + return m_mesh_renderer.remove(std::get(id)); + case id_point_cloud_index: + return m_point_cloud_renderer.remove(std::get(id)); + default: + return false; + } +} + +bool instance::look_at(asset_id id) +{ + auto bounding_box_opt = std::optional{ std::nullopt }; + + switch (id.index()) { + case id_mesh_index: + bounding_box_opt = m_mesh_renderer.bounding_box(std::get(id)); + break; + case id_point_cloud_index: + bounding_box_opt = m_point_cloud_renderer.bounding_box(std::get(id)); + break; + default: + return false; + } + + if (bounding_box_opt) + { + const auto& bounding_box = *bounding_box_opt; + ztu::logger::debug("aabb: % %", glm::to_string(bounding_box.min), glm::to_string(bounding_box.max)); + //m_camera.look_at(bounding_box.max, bounding_box.min, m_view); + + m_camera.look_at(bounding_box.center(), bounding_box.min, m_view); + + ztu::logger::debug("pos: % front: % right: % up: % as: % fov: %", + glm::to_string(m_view.position), + glm::to_string(m_view.front), + glm::to_string(m_view.right), + glm::to_string(m_view.up), + m_view.aspect_ratio, + m_view.fov + ); + + } + + return true; +} + + +void instance::run_progress( + std::mutex& lock, + std::string& title, + float& progress, + const double fps +) { + namespace chr = std::chrono; + using namespace std::chrono_literals; + using clock = chr::high_resolution_clock; + using duration_type = clock::duration; + using floating_second = chr::duration; + + sf::RectangleShape bar_background, bar_foreground; + sf::Text title_text, percent_text; + sf::Texture logo_texture, spinner_texture; + sf::Sprite logo_sprite, spinner_sprite; + + sf::Color background_color = { 0, 0, 0, 255 }; + + constexpr auto font_size = 14; + constexpr auto padding = 15; + constexpr auto spinner_size = 10.0f; + constexpr auto spinner_degrees_per_second = 270.0f; + + title_text.setFont(m_font); + title_text.setCharacterSize(font_size); + percent_text.setFont(m_font); + percent_text.setCharacterSize(font_size); + + bar_foreground.setOutlineColor(sf::Color::Transparent); + bar_foreground.setFillColor(sf::Color{ 107, 203, 119 }); + + bar_background.setOutlineColor(sf::Color::Transparent); + bar_background.setFillColor(sf::Color{ 64, 170, 87 }); + + + const auto target_frame_time = std::chrono::duration_cast(1s) / fps; + + std::stringstream percent_builder; + percent_builder << std::setw(3) << std::setfill(' '); + + logo_texture.loadFromImage(m_logo); + logo_sprite.setTexture(logo_texture); + const auto logo_scale = static_cast(m_screen_size.x) / static_cast(logo_texture.getSize().x); + logo_sprite.scale({ logo_scale, logo_scale }); + logo_sprite.setOrigin(0, 0); + logo_sprite.setPosition(0, 0); + + const auto dim = static_cast(m_screen_size); + const auto bar_dim = sf::Vector2f(dim.x - 2 * padding, 10.0f); + const auto bar_pos = sf::Vector2f((dim.x - bar_dim.x) / 2.0f, dim.y - bar_dim.y - padding); + bar_background.setPosition(bar_pos + sf::Vector2f{ 3, 3 }); + bar_background.setSize(bar_dim); + bar_foreground.setPosition(bar_pos); + + spinner_texture.loadFromImage(m_spinner); + spinner_sprite.setTexture(spinner_texture); + const auto spinner_dim = static_cast(spinner_texture.getSize().y); + const auto spinner_scale = spinner_size / spinner_dim; + spinner_sprite.scale({ spinner_scale, spinner_scale }); + spinner_sprite.setOrigin(spinner_dim / 2.0f, spinner_dim / 2.0f); + spinner_sprite.setPosition(padding + spinner_size / 2.0f, bar_pos.y - 0.5f * padding - spinner_size / 2.0f); + + const auto title_text_pos = sf::Vector2f(1.5f * padding + spinner_size, bar_pos.y - 0.5f * padding - font_size); + const auto percent_text_pos = sf::Vector2f(dim.x - padding, bar_pos.y - 0.5f * padding - font_size); + title_text.setPosition(title_text_pos); + percent_text.setPosition(percent_text_pos); + + percent_text.setFillColor(sf::Color::White); + title_text.setFillColor(sf::Color::White); + + const auto start_time = clock::now(); + + while (true) + { + const auto frame_begin = clock::now(); + + const auto t = chr::duration_cast(frame_begin - start_time).count(); + + lock.lock(); + + auto curr_progress = progress; + title_text.setString(title); + + lock.unlock(); + + if (curr_progress == std::numeric_limits::max()) + { + break; + } + + m_window.clear(background_color); + + m_window.draw(logo_sprite); + m_window.draw(bar_background); + + bar_foreground.setSize(sf::Vector2f(curr_progress * bar_dim.x, bar_dim.y)); + m_window.draw(bar_foreground); + + const auto spinner_angle = t * spinner_degrees_per_second; + spinner_sprite.setRotation(spinner_angle); + m_window.draw(spinner_sprite); + + const auto percent = static_cast(std::round(100.0f * curr_progress)); + + percent_builder.str(""); + percent_builder << percent << '%'; + + percent_text.setString(percent_builder.str()); + const auto percent_text_bounds = percent_text.getLocalBounds(); + percent_text.setOrigin( + percent_text_bounds.left + percent_text_bounds.width, + 0 + ); + + m_window.draw(title_text); + m_window.draw(percent_text); + m_window.display(); + + const auto time_taken = clock::now() - frame_begin; + if (time_taken < target_frame_time) + { + std::this_thread::sleep_for(target_frame_time - time_taken); + } + } +} + +void instance::run(std::mutex& gl_resource_lock, const double fps) +{ + namespace chr = std::chrono; + using namespace std::chrono_literals; + using clock = chr::high_resolution_clock; + using duration_type = clock::duration; + using floating_second = chr::duration; + + const auto target_frame_time = std::chrono::duration_cast(1s) / fps; + + auto frame_begin = clock::now(); + + m_mouse_locked = true; + m_window.setMouseCursorVisible(false); + + while (true) + { + auto prev_frame_begin = frame_begin; + frame_begin = clock::now(); + + const auto dt = chr::duration_cast(frame_begin - prev_frame_begin); + + if (not update(dt.count())) break; + + gl_resource_lock.lock(); + render(); + gl_resource_lock.unlock(); + + /*ztu::logger::debug("pos: % front: % right: % up: % as: % fov: %", + glm::to_string(m_view.position), + glm::to_string(m_view.front), + glm::to_string(m_view.right), + glm::to_string(m_view.up), + m_view.aspect_ratio, + m_view.fov + );*/ + + const auto time_taken = clock::now() - frame_begin; + if (time_taken < target_frame_time) + { + std::this_thread::sleep_for(target_frame_time - time_taken); + } + } +} + +void instance::windowed(const unsigned int width, const unsigned int height, const bool decorations) { + + m_window.create( + sf::VideoMode(width, height), + m_title, + decorations ? sf::Style::Default : sf::Style::None, + m_context_settings + ); + + m_screen_size = { width, height }; + + m_view.aspect_ratio = static_cast(width) / static_cast(height); +} + +void instance::fullscreen() +{ + m_window.create( + sf::VideoMode(), + m_title, + sf::Style::Fullscreen, + m_context_settings + ); +} + + +void instance::size(const unsigned int width, const unsigned int height) { + + m_window.setSize({ width, height }); + + m_screen_size = { width, height }; + + m_view.aspect_ratio = static_cast(width) / static_cast(height); +} + +bool instance::update(const double dt) { + + //ztu::logger::log("pos: % dir: %", glm::to_string(m_view.position), glm::to_string(m_view.front)); + + auto mouse_pos_delta = glm::vec2{ 0, 0 }; + auto mouse_wheel_delta = 0.0f; + + sf::Event event; + while (m_window.pollEvent(event)) + { + switch (event.type) { + case sf::Event::Closed: [[unlikely]] + { + return false; + } + case sf::Event::Resized: + { + const auto& [ width, height ] = event.size; + glViewport(0, 0, width, height); + m_screen_size = { width, height }; + break; + } + case sf::Event::MouseWheelMoved: + { + mouse_wheel_delta = m_settings.scroll_speed * event.mouseWheel.delta; + break; + } + case sf::Event::KeyPressed: + switch (event.key.code) { + case sf::Keyboard::Escape: + return false; + case sf::Keyboard::Tab: + m_window.setMouseCursorVisible(m_mouse_locked); + m_mouse_locked = not m_mouse_locked; + break; + case sf::Keyboard::T: + //renderIndex = (renderIndex + 1) % renderers.size(); + break; + case sf::Keyboard::C: + //renderIndex = (renderIndex + 1) % renderers.size(); + break; + default: + break; + } + break; + default: + break; + } + } + + m_view.aspect_ratio = static_cast(m_screen_size.x) / static_cast(m_screen_size.y); + + if (m_mouse_locked) [[likely]] + { + const auto screen_center = m_screen_size / 2; + + const auto [ mouse_x, mouse_y ] = sf::Mouse::getPosition(m_window); + sf::Mouse::setPosition({ screen_center.x, screen_center.y }, m_window); + + const auto mouse_pixel_delta = glm::ivec2{ mouse_x, mouse_y } - screen_center; + + mouse_pos_delta = m_settings.mouse_sensitivity * static_cast(mouse_pixel_delta); + + m_camera.update( + static_cast(dt), + mouse_pos_delta, + mouse_wheel_delta, + m_view + ); + } + + return true; +} + +void instance::render() +{ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + const auto view_matrix = m_view.create_view_matrix(); + const auto projection_matrix = m_view.create_projection_matrix(); + + const auto vp_matrix = projection_matrix * view_matrix; + + m_mesh_renderer.render( + m_mesh_render_mode, + vp_matrix, + view_matrix, + m_view.position, + m_settings.lighting + ); + + m_point_cloud_renderer.render( + m_point_cloud_render_mode, + vp_matrix, + m_view.position, + m_settings.lighting + ); + + m_window.display(); + + //std::cout << "f\n"; +} + + +}