diff --git a/src/engine/app/input-manager.hpp b/src/engine/app/input-manager.hpp index 9beb243..bfffbf2 100644 --- a/src/engine/app/input-manager.hpp +++ b/src/engine/app/input-manager.hpp @@ -51,14 +51,18 @@ public: virtual void update() = 0; /** - * Makes the cursor visible. + * Shows or hides the cursor. + * + * @param visible `true` to show the cursor, `false` to hide it. */ - virtual void show_cursor() = 0; + virtual void set_cursor_visible(bool visible) = 0; /** - * Makes the cursor invisible. + * Enables or disables relative mouse mode. + * + * @param enabled `true` to enable relative mouse mode, `false` to disable it. */ - virtual void hide_cursor() = 0; + virtual void set_relative_mouse_mode(bool enabled) = 0; /** * Returns the event queue associated with registered input devices. diff --git a/src/engine/app/sdl/sdl-input-manager.cpp b/src/engine/app/sdl/sdl-input-manager.cpp index bdbee4e..c4e02c0 100644 --- a/src/engine/app/sdl/sdl-input-manager.cpp +++ b/src/engine/app/sdl/sdl-input-manager.cpp @@ -317,20 +317,20 @@ void sdl_input_manager::update() this->event_queue.flush(); } -void sdl_input_manager::show_cursor() +void sdl_input_manager::set_cursor_visible(bool visible) { - if (SDL_ShowCursor(SDL_ENABLE) < 0) + if (SDL_ShowCursor((visible) ? SDL_ENABLE : SDL_DISABLE) < 0) { - debug::log::error("Failed to show cursor: \"{}\"", SDL_GetError()); + debug::log::error("Failed to set cursor visibility: \"{}\"", SDL_GetError()); SDL_ClearError(); } } -void sdl_input_manager::hide_cursor() +void sdl_input_manager::set_relative_mouse_mode(bool enabled) { - if (SDL_ShowCursor(SDL_DISABLE) < 0) + if (SDL_SetRelativeMouseMode((enabled) ? SDL_TRUE : SDL_FALSE) < 0) { - debug::log::error("Failed to hide cursor: \"{}\"", SDL_GetError()); + debug::log::error("Failed to set relative mouse mode: \"{}\"", SDL_GetError()); SDL_ClearError(); } } diff --git a/src/engine/app/sdl/sdl-input-manager.hpp b/src/engine/app/sdl/sdl-input-manager.hpp index b1445e6..3cb4e3a 100644 --- a/src/engine/app/sdl/sdl-input-manager.hpp +++ b/src/engine/app/sdl/sdl-input-manager.hpp @@ -28,7 +28,7 @@ namespace app { class sdl_window; /** - * + * Input manager implementation using SDL2. */ class sdl_input_manager: public input_manager { @@ -43,9 +43,9 @@ public: */ virtual ~sdl_input_manager(); - virtual void update(); - virtual void show_cursor(); - virtual void hide_cursor(); + void update() override; + void set_cursor_visible(bool visible) override; + void set_relative_mouse_mode(bool enabled) override; private: input::keyboard keyboard; diff --git a/src/engine/render/compositor.cpp b/src/engine/render/compositor.cpp index 57fa106..27a38f8 100644 --- a/src/engine/render/compositor.cpp +++ b/src/engine/render/compositor.cpp @@ -37,13 +37,13 @@ void compositor::remove_passes() passes.clear(); } -void compositor::composite(const render::context& ctx, render::queue& queue) +void compositor::composite(render::context& ctx) { for (pass* pass: passes) { if (pass->is_enabled()) { - pass->render(ctx, queue); + pass->render(ctx); } } } diff --git a/src/engine/render/compositor.hpp b/src/engine/render/compositor.hpp index 8345002..2bf18d3 100644 --- a/src/engine/render/compositor.hpp +++ b/src/engine/render/compositor.hpp @@ -21,7 +21,6 @@ #define ANTKEEPER_RENDER_COMPOSITOR_HPP #include -#include #include namespace render { @@ -38,7 +37,7 @@ public: void remove_pass(pass* pass); void remove_passes(); - void composite(const render::context& ctx, render::queue& queue); + void composite(render::context& ctx); const std::list* get_passes() const; diff --git a/src/engine/render/context.hpp b/src/engine/render/context.hpp index 07ef15f..580b98b 100644 --- a/src/engine/render/context.hpp +++ b/src/engine/render/context.hpp @@ -24,7 +24,8 @@ #include #include #include -#include +#include +#include namespace scene { @@ -82,8 +83,11 @@ struct context /// Subframe interpolation factor. float alpha; - /// List of objects visible to the active camera. - std::list visible_objects; + /// Objects visible to the active camera. + std::vector objects; + + /// Render operations generated by visible objects. + std::vector operations; }; } // namespace render diff --git a/src/engine/render/operation.hpp b/src/engine/render/operation.hpp index 56f669c..fcd0575 100644 --- a/src/engine/render/operation.hpp +++ b/src/engine/render/operation.hpp @@ -24,27 +24,29 @@ #include #include #include -#include +#include +#include +#include +#include namespace render { -class material; - /** - * Encapsulates an atomic render operation. + * Atomic render operation. */ struct operation { - const float4x4* skinning_palette; - std::size_t bone_count; - const material* material; - const gl::vertex_array* vertex_array; - gl::drawing_mode drawing_mode; - std::size_t start_index; - std::size_t index_count; - float4x4 transform; - float depth; - std::size_t instance_count; + const gl::vertex_array* vertex_array{nullptr}; + gl::drawing_mode drawing_mode{gl::drawing_mode::triangles}; + std::size_t start_index{0}; + std::size_t index_count{0}; + std::shared_ptr material; + + float4x4 transform{float4x4::identity()}; + float depth{0.0f}; + + std::size_t instance_count{0}; + std::span skinning_palette{}; }; } // namespace render diff --git a/src/engine/render/pass.hpp b/src/engine/render/pass.hpp index 203fd1f..a5cb10a 100644 --- a/src/engine/render/pass.hpp +++ b/src/engine/render/pass.hpp @@ -23,7 +23,6 @@ #include #include #include -#include namespace render { @@ -36,7 +35,7 @@ public: pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer); virtual ~pass(); - virtual void render(const render::context& ctx, render::queue& queue) = 0; + virtual void render(render::context& ctx) = 0; void set_enabled(bool enabled); bool is_enabled() const; diff --git a/src/engine/render/passes/bloom-pass.cpp b/src/engine/render/passes/bloom-pass.cpp index 9d3bfa0..b788599 100644 --- a/src/engine/render/passes/bloom-pass.cpp +++ b/src/engine/render/passes/bloom-pass.cpp @@ -94,7 +94,7 @@ bloom_pass::bloom_pass(gl::rasterizer* rasterizer, resource_manager* resource_ma quad_vao->bind(render::vertex_attribute::position, position_attribute); } -void bloom_pass::render(const render::context& ctx, render::queue& queue) +void bloom_pass::render(render::context& ctx) { // Execute command buffer for (const auto& command: command_buffer) diff --git a/src/engine/render/passes/bloom-pass.hpp b/src/engine/render/passes/bloom-pass.hpp index e7a0fab..90e8d47 100644 --- a/src/engine/render/passes/bloom-pass.hpp +++ b/src/engine/render/passes/bloom-pass.hpp @@ -57,7 +57,7 @@ public: * @param ctx Render context. * @param queue Render queue. */ - void render(const render::context& ctx, render::queue& queue) override; + void render(render::context& ctx) override; /** * Resizes the mip chain resolution according to the resolution of the source texture. diff --git a/src/engine/render/passes/clear-pass.cpp b/src/engine/render/passes/clear-pass.cpp index e6ecf9d..34d5116 100644 --- a/src/engine/render/passes/clear-pass.cpp +++ b/src/engine/render/passes/clear-pass.cpp @@ -34,7 +34,7 @@ clear_pass::clear_pass(gl::rasterizer* rasterizer, const gl::framebuffer* frameb clear_stencil(0) {} -void clear_pass::render(const render::context& ctx, render::queue& queue) +void clear_pass::render(render::context& ctx) { for (const auto& command: command_buffer) { diff --git a/src/engine/render/passes/clear-pass.hpp b/src/engine/render/passes/clear-pass.hpp index 6198700..89be077 100644 --- a/src/engine/render/passes/clear-pass.hpp +++ b/src/engine/render/passes/clear-pass.hpp @@ -34,7 +34,7 @@ class clear_pass: public pass public: clear_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer); - void render(const render::context& ctx, render::queue& queue) override; + void render(render::context& ctx) override; /** * Sets the buffers to be cleared. diff --git a/src/engine/render/passes/final-pass.cpp b/src/engine/render/passes/final-pass.cpp index b5ef9f0..432904d 100644 --- a/src/engine/render/passes/final-pass.cpp +++ b/src/engine/render/passes/final-pass.cpp @@ -79,7 +79,7 @@ final_pass::final_pass(gl::rasterizer* rasterizer, const gl::framebuffer* frameb quad_vao->bind(render::vertex_attribute::position, position_attribute); } -void final_pass::render(const render::context& ctx, render::queue& queue) +void final_pass::render(render::context& ctx) { // Update resolution const auto viewport_size = framebuffer->get_dimensions(); diff --git a/src/engine/render/passes/final-pass.hpp b/src/engine/render/passes/final-pass.hpp index 6257edb..340601c 100644 --- a/src/engine/render/passes/final-pass.hpp +++ b/src/engine/render/passes/final-pass.hpp @@ -41,7 +41,7 @@ class final_pass: public pass { public: final_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager); - void render(const render::context& ctx, render::queue& queue) override; + void render(render::context& ctx) override; void set_color_texture(const gl::texture_2d* texture); void set_bloom_texture(const gl::texture_2d* texture) noexcept; diff --git a/src/engine/render/passes/fxaa-pass.cpp b/src/engine/render/passes/fxaa-pass.cpp index 8a13e7f..4e22dc6 100644 --- a/src/engine/render/passes/fxaa-pass.cpp +++ b/src/engine/render/passes/fxaa-pass.cpp @@ -73,7 +73,7 @@ fxaa_pass::fxaa_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuf quad_vao->bind(render::vertex_attribute::position, position_attribute); } -void fxaa_pass::render(const render::context& ctx, render::queue& queue) +void fxaa_pass::render(render::context& ctx) { for (const auto& command: command_buffer) { diff --git a/src/engine/render/passes/fxaa-pass.hpp b/src/engine/render/passes/fxaa-pass.hpp index 8d553bf..70d2f0b 100644 --- a/src/engine/render/passes/fxaa-pass.hpp +++ b/src/engine/render/passes/fxaa-pass.hpp @@ -56,7 +56,7 @@ public: * @param ctx Render context. * @param queue Render queue. */ - void render(const render::context& ctx, render::queue& queue) override; + void render(render::context& ctx) override; /** * Sets the FXAA source texture. diff --git a/src/engine/render/passes/ground-pass.cpp b/src/engine/render/passes/ground-pass.cpp index c0e2665..4c46e1f 100644 --- a/src/engine/render/passes/ground-pass.cpp +++ b/src/engine/render/passes/ground-pass.cpp @@ -58,7 +58,7 @@ ground_pass::ground_pass(gl::rasterizer* rasterizer, const gl::framebuffer* fram ground_pass::~ground_pass() {} -void ground_pass::render(const render::context& ctx, render::queue& queue) +void ground_pass::render(render::context& ctx) { /* if (!ground_model) diff --git a/src/engine/render/passes/ground-pass.hpp b/src/engine/render/passes/ground-pass.hpp index 113e3da..a183fed 100644 --- a/src/engine/render/passes/ground-pass.hpp +++ b/src/engine/render/passes/ground-pass.hpp @@ -44,7 +44,7 @@ class ground_pass: public pass public: ground_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager); virtual ~ground_pass(); - void render(const render::context& ctx, render::queue& queue) override; + void render(render::context& ctx) override; void set_ground_model(std::shared_ptr model); diff --git a/src/engine/render/passes/material-pass.cpp b/src/engine/render/passes/material-pass.cpp index c63a9d5..ac0ef29 100644 --- a/src/engine/render/passes/material-pass.cpp +++ b/src/engine/render/passes/material-pass.cpp @@ -48,16 +48,78 @@ #include #include #include +#include namespace render { -static bool operation_compare(const render::operation& a, const render::operation& b); +namespace { + +/** + * Sorts render operations for the material pass. + */ +bool operation_compare(const render::operation* a, const render::operation* b) +{ + // Render operations with materials first + if (!a->material) + { + return false; + } + else if (!b->material) + { + return true; + } + + const bool translucent_a = a->material->get_blend_mode() == material_blend_mode::translucent; + const bool translucent_b = b->material->get_blend_mode() == material_blend_mode::translucent; + + if (translucent_a) + { + if (translucent_b) + { + // A and B are both translucent, render back to front + return (a->depth < b->depth); + } + else + { + // A is translucent, B is opaque. Render B first + return false; + } + } + else + { + if (translucent_b) + { + // A is opaque, B is translucent. Render A first + return true; + } + else + { + // A and B are both opaque + + const std::size_t hash_a = a->material->hash(); + const std::size_t hash_b = b->material->hash(); + + if (hash_a == hash_b) + { + // A and B have same material hash, sort by VAO + return (a->vertex_array < b->vertex_array); + } + else + { + // A and B have different material hashes, sort by hash + return (hash_a < hash_b); + } + } + } +} + +} // namespace material_pass::material_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager): pass(rasterizer, framebuffer) {} -void material_pass::render(const render::context& ctx, render::queue& queue) +void material_pass::render(render::context& ctx) { rasterizer->use_framebuffer(*framebuffer); @@ -88,13 +150,13 @@ void material_pass::render(const render::context& ctx, render::queue& queue) evaluate_lighting(ctx); evaluate_misc(ctx); - // Sort render queue - queue.sort(operation_compare); + // Sort render operations + std::sort(std::execution::par_unseq, ctx.operations.begin(), ctx.operations.end(), operation_compare); - for (const render::operation& operation: queue) + for (const render::operation* operation: ctx.operations) { // Get operation material - const render::material* material = operation.material; + const render::material* material = operation->material.get(); if (!material) { if (!fallback_material) @@ -204,20 +266,20 @@ void material_pass::render(const render::context& ctx, render::queue& queue) } // Update geometry-dependent shader variables - model = &operation.transform; + model = &operation->transform; for (const auto& command: active_cache_entry->geometry_command_buffer) { command(); } // Draw geometry - if (operation.instance_count) + if (operation->instance_count) { - rasterizer->draw_arrays_instanced(*operation.vertex_array, operation.drawing_mode, operation.start_index, operation.index_count, operation.instance_count); + rasterizer->draw_arrays_instanced(*operation->vertex_array, operation->drawing_mode, operation->start_index, operation->index_count, operation->instance_count); } else { - rasterizer->draw_arrays(*operation.vertex_array, operation.drawing_mode, operation.start_index, operation.index_count); + rasterizer->draw_arrays(*operation->vertex_array, operation->drawing_mode, operation->start_index, operation->index_count); } } @@ -253,8 +315,8 @@ void material_pass::evaluate_lighting(const render::context& ctx) directional_shadow_count = 0; spot_light_count = 0; - const std::list* lights = ctx.collection->get_objects(scene::light::object_type_id); - for (const scene::object_base* object: *lights) + const auto& lights = ctx.collection->get_objects(scene::light::object_type_id); + for (const scene::object_base* object: lights) { // Ignore inactive lights if (!object->is_active()) @@ -965,60 +1027,4 @@ void material_pass::build_material_command_buffer(std::vectorget_blend_mode() == material_blend_mode::translucent; - const bool translucent_b = b.material->get_blend_mode() == material_blend_mode::translucent; - - if (translucent_a) - { - if (translucent_b) - { - // A and B are both translucent, render back to front - return (a.depth < b.depth); - } - else - { - // A is translucent, B is opaque. Render B first - return false; - } - } - else - { - if (translucent_b) - { - // A is opaque, B is translucent. Render A first - return true; - } - else - { - // A and B are both opaque - - const std::size_t hash_a = a.material->hash(); - const std::size_t hash_b = b.material->hash(); - - if (hash_a == hash_b) - { - // A and B have same material hash, sort by VAO - return (a.vertex_array < b.vertex_array); - } - else - { - // A and B have different material hashes, sort by hash - return (hash_a < hash_b); - } - } - } -} - } // namespace render diff --git a/src/engine/render/passes/material-pass.hpp b/src/engine/render/passes/material-pass.hpp index cdcbcf9..41eee8d 100644 --- a/src/engine/render/passes/material-pass.hpp +++ b/src/engine/render/passes/material-pass.hpp @@ -41,7 +41,7 @@ class material_pass: public pass public: material_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager); - void render(const render::context& ctx, render::queue& queue) override; + void render(render::context& ctx) override; /// Sets the material to be used when a render operation is missing a material. If no fallback material is specified, render operations without materials will not be processed. void set_fallback_material(std::shared_ptr fallback); diff --git a/src/engine/render/passes/outline-pass.cpp b/src/engine/render/passes/outline-pass.cpp index e5e1497..6a3a23d 100644 --- a/src/engine/render/passes/outline-pass.cpp +++ b/src/engine/render/passes/outline-pass.cpp @@ -60,8 +60,9 @@ outline_pass::outline_pass(gl::rasterizer* rasterizer, const gl::framebuffer* fr stroke_color_var = stroke_shader->variable("color"); } -void outline_pass::render(const render::context& ctx, render::queue& queue) +void outline_pass::render(render::context& ctx) { + /* rasterizer->use_framebuffer(*framebuffer); // Determine viewport based on framebuffer resolution @@ -90,6 +91,7 @@ void outline_pass::render(const render::context& ctx, render::queue& queue) // Setup fill shader rasterizer->use_program(*fill_shader); + // Render fills for (const render::operation& operation: queue) { @@ -143,6 +145,7 @@ void outline_pass::render(const render::context& ctx, render::queue& queue) } glDisable(GL_STENCIL_TEST); + */ } void outline_pass::set_outline_width(float width) diff --git a/src/engine/render/passes/outline-pass.hpp b/src/engine/render/passes/outline-pass.hpp index e1ea39c..9694157 100644 --- a/src/engine/render/passes/outline-pass.hpp +++ b/src/engine/render/passes/outline-pass.hpp @@ -38,7 +38,7 @@ class outline_pass: public pass public: outline_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager); virtual ~outline_pass() = default; - void render(const render::context& ctx, render::queue& queue) override; + void render(render::context& ctx) override; void set_outline_width(float width); void set_outline_color(const float4& color); diff --git a/src/engine/render/passes/resample-pass.cpp b/src/engine/render/passes/resample-pass.cpp index 6e54410..2732eb6 100644 --- a/src/engine/render/passes/resample-pass.cpp +++ b/src/engine/render/passes/resample-pass.cpp @@ -73,7 +73,7 @@ resample_pass::resample_pass(gl::rasterizer* rasterizer, const gl::framebuffer* quad_vao->bind(render::vertex_attribute::position, position_attribute); } -void resample_pass::render(const render::context& ctx, render::queue& queue) +void resample_pass::render(render::context& ctx) { for (const auto& command: command_buffer) { diff --git a/src/engine/render/passes/resample-pass.hpp b/src/engine/render/passes/resample-pass.hpp index 67e08f1..538268d 100644 --- a/src/engine/render/passes/resample-pass.hpp +++ b/src/engine/render/passes/resample-pass.hpp @@ -55,7 +55,7 @@ public: * @param ctx Render context. * @param queue Render queue. */ - void render(const render::context& ctx, render::queue& queue) override; + void render(render::context& ctx) override; /** * Sets the resample source texture. diff --git a/src/engine/render/passes/shadow-map-pass.cpp b/src/engine/render/passes/shadow-map-pass.cpp index 72b72eb..84bcb50 100644 --- a/src/engine/render/passes/shadow-map-pass.cpp +++ b/src/engine/render/passes/shadow-map-pass.cpp @@ -39,10 +39,11 @@ #include #include #include +#include namespace render { -static bool operation_compare(const render::operation& a, const render::operation& b); +static bool operation_compare(const render::operation* a, const render::operation* b); shadow_map_pass::shadow_map_pass(gl::rasterizer* rasterizer, resource_manager* resource_manager): pass(rasterizer, nullptr) @@ -73,11 +74,11 @@ shadow_map_pass::shadow_map_pass(gl::rasterizer* rasterizer, resource_manager* r } } -void shadow_map_pass::render(const render::context& ctx, render::queue& queue) +void shadow_map_pass::render(render::context& ctx) { // For each light - const std::list* lights = ctx.collection->get_objects(scene::light::object_type_id); - for (const scene::object_base* object: *lights) + const auto& lights = ctx.collection->get_objects(scene::light::object_type_id); + for (const scene::object_base* object: lights) { // Ignore inactive lights if (!object->is_active()) @@ -106,11 +107,11 @@ void shadow_map_pass::render(const render::context& ctx, render::queue& queue) } // Render cascaded shadow maps - render_csm(directional_light, ctx, queue); + render_csm(directional_light, ctx); } } -void shadow_map_pass::render_csm(const scene::directional_light& light, const render::context& ctx, render::queue& queue) const +void shadow_map_pass::render_csm(const scene::directional_light& light, render::context& ctx) { rasterizer->use_framebuffer(*light.get_shadow_framebuffer()); @@ -189,8 +190,8 @@ void shadow_map_pass::render_csm(const scene::directional_light& light, const re float4x4 cropped_view_projection; float4x4 model_view_projection; - // Sort render queue - queue.sort(operation_compare); + // Sort render operations + std::sort(std::execution::par_unseq, ctx.operations.begin(), ctx.operations.end(), operation_compare); gl::shader_program* active_shader_program = nullptr; @@ -242,9 +243,9 @@ void shadow_map_pass::render_csm(const scene::directional_light& light, const re // Calculate world-space to cascade texture-space transformation matrix cascade_matrices[i] = bias_tile_matrices[i] * cropped_view_projection; - for (const render::operation& operation: queue) + for (const render::operation* operation: ctx.operations) { - const render::material* material = operation.material; + const render::material* material = operation->material.get(); if (material) { // Skip materials which don't cast shadows @@ -267,7 +268,7 @@ void shadow_map_pass::render_csm(const scene::directional_light& light, const re } // Switch shader programs if necessary - gl::shader_program* shader_program = (operation.bone_count) ? skinned_shader_program.get() : unskinned_shader_program.get(); + gl::shader_program* shader_program = (operation->skinning_palette.empty()) ? unskinned_shader_program.get() : skinned_shader_program.get(); if (active_shader_program != shader_program) { active_shader_program = shader_program; @@ -275,7 +276,7 @@ void shadow_map_pass::render_csm(const scene::directional_light& light, const re } // Calculate model-view-projection matrix - model_view_projection = cropped_view_projection * operation.transform; + model_view_projection = cropped_view_projection * operation->transform; // Upload operation-dependent parameters to shader program if (active_shader_program == unskinned_shader_program.get()) @@ -288,17 +289,17 @@ void shadow_map_pass::render_csm(const scene::directional_light& light, const re } // Draw geometry - rasterizer->draw_arrays(*operation.vertex_array, operation.drawing_mode, operation.start_index, operation.index_count); + rasterizer->draw_arrays(*operation->vertex_array, operation->drawing_mode, operation->start_index, operation->index_count); } } } -bool operation_compare(const render::operation& a, const render::operation& b) +bool operation_compare(const render::operation* a, const render::operation* b) { - const bool skinned_a = (a.bone_count); - const bool skinned_b = (b.bone_count); - const bool two_sided_a = (a.material) ? a.material->is_two_sided() : false; - const bool two_sided_b = (b.material) ? b.material->is_two_sided() : false; + const bool skinned_a = !a->skinning_palette.empty(); + const bool skinned_b = !b->skinning_palette.empty(); + const bool two_sided_a = (a->material) ? a->material->is_two_sided() : false; + const bool two_sided_b = (b->material) ? b->material->is_two_sided() : false; if (skinned_a) { @@ -310,7 +311,7 @@ bool operation_compare(const render::operation& a, const render::operation& b) if (two_sided_b) { // A and B are both two-sided, sort by VAO - return (a.vertex_array < b.vertex_array); + return (a->vertex_array < b->vertex_array); } else { @@ -328,7 +329,7 @@ bool operation_compare(const render::operation& a, const render::operation& b) else { // A and B are both one-sided, sort by VAO - return (a.vertex_array < b.vertex_array); + return (a->vertex_array < b->vertex_array); } } } @@ -353,7 +354,7 @@ bool operation_compare(const render::operation& a, const render::operation& b) if (two_sided_b) { // A and B are both two-sided, sort by VAO - return (a.vertex_array < b.vertex_array); + return (a->vertex_array < b->vertex_array); } else { @@ -371,7 +372,7 @@ bool operation_compare(const render::operation& a, const render::operation& b) else { // A and B are both one-sided, sort by VAO - return (a.vertex_array < b.vertex_array); + return (a->vertex_array < b->vertex_array); } } } diff --git a/src/engine/render/passes/shadow-map-pass.hpp b/src/engine/render/passes/shadow-map-pass.hpp index 2ec1adf..3c599a3 100644 --- a/src/engine/render/passes/shadow-map-pass.hpp +++ b/src/engine/render/passes/shadow-map-pass.hpp @@ -52,7 +52,7 @@ public: * @param ctx Render context. * @param queue Render queue. */ - void render(const render::context& ctx, render::queue& queue) override; + void render(render::context& ctx) override; private: /** @@ -62,7 +62,7 @@ private: * @param ctx Render context. * @param queue Render queue. */ - void render_csm(const scene::directional_light& light, const render::context& ctx, render::queue& queue) const; + void render_csm(const scene::directional_light& light, render::context& ctx); std::unique_ptr unskinned_shader_program; const gl::shader_variable* unskinned_model_view_projection_var; diff --git a/src/engine/render/passes/sky-pass.cpp b/src/engine/render/passes/sky-pass.cpp index 25b1742..cc1fa06 100644 --- a/src/engine/render/passes/sky-pass.cpp +++ b/src/engine/render/passes/sky-pass.cpp @@ -168,7 +168,7 @@ sky_pass::sky_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffe rebuild_sky_lut_command_buffer(); } -void sky_pass::render(const render::context& ctx, render::queue& queue) +void sky_pass::render(render::context& ctx) { glDisable(GL_BLEND); glDisable(GL_DEPTH_TEST); diff --git a/src/engine/render/passes/sky-pass.hpp b/src/engine/render/passes/sky-pass.hpp index 4dae84e..a4fd6ef 100644 --- a/src/engine/render/passes/sky-pass.hpp +++ b/src/engine/render/passes/sky-pass.hpp @@ -49,7 +49,7 @@ class sky_pass: public pass public: sky_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager); virtual ~sky_pass() = default; - void render(const render::context& ctx, render::queue& queue) override; + void render(render::context& ctx) override; void update_tweens(); diff --git a/src/engine/render/renderer.cpp b/src/engine/render/renderer.cpp index 7d942cc..c41bca1 100644 --- a/src/engine/render/renderer.cpp +++ b/src/engine/render/renderer.cpp @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include #include @@ -39,211 +39,64 @@ namespace render { renderer::renderer() -{ - // Setup billboard render operation - billboard_op.bone_count = 0; - billboard_op.skinning_palette = nullptr; - billboard_op.drawing_mode = gl::drawing_mode::triangles; - billboard_op.vertex_array = nullptr; - billboard_op.start_index = 0; - billboard_op.index_count = 6; - billboard_op.instance_count = 0; - - // Allocate skinning palette - skinning_palette.resize(64); - - // Construct culling stage +{ culling_stage = std::make_unique(); + queue_stage = std::make_unique(); } void renderer::render(float t, float dt, float alpha, const scene::collection& collection) { - // Get list of all objects in the collection - const std::list* objects = collection.get_objects(); - - // Build list of cameras to be sorted - const std::list* cameras = collection.get_objects(scene::camera::object_type_id); - std::list sorted_cameras; - for (scene::object_base* object: *cameras) - { - sorted_cameras.push_back(static_cast(object)); - } - - // Sort cameras according to their respective compositing indices - sorted_cameras.sort - ( - [](const scene::camera* a, const scene::camera* b) -> bool - { - return a->get_composite_index() < b->get_composite_index(); - } - ); - // Init render context - render::context ctx; ctx.collection = &collection; ctx.t = t; ctx.dt = dt; ctx.alpha = alpha; - + + // Get list of cameras to be sorted + const auto& cameras = collection.get_objects(scene::camera::object_type_id); + // Process cameras in order - for (scene::camera* camera: sorted_cameras) + for (scene::object_base* camera_object: cameras) { // Skip inactive cameras - if (!camera->is_active()) + if (!camera_object->is_active()) { continue; } + scene::camera& camera = static_cast(*camera_object); + // Skip cameras with no compositors - compositor* compositor = camera->get_compositor(); + compositor* compositor = camera.get_compositor(); if (!compositor) { continue; } - // Update render context with camera parameters - ctx.camera = camera; - ctx.camera_transform = camera->get_transform_tween().interpolate(alpha); + // Update render context with camera-specific parameters + ctx.camera = &camera; + ctx.camera_transform = camera.get_transform_tween().interpolate(alpha); ctx.camera_forward = ctx.camera_transform.rotation * config::global_forward; ctx.camera_up = ctx.camera_transform.rotation * config::global_up; - ctx.clip_near = camera->get_view_frustum().get_near(); ///< @TODO: tween this - ctx.view = camera->get_view_tween().interpolate(alpha); - ctx.projection = camera->get_projection_tween().interpolate(alpha); + ctx.clip_near = camera.get_view_frustum().get_near(); ///< @TODO: tween this + ctx.view = camera.get_view_tween().interpolate(alpha); + ctx.projection = camera.get_projection_tween().interpolate(alpha); ctx.view_projection = ctx.projection * ctx.view; - ctx.exposure = std::exp2(-camera->get_exposure_tween().interpolate(alpha)); + ctx.exposure = std::exp2(-camera.get_exposure_tween().interpolate(alpha)); + + // Clear render queues + ctx.objects.clear(); + ctx.operations.clear(); // Execute culling stage culling_stage->execute(ctx); - // Create render queue - render::queue queue; - - // Queue render operations for each visible scene object - for (const scene::object_base* object: ctx.visible_objects) - { - // Process object - process_object(ctx, queue, object); - } + // Execute queue stage + queue_stage->execute(ctx); // Pass render context to the camera's compositor - compositor->composite(ctx, queue); + compositor->composite(ctx); } } -void renderer::set_billboard_vao(gl::vertex_array* vao) -{ - billboard_op.vertex_array = vao; -} - -void renderer::process_object(const render::context& ctx, render::queue& queue, const scene::object_base* object) const -{ - std::size_t type = object->get_object_type_id(); - - if (type == scene::model_instance::object_type_id) - process_model_instance(ctx, queue, static_cast(object)); - else if (type == scene::billboard::object_type_id) - process_billboard(ctx, queue, static_cast(object)); - else if (type == scene::lod_group::object_type_id) - process_lod_group(ctx, queue, static_cast(object)); - else if (type == scene::text::object_type_id) - process_text(ctx, queue, static_cast(object)); -} - -void renderer::process_model_instance(const render::context& ctx, render::queue& queue, const scene::model_instance* model_instance) const -{ - const model* model = model_instance->get_model().get(); - if (!model) - return; - - const auto& instance_materials = model_instance->get_materials(); - const auto& material_groups = model->get_groups(); - - render::operation operation; - operation.transform = math::matrix_cast(model_instance->get_transform_tween().interpolate(ctx.alpha)); - operation.depth = ctx.clip_near.signed_distance(float3(operation.transform[3])); - operation.vertex_array = model->get_vertex_array().get(); - operation.instance_count = model_instance->get_instance_count(); - - // Skinning parameters - operation.bone_count = model_instance->get_pose().size(); - if (operation.bone_count) - { - /// @TODO skinning palette should be model instance-specific - //operation.skinning_palette = skinning_palette.data(); - //::matrix_palette(model->get_skeleton().inverse_bind_pose, model_instance->get_pose(), skinning_palette.data()); - operation.skinning_palette = nullptr; - - } - else - { - operation.skinning_palette = nullptr; - } - - for (std::size_t i = 0; i < material_groups.size(); ++i) - { - const auto& group = material_groups[i]; - - if (instance_materials[i]) - { - // Override model group material with the instance's material - operation.material = instance_materials[i].get(); - } - else - { - // Use model material - operation.material = group.material.get(); - } - - operation.drawing_mode = group.drawing_mode; - operation.start_index = group.start_index; - operation.index_count = group.index_count; - - queue.push_back(operation); - } -} - -void renderer::process_billboard(const render::context& ctx, render::queue& queue, const scene::billboard* billboard) const -{ - math::transform billboard_transform = billboard->get_transform_tween().interpolate(ctx.alpha); - billboard_op.material = billboard->get_material().get(); - billboard_op.depth = ctx.clip_near.signed_distance(float3(billboard_transform.translation)); - - // Align billboard - if (billboard->get_billboard_type() == scene::billboard_type::spherical) - { - billboard_transform.rotation = math::normalize(math::look_rotation(ctx.camera_forward, ctx.camera_up) * billboard_transform.rotation); - } - else if (billboard->get_billboard_type() == scene::billboard_type::cylindrical) - { - const float3& alignment_axis = billboard->get_alignment_axis(); - float3 look = math::normalize(geom::project_on_plane(billboard_transform.translation - ctx.camera_transform.translation, {0.0f, 0.0f, 0.0f}, alignment_axis)); - float3 right = math::normalize(math::cross(alignment_axis, look)); - look = math::cross(right, alignment_axis); - float3 up = math::cross(look, right); - billboard_transform.rotation = math::normalize(math::look_rotation(look, up) * billboard_transform.rotation); - } - - billboard_op.transform = math::matrix_cast(billboard_transform); - - queue.push_back(billboard_op); -} - -void renderer::process_lod_group(const render::context& ctx, render::queue& queue, const scene::lod_group* lod_group) const -{ - // Select level of detail - std::size_t level = lod_group->select_lod(*ctx.camera); - - // Process all objects in the group with the selected level of detail - const std::list& objects = lod_group->get_objects(level); - for (const scene::object_base* object: objects) - { - process_object(ctx, queue, object); - } -} - -void renderer::process_text(const render::context& ctx, render::queue& queue, const scene::text* text) const -{ - text->render(ctx, queue); -} - } // namespace render diff --git a/src/engine/render/renderer.hpp b/src/engine/render/renderer.hpp index b19fc25..88d7f7a 100644 --- a/src/engine/render/renderer.hpp +++ b/src/engine/render/renderer.hpp @@ -20,22 +20,11 @@ #ifndef ANTKEEPER_RENDER_RENDERER_HPP #define ANTKEEPER_RENDER_RENDERER_HPP -#include #include -#include -#include -#include -#include - -namespace scene -{ - class collection; - class object_base; - class model_instance; - class billboard; - class lod_group; - class text; -} +#include +#include +#include +#include namespace render { @@ -60,21 +49,11 @@ public: */ void render(float t, float dt, float alpha, const scene::collection& collection); - /** - * Sets the VAO to be used when generating render operations for billboards. - */ - void set_billboard_vao(gl::vertex_array* vao); - private: - void process_object(const render::context& ctx, render::queue& queue, const scene::object_base* object) const; - void process_model_instance(const render::context& ctx, render::queue& queue, const scene::model_instance* model_instance) const; - void process_billboard(const render::context& ctx, render::queue& queue, const scene::billboard* billboard) const; - void process_lod_group(const render::context& ctx, render::queue& queue, const scene::lod_group* lod_group) const; - void process_text(const render::context& ctx, render::queue& queue, const scene::text* text) const; - - mutable render::operation billboard_op; - std::vector skinning_palette; + render::context ctx; + std::unique_ptr culling_stage; + std::unique_ptr queue_stage; }; } // namespace render diff --git a/src/engine/render/stage.cpp b/src/engine/render/stage.cpp index 4532775..75c51a5 100644 --- a/src/engine/render/stage.cpp +++ b/src/engine/render/stage.cpp @@ -21,9 +21,6 @@ namespace render { -void stage::set_priority(int priority) -{ - this->priority = priority; -} + } // namespace render diff --git a/src/engine/render/stage.hpp b/src/engine/render/stage.hpp index 5326d62..e145caf 100644 --- a/src/engine/render/stage.hpp +++ b/src/engine/render/stage.hpp @@ -39,22 +39,6 @@ public: * @param ctx Render context. */ virtual void execute(render::context& ctx) = 0; - - /** - * Sets the priority of the stage's execution order in the render pipeline. - * - * @param priority Stage execution order priority. Stages with lower priorities are executed first. - */ - void set_priority(int priority); - - /// Returns the priority of the stage's execution order in the render pipeline. - [[nodiscard]] inline int get_priority() const noexcept - { - return priority; - } - -private: - int priority{0}; }; } // namespace render diff --git a/src/engine/render/stage/culling-stage.cpp b/src/engine/render/stages/culling-stage.cpp similarity index 88% rename from src/engine/render/stage/culling-stage.cpp rename to src/engine/render/stages/culling-stage.cpp index 99191e4..3b62e7f 100644 --- a/src/engine/render/stage/culling-stage.cpp +++ b/src/engine/render/stages/culling-stage.cpp @@ -17,7 +17,7 @@ * along with Antkeeper source code. If not, see . */ -#include +#include #include #include #include @@ -28,8 +28,8 @@ namespace render { void culling_stage::execute(render::context& ctx) { - // Get list of all objects in the collection - const std::list& objects = *(ctx.collection->get_objects()); + // Get all objects in the collection + const auto& objects = ctx.collection->get_objects(); // Get camera culling volume ctx.camera_culling_volume = ctx.camera->get_culling_mask(); @@ -38,9 +38,6 @@ void culling_stage::execute(render::context& ctx) ctx.camera_culling_volume = &ctx.camera->get_world_bounds(); } - // Clear set of visible objects - ctx.visible_objects.clear(); - // Construct mutex to guard set of visible objects std::mutex mutex; @@ -77,7 +74,7 @@ void culling_stage::execute(render::context& ctx) // Insert object into set of visible objects std::lock_guard guard(mutex); - ctx.visible_objects.push_back(object); + ctx.objects.push_back(object); } ); } diff --git a/src/engine/render/stage/culling-stage.hpp b/src/engine/render/stages/culling-stage.hpp similarity index 100% rename from src/engine/render/stage/culling-stage.hpp rename to src/engine/render/stages/culling-stage.hpp diff --git a/src/game/components/locomotion-component.hpp b/src/engine/render/stages/queue-stage.cpp similarity index 64% rename from src/game/components/locomotion-component.hpp rename to src/engine/render/stages/queue-stage.cpp index fa8ffd0..969ae77 100644 --- a/src/game/components/locomotion-component.hpp +++ b/src/engine/render/stages/queue-stage.cpp @@ -17,21 +17,26 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_GAME_PLACEMENT_COMPONENT_HPP -#define ANTKEEPER_GAME_PLACEMENT_COMPONENT_HPP +#include +#include +#include +#include -#include -#include +namespace render { - -struct locomotion_component +void queue_stage::execute(render::context& ctx) { - //const geom::mesh::face* triangle; - //float3 barycentric_position; - - float yaw; -}; - - -#endif // ANTKEEPER_GAME_PLACEMENT_COMPONENT_HPP + // For each visible object in the render context + std::for_each + ( + std::execution::seq, + std::begin(ctx.objects), + std::end(ctx.objects), + [&ctx](scene::object_base* object) + { + object->render(ctx); + } + ); +} +} // namespace render diff --git a/src/engine/render/queue.hpp b/src/engine/render/stages/queue-stage.hpp similarity index 74% rename from src/engine/render/queue.hpp rename to src/engine/render/stages/queue-stage.hpp index 30f0105..03333c8 100644 --- a/src/engine/render/queue.hpp +++ b/src/engine/render/stages/queue-stage.hpp @@ -17,17 +17,22 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_RENDER_QUEUE_HPP -#define ANTKEEPER_RENDER_QUEUE_HPP +#ifndef ANTKEEPER_RENDER_QUEUE_STAGE_HPP +#define ANTKEEPER_RENDER_QUEUE_STAGE_HPP -#include -#include +#include namespace render { -/// Queue of render operations -typedef std::list queue; +/** + * Builds render queues. + */ +class queue_stage: public stage +{ +public: + void execute(render::context& ctx) override; +}; } // namespace render -#endif // ANTKEEPER_RENDER_QUEUE_HPP +#endif // ANTKEEPER_RENDER_QUEUE_STAGE_HPP diff --git a/src/engine/scene/ambient-light.hpp b/src/engine/scene/ambient-light.hpp index 0063cd6..a0f721f 100644 --- a/src/engine/scene/ambient-light.hpp +++ b/src/engine/scene/ambient-light.hpp @@ -27,16 +27,12 @@ namespace scene { class ambient_light: public light { public: - /// Returns light_type::ambient - virtual light_type get_light_type() const; + [[nodiscard]] inline light_type get_light_type() const noexcept override + { + return light_type::ambient; + } }; -inline light_type ambient_light::get_light_type() const -{ - return light_type::ambient; -} - } // namespace scene #endif // ANTKEEPER_SCENE_AMBIENT_LIGHT_HPP - diff --git a/src/engine/scene/billboard.cpp b/src/engine/scene/billboard.cpp index 118a537..48b435b 100644 --- a/src/engine/scene/billboard.cpp +++ b/src/engine/scene/billboard.cpp @@ -19,6 +19,8 @@ #include #include +#include +#include namespace scene { @@ -26,30 +28,94 @@ const typename billboard::aabb_type billboard::local_bounds = {{-1, -1, -1}, {1, billboard::billboard(): world_bounds(local_bounds), - material(nullptr), type(billboard_type::flat), alignment_axis(config::global_up) -{} - -billboard::billboard(const billboard& other) { - *this = other; + const float vertex_data[] = + { + -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, + -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, + 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, + 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, + -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, + 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f + }; + + const std::size_t vertex_size = 8; + const std::size_t vertex_stride = sizeof(float) * vertex_size; + const std::size_t vertex_count = 6; + + vbo = std::make_unique(gl::buffer_usage::static_draw, sizeof(float) * vertex_size * vertex_count, std::as_bytes(std::span{vertex_data})); + + std::size_t attribute_offset = 0; + + // Define position vertex attribute + gl::vertex_attribute position_attribute; + position_attribute.buffer = vbo.get(); + position_attribute.offset = attribute_offset; + position_attribute.stride = vertex_stride; + position_attribute.type = gl::vertex_attribute_type::float_32; + position_attribute.components = 3; + attribute_offset += position_attribute.components * sizeof(float); + + // Define UV vertex attribute + gl::vertex_attribute uv_attribute; + uv_attribute.buffer = vbo.get(); + uv_attribute.offset = attribute_offset; + uv_attribute.stride = vertex_stride; + uv_attribute.type = gl::vertex_attribute_type::float_32; + uv_attribute.components = 2; + attribute_offset += uv_attribute.components * sizeof(float); + + // Define barycentric vertex attribute + gl::vertex_attribute barycentric_attribute; + barycentric_attribute.buffer = vbo.get(); + barycentric_attribute.offset = attribute_offset; + barycentric_attribute.stride = vertex_stride; + barycentric_attribute.type = gl::vertex_attribute_type::float_32; + barycentric_attribute.components = 3; + //attribute_offset += barycentric_attribute.components * sizeof(float); + + // Bind vertex attributes to VAO + vao = std::make_unique(); + vao->bind(render::vertex_attribute::position, position_attribute); + vao->bind(render::vertex_attribute::uv, uv_attribute); + vao->bind(render::vertex_attribute::barycentric, barycentric_attribute); + + // Init render operation + render_op.vertex_array = vao.get(); + render_op.drawing_mode = gl::drawing_mode::triangles; + render_op.start_index = 0; + render_op.index_count = 6; } -billboard& billboard::operator=(const billboard& other) +void billboard::render(render::context& ctx) const { - material = other.material; - type = other.type; - alignment_axis = other.alignment_axis; - set_transform(other.get_transform()); - set_active(other.is_active()); - set_culling_mask(other.get_culling_mask()); - return *this; + auto transform = get_transform_tween().interpolate(ctx.alpha); + + // Align billboard + if (type == scene::billboard_type::spherical) + { + transform.rotation = math::normalize(math::look_rotation(ctx.camera_forward, ctx.camera_up) * transform.rotation); + } + else if (type == scene::billboard_type::cylindrical) + { + float3 look = math::normalize(geom::project_on_plane(transform.translation - ctx.camera_transform.translation, {0.0f, 0.0f, 0.0f}, alignment_axis)); + float3 right = math::normalize(math::cross(alignment_axis, look)); + look = math::cross(right, alignment_axis); + float3 up = math::cross(look, right); + transform.rotation = math::normalize(math::look_rotation(look, up) * transform.rotation); + } + + render_op.transform = math::matrix_cast(transform); + render_op.depth = ctx.clip_near.signed_distance(transform.translation); + + ctx.operations.emplace_back(&render_op); } void billboard::set_material(std::shared_ptr material) { - this->material = material; + render_op.material = material; } void billboard::set_billboard_type(billboard_type type) @@ -70,10 +136,6 @@ void billboard::transformed() void billboard::update_tweens() { object_base::update_tweens(); - if (material) - { - //material->update_tweens(); - } } } // namespace scene diff --git a/src/engine/scene/billboard.hpp b/src/engine/scene/billboard.hpp index 72a3f9c..0fb7608 100644 --- a/src/engine/scene/billboard.hpp +++ b/src/engine/scene/billboard.hpp @@ -24,6 +24,9 @@ #include #include #include +#include +#include +#include #include #include @@ -32,13 +35,13 @@ namespace scene { /// Enumerates billboard types. enum class billboard_type: std::uint8_t { - // No alignment + /// No alignment flat, - // Aligns to face camera + /// Aligns to face camera spherical, - // Rotates about an alignment axis to face camera + /// Rotates about an alignment axis to face camera cylindrical }; @@ -51,8 +54,8 @@ public: typedef geom::aabb aabb_type; billboard(); - billboard(const billboard& other); - billboard& operator=(const billboard& other); + + void render(render::context& ctx) const override; void set_material(std::shared_ptr material); @@ -72,9 +75,9 @@ public: return world_bounds; } - [[nodiscard]] inline const std::shared_ptr& get_material() const noexcept + [[nodiscard]] inline std::shared_ptr get_material() const noexcept { - return material; + return render_op.material; } [[nodiscard]] inline billboard_type get_billboard_type() const noexcept @@ -93,9 +96,12 @@ private: static const aabb_type local_bounds; virtual void transformed(); - + + std::unique_ptr vbo; + std::unique_ptr vao; + + mutable render::operation render_op; aabb_type world_bounds; - std::shared_ptr material; billboard_type type; float3 alignment_axis; }; diff --git a/src/engine/scene/collection.cpp b/src/engine/scene/collection.cpp index bebdcaa..271d64e 100644 --- a/src/engine/scene/collection.cpp +++ b/src/engine/scene/collection.cpp @@ -24,14 +24,14 @@ namespace scene { void collection::add_object(object_base* object) { - objects.push_back(object); - object_map[object->get_object_type_id()].push_back(object); + objects.emplace_back(object); + object_map[object->get_object_type_id()].emplace_back(object); } void collection::remove_object(object_base* object) { - objects.remove(object); - object_map[object->get_object_type_id()].remove(object); + std::erase(objects, object); + std::erase(object_map[object->get_object_type_id()], object); } void collection::remove_objects() diff --git a/src/engine/scene/collection.hpp b/src/engine/scene/collection.hpp index de07fa2..6d4794a 100644 --- a/src/engine/scene/collection.hpp +++ b/src/engine/scene/collection.hpp @@ -20,7 +20,7 @@ #ifndef ANTKEEPER_SCENE_COLLECTION_HPP #define ANTKEEPER_SCENE_COLLECTION_HPP -#include +#include #include namespace scene { @@ -52,33 +52,31 @@ public: /// Updates the tweens of all objects in the collection. void update_tweens(); - - /// Returns a list of all objects in the collection. - const std::list* get_objects() const; + + /// Returns all objects in the collection. + [[nodiscard]] inline const std::vector& get_objects() const noexcept + { + return objects; + } /** - * Returns a list of all objects in the collection with the specified type ID. + * Returns all objects in the collection with the given type ID. * * @param type_id Scene object type ID. - * @return List of scene objects with the specified type ID. + * + * @return Scene objects with the given type ID. */ - const std::list* get_objects(std::size_t type_id) const; + [[nodiscard]] inline const std::vector& get_objects(std::size_t type_id) const + { + return object_map[type_id]; + } private: - std::list objects; - mutable std::unordered_map> object_map; + std::vector objects; + + mutable std::unordered_map> object_map; }; -inline const std::list* collection::get_objects() const -{ - return &objects; -} - -inline const std::list* collection::get_objects(std::size_t type_id) const -{ - return &object_map[type_id]; -} - } // namespace scene #endif // ANTKEEPER_SCENE_COLLECTION_HPP diff --git a/src/engine/scene/directional-light.cpp b/src/engine/scene/directional-light.cpp index a4b4da0..20a75cf 100644 --- a/src/engine/scene/directional-light.cpp +++ b/src/engine/scene/directional-light.cpp @@ -32,13 +32,7 @@ static float3 interpolate_direction(const float3& x, const float3& y, float a) } directional_light::directional_light(): - direction(config::global_forward, interpolate_direction), - shadow_caster(false), - shadow_framebuffer(nullptr), - shadow_bias(0.005f), - shadow_cascade_count(4), - shadow_cascade_coverage(1.0f), - shadow_cascade_distribution(0.8f) + direction(config::global_forward, interpolate_direction) { shadow_cascade_distances.resize(shadow_cascade_count); shadow_cascade_matrices.resize(shadow_cascade_count); diff --git a/src/engine/scene/directional-light.hpp b/src/engine/scene/directional-light.hpp index 7fd87bf..05636d8 100644 --- a/src/engine/scene/directional-light.hpp +++ b/src/engine/scene/directional-light.hpp @@ -37,10 +37,16 @@ public: directional_light(); /// Returns light_type::directional. - virtual light_type get_light_type() const; + [[nodiscard]] inline light_type get_light_type() const noexcept override + { + return light_type::directional; + } /// Returns the normalized direction vector of the light. - const float3& get_direction() const; + [[nodiscard]] inline const float3& get_direction() const noexcept + { + return direction[1]; + } inline const tween& get_direction_tween() const noexcept { @@ -48,7 +54,7 @@ public: } /// @copydoc object_base::update_tweens(); - virtual void update_tweens(); + void update_tweens() override; /// @name Shadow /// @{ @@ -96,108 +102,82 @@ public: void set_shadow_cascade_distribution(float weight) noexcept; /// Returns `true` if the light casts shadows, `false` otherwise. - bool is_shadow_caster() const noexcept; + [[nodiscard]] inline bool is_shadow_caster() const noexcept + { + return shadow_caster; + } /// Returns the shadow map framebuffer, of `nullptr` if no shadow map framebuffer is set. - const gl::framebuffer* get_shadow_framebuffer() const noexcept; + [[nodiscard]] inline const gl::framebuffer* get_shadow_framebuffer() const noexcept + { + return shadow_framebuffer; + } /// Returns the shadow bias factor. - float get_shadow_bias() const noexcept; + [[nodiscard]] inline float get_shadow_bias() const noexcept + { + return shadow_bias; + } /// Returns the number of shadow cascades. - unsigned int get_shadow_cascade_count() const noexcept; + [[nodiscard]] inline unsigned int get_shadow_cascade_count() const noexcept + { + return shadow_cascade_count; + } /// Returns the shadow cascade coverage factor. - float get_shadow_cascade_coverage() const noexcept; + [[nodiscard]] inline float get_shadow_cascade_coverage() const noexcept + { + return shadow_cascade_coverage; + } /// Returns the shadow cascade distribution weight. - float get_shadow_cascade_distribution() const noexcept; + [[nodiscard]] inline float get_shadow_cascade_distribution() const noexcept + { + return shadow_cascade_distribution; + } /// Returns the array of shadow cascade far clipping plane distances. - const std::vector& get_shadow_cascade_distances() const noexcept; - std::vector& get_shadow_cascade_distances() noexcept; + /// @{ + [[nodiscard]] inline const std::vector& get_shadow_cascade_distances() const noexcept + { + return shadow_cascade_distances; + } + [[nodiscard]] inline std::vector& get_shadow_cascade_distances() noexcept + { + return shadow_cascade_distances; + } + /// @} /// Returns the array of world-space to cascade texture-space transformation matrices. - const std::vector& get_shadow_cascade_matrices() const noexcept; - std::vector& get_shadow_cascade_matrices() noexcept; + /// @{ + [[nodiscard]] inline const std::vector& get_shadow_cascade_matrices() const noexcept + { + return shadow_cascade_matrices; + } + [[nodiscard]] inline std::vector& get_shadow_cascade_matrices() noexcept + { + return shadow_cascade_matrices; + } + /// @} /// @} private: - virtual void transformed(); + void transformed() override; tween direction; - bool shadow_caster; - const gl::framebuffer* shadow_framebuffer; - float shadow_bias; - unsigned int shadow_cascade_count; - float shadow_cascade_coverage; - float shadow_cascade_distribution; + bool shadow_caster{false}; + const gl::framebuffer* shadow_framebuffer{nullptr}; + float shadow_bias{0.005f}; + unsigned int shadow_cascade_count{4}; + float shadow_cascade_coverage{1.0f}; + float shadow_cascade_distribution{0.8f}; mutable std::vector shadow_cascade_distances; mutable std::vector shadow_cascade_matrices; }; -inline light_type directional_light::get_light_type() const -{ - return light_type::directional; -} - -inline const float3& directional_light::get_direction() const -{ - return direction[1]; -} - -inline bool directional_light::is_shadow_caster() const noexcept -{ - return shadow_caster; -} - -inline const gl::framebuffer* directional_light::get_shadow_framebuffer() const noexcept -{ - return shadow_framebuffer; -} - -inline float directional_light::get_shadow_bias() const noexcept -{ - return shadow_bias; -} - -inline unsigned int directional_light::get_shadow_cascade_count() const noexcept -{ - return shadow_cascade_count; -} - -inline float directional_light::get_shadow_cascade_coverage() const noexcept -{ - return shadow_cascade_coverage; -} - -inline float directional_light::get_shadow_cascade_distribution() const noexcept -{ - return shadow_cascade_distribution; -} - -inline const std::vector& directional_light::get_shadow_cascade_distances() const noexcept -{ - return shadow_cascade_distances; -} - -inline std::vector& directional_light::get_shadow_cascade_distances() noexcept -{ - return shadow_cascade_distances; -} - -inline const std::vector& directional_light::get_shadow_cascade_matrices() const noexcept -{ - return shadow_cascade_matrices; -} - -inline std::vector& directional_light::get_shadow_cascade_matrices() noexcept -{ - return shadow_cascade_matrices; -} - } // namespace scene #endif // ANTKEEPER_SCENE_DIRECTIONAL_LIGHT_HPP diff --git a/src/engine/scene/light.hpp b/src/engine/scene/light.hpp index 67d00d6..23adfa3 100644 --- a/src/engine/scene/light.hpp +++ b/src/engine/scene/light.hpp @@ -54,7 +54,7 @@ public: light(); /// Returns an enumeration denoting the light object type. - virtual light_type get_light_type() const = 0; + [[nodiscard]] virtual light_type get_light_type() const noexcept = 0; /** * Sets the color of the light. @@ -71,23 +71,49 @@ public: void set_intensity(float intensity); /// Returns the local-space bounding volume of the light. - virtual const bounding_volume_type& get_local_bounds() const; + inline const bounding_volume_type& get_local_bounds() const noexcept override + { + return local_bounds; + } /// Returns the world-space bounding volume of the light. - virtual const bounding_volume_type& get_world_bounds() const; + inline const bounding_volume_type& get_world_bounds() const noexcept override + { + return world_bounds; + } /// Returns the light color. - const float3& get_color() const; + [[nodiscard]] inline const float3& get_color() const noexcept + { + return color[1]; + } /// Returns the light intensity. - float get_intensity() const; + [[nodiscard]] inline float get_intensity() const noexcept + { + return intensity[1]; + } /// Returns the intensity-scaled light color. - const float3& get_scaled_color() const; - - const tween& get_color_tween() const; - const tween& get_intensity_tween() const; - const tween& get_scaled_color_tween() const; + [[nodiscard]] inline const float3& get_scaled_color() const noexcept + { + return scaled_color[1]; + } + + [[nodiscard]] inline const tween& get_color_tween() const noexcept + { + return color; + } + + [[nodiscard]] inline const tween& get_intensity_tween() const noexcept + { + return intensity; + } + + [[nodiscard]] inline const tween& get_scaled_color_tween() const noexcept + { + return scaled_color; + } /// @copydoc object_base::update_tweens(); virtual void update_tweens(); @@ -102,46 +128,6 @@ private: sphere_type world_bounds; }; -inline const typename object_base::bounding_volume_type& light::get_local_bounds() const -{ - return local_bounds; -} - -inline const typename object_base::bounding_volume_type& light::get_world_bounds() const -{ - return world_bounds; -} - -inline const float3& light::get_color() const -{ - return color[1]; -} - -inline float light::get_intensity() const -{ - return intensity[1]; -} - -inline const float3& light::get_scaled_color() const -{ - return scaled_color[1]; -} - -inline const tween& light::get_color_tween() const -{ - return color; -} - -inline const tween& light::get_intensity_tween() const -{ - return intensity; -} - -inline const tween& light::get_scaled_color_tween() const -{ - return scaled_color; -} - } // namespace scene #endif // ANTKEEPER_SCENE_LIGHT_HPP diff --git a/src/engine/scene/object.cpp b/src/engine/scene/object.cpp index 13e3437..cd7da1e 100644 --- a/src/engine/scene/object.cpp +++ b/src/engine/scene/object.cpp @@ -33,25 +33,15 @@ typename object_base::transform_type object_base::interpolate_transforms(const t } object_base::object_base(): - active(true), - transform(math::transform::identity, interpolate_transforms), - culling_mask(nullptr) + transform(math::transform::identity, interpolate_transforms) {} -void object_base::set_culling_mask(const bounding_volume_type* culling_mask) -{ - this->culling_mask = culling_mask; -} - std::size_t object_base::next_object_type_id() { static std::atomic id{0}; return id++; } -void object_base::render(const render::context& ctx, render::queue& queue) const -{} - void object_base::update_tweens() { transform.update(); diff --git a/src/engine/scene/object.hpp b/src/engine/scene/object.hpp index 1da35da..de333dd 100644 --- a/src/engine/scene/object.hpp +++ b/src/engine/scene/object.hpp @@ -26,7 +26,6 @@ #include #include #include -#include #include #include @@ -44,28 +43,19 @@ public: typedef geom::bounding_volume bounding_volume_type; /// Returns the type ID for this scene object type. - virtual const std::size_t get_object_type_id() const = 0; + virtual const std::size_t get_object_type_id() const noexcept = 0; /** * Creates a scene object base. */ object_base(); - - /** - * Destroys a scene object base. - */ - virtual ~object_base() = default; /** - * Adds a render operation describing this object to a render queue. + * Adds render operations to a render context. * * @param ctx Render context. - * @param queue Render queue. - * - * @see render::context - * @see render::operation */ - virtual void render(const render::context& ctx, render::queue& queue) const; + inline virtual void render(render::context& ctx) const {} /** * Updates all tweens in the scene object. @@ -75,7 +65,10 @@ public: /** * Activates or deactivates the scene object. */ - void set_active(bool active); + inline void set_active(bool active) noexcept + { + this->active = active; + } /** * @@ -85,71 +78,116 @@ public: /** * Sets the scene object's transform. */ - void set_transform(const transform_type& transform); + inline void set_transform(const transform_type& transform) + { + this->transform[1] = transform; + transformed(); + } /** * Sets the scene object's translation. */ - void set_translation(const vector_type& translation); + inline void set_translation(const vector_type& translation) + { + transform[1].translation = translation; + transformed(); + } /** * Sets the scene object's rotation. */ - void set_rotation(const quaternion_type& rotation); + inline void set_rotation(const quaternion_type& rotation) + { + transform[1].rotation = rotation; + transformed(); + } /** * Sets the scene object's scale. */ - void set_scale(const vector_type& scale); + inline void set_scale(const vector_type& scale) + { + transform[1].scale = scale; + transformed(); + } /** * Sets a culling mask for the object, which will be used for view-frustum culling instead of the object's bounds. */ - void set_culling_mask(const bounding_volume_type* culling_mask); + inline void set_culling_mask(const bounding_volume_type* culling_mask) noexcept + { + this->culling_mask = culling_mask; + } /// Returns whether the scene object is active. - bool is_active() const; + [[nodiscard]] inline bool is_active() const noexcept + { + return active; + } /** * Returns the transform. */ - const transform_type& get_transform() const; + [[nodiscard]] inline const transform_type& get_transform() const noexcept + { + return transform[1]; + } /** * Returns the transform's translation vector. */ - const vector_type& get_translation() const; + [[nodiscard]] inline const vector_type& get_translation() const noexcept + { + return transform[1].translation; + } /** * Returns the transform's rotation quaternion. */ - const quaternion_type& get_rotation() const; + [[nodiscard]] inline const quaternion_type& get_rotation() const noexcept + { + return transform[1].rotation; + } /** * Returns the transform's scale vector. */ - const vector_type& get_scale() const; + [[nodiscard]] inline const vector_type& get_scale() const noexcept + { + return transform[1].scale; + } /** * Returns the transform tween. */ - const tween& get_transform_tween() const; - tween& get_transform_tween(); + /// @{ + [[nodiscard]] inline const tween& get_transform_tween() const noexcept + { + return transform; + } + [[nodiscard]] inline tween& get_transform_tween() noexcept + { + return transform; + } + /// @} /** * Returns the local-space (untransformed) bounds of the object. */ - virtual const bounding_volume_type& get_local_bounds() const = 0; + [[nodiscard]] virtual const bounding_volume_type& get_local_bounds() const = 0; /** * Returns the world-space (transformed) bounds of the object. */ - virtual const bounding_volume_type& get_world_bounds() const = 0; + [[nodiscard]] virtual const bounding_volume_type& get_world_bounds() const = 0; /** * Returns the culling mask of the object. */ - const bounding_volume_type* get_culling_mask() const; + [[nodiscard]] inline const bounding_volume_type* get_culling_mask() const noexcept + { + return culling_mask; + } protected: static std::size_t next_object_type_id(); @@ -163,80 +201,11 @@ private: */ virtual void transformed(); - bool active; + bool active{true}; tween transform; - const bounding_volume_type* culling_mask; + const bounding_volume_type* culling_mask{nullptr}; }; -inline void object_base::set_active(bool active) -{ - this->active = active; -} - -inline void object_base::set_transform(const transform_type& transform) -{ - this->transform[1] = transform; - transformed(); -} - -inline void object_base::set_translation(const vector_type& translation) -{ - transform[1].translation = translation; - transformed(); -} - -inline void object_base::set_rotation(const quaternion_type& rotation) -{ - transform[1].rotation = rotation; - transformed(); -} - -inline void object_base::set_scale(const vector_type& scale) -{ - transform[1].scale = scale; - transformed(); -} - -inline bool object_base::is_active() const -{ - return active; -} - -inline const typename object_base::transform_type& object_base::get_transform() const -{ - return transform[1]; -} - -inline const typename object_base::vector_type& object_base::get_translation() const -{ - return get_transform().translation; -} - -inline const typename object_base::quaternion_type& object_base::get_rotation() const -{ - return get_transform().rotation; -} - -inline const typename object_base::vector_type& object_base::get_scale() const -{ - return get_transform().scale; -} - -inline const tween& object_base::get_transform_tween() const -{ - return transform; -} - -inline tween& object_base::get_transform_tween() -{ - return transform; -} - -inline const typename object_base::bounding_volume_type* object_base::get_culling_mask() const -{ - return culling_mask; -} - /** * Abstract base class for lights, cameras, model instances, and other scene objects. * @@ -249,20 +218,15 @@ public: /// Unique type ID for this scene object type. static const std::atomic object_type_id; - /// @copydoc object_base::get_object_type_id() const - virtual const std::size_t get_object_type_id() const final; + inline const std::size_t get_object_type_id() const noexcept final + { + return object_type_id; + } }; template const std::atomic object::object_type_id{object_base::next_object_type_id()}; -template -inline const std::size_t object::get_object_type_id() const -{ - return object_type_id; -} - } // namespace scene #endif // ANTKEEPER_SCENE_OBJECT_HPP - diff --git a/src/engine/scene/point-light.hpp b/src/engine/scene/point-light.hpp index 326a5b2..049680a 100644 --- a/src/engine/scene/point-light.hpp +++ b/src/engine/scene/point-light.hpp @@ -34,7 +34,10 @@ public: point_light(); /// Returns light_type::point - virtual light_type get_light_type() const; + [[nodiscard]] inline light_type get_light_type() const noexcept override + { + return light_type::point; + } /** * Sets the attenuation factors of the light. @@ -44,33 +47,23 @@ public: void set_attenuation(const float3& attenuation); /// Returns the attenuation factors of the light. - const float3& get_attenuation() const; + [[nodiscard]] inline const float3& get_attenuation() const noexcept + { + return attenuation[1]; + } /// Returns the attenuation tween. - const tween& get_attenuation_tween() const; + [[nodiscard]] inline const tween& get_attenuation_tween() const noexcept + { + return attenuation; + } - /// @copydoc object_base::update_tweens(); - virtual void update_tweens(); + void update_tweens() override; private: tween attenuation; }; -inline light_type point_light::get_light_type() const -{ - return light_type::point; -} - -inline const float3& point_light::get_attenuation() const -{ - return attenuation[1]; -} - -inline const tween& point_light::get_attenuation_tween() const -{ - return attenuation; -} - } // namespace scene #endif // ANTKEEPER_SCENE_POINT_LIGHT_HPP diff --git a/src/engine/scene/scene.hpp b/src/engine/scene/scene.hpp index 559dc85..b9b8c7e 100644 --- a/src/engine/scene/scene.hpp +++ b/src/engine/scene/scene.hpp @@ -30,7 +30,7 @@ namespace scene {} #include #include #include -#include +#include #include #include #include diff --git a/src/engine/scene/spot-light.hpp b/src/engine/scene/spot-light.hpp index f46f442..7c62bda 100644 --- a/src/engine/scene/spot-light.hpp +++ b/src/engine/scene/spot-light.hpp @@ -35,7 +35,10 @@ public: spot_light(); /// Returns light_type::spot - virtual light_type get_light_type() const; + [[nodiscard]] inline light_type get_light_type() const noexcept override + { + return light_type::spot; + } /** * Sets the attenuation factors of the light. @@ -52,34 +55,57 @@ public: void set_cutoff(const float2& cutoff); /// Returns the direction vector. - const float3& get_direction() const; + [[nodiscard]] inline const float3& get_direction() const noexcept + { + return direction[1]; + } /// Returns the attenuation factors of the light. - const float3& get_attenuation() const; + [[nodiscard]] inline const float3& get_attenuation() const noexcept + { + return attenuation[1]; + } /// Returns the spot light cutoff angles. - const float2& get_cutoff() const; + [[nodiscard]] inline const float2& get_cutoff() const noexcept + { + return cutoff[1]; + } /// Returns the cosine of the spot light cutoff angles. - const float2& get_cosine_cutoff() const; + [[nodiscard]] inline const float2& get_cosine_cutoff() const noexcept + { + return cosine_cutoff[1]; + } /// Returns the direction tween. - const tween& get_direction_tween() const; + [[nodiscard]] inline const tween& get_direction_tween() const noexcept + { + return direction; + } /// Returns the attenuation tween. - const tween& get_attenuation_tween() const; + [[nodiscard]] inline const tween& get_attenuation_tween() const noexcept + { + return attenuation; + } /// Returns the cutoff tween. - const tween& get_cutoff_tween() const; + [[nodiscard]] inline const tween& get_cutoff_tween() const noexcept + { + return cutoff; + } /// Returns the cosine cutoff tween. - const tween& get_cosine_cutoff_tween() const; + [[nodiscard]] inline const tween& get_cosine_cutoff_tween() const noexcept + { + return cosine_cutoff; + } - /// @copydoc object_base::update_tweens(); - virtual void update_tweens(); + void update_tweens() override; private: - virtual void transformed(); + void transformed() override; tween direction; tween attenuation; @@ -87,51 +113,6 @@ private: tween cosine_cutoff; }; -inline light_type spot_light::get_light_type() const -{ - return light_type::spot; -} - -inline const float3& spot_light::get_direction() const -{ - return direction[1]; -} - -inline const float3& spot_light::get_attenuation() const -{ - return attenuation[1]; -} - -inline const float2& spot_light::get_cutoff() const -{ - return cutoff[1]; -} - -inline const float2& spot_light::get_cosine_cutoff() const -{ - return cosine_cutoff[1]; -} - -inline const tween& spot_light::get_direction_tween() const -{ - return direction; -} - -inline const tween& spot_light::get_attenuation_tween() const -{ - return attenuation; -} - -inline const tween& spot_light::get_cutoff_tween() const -{ - return cutoff; -} - -inline const tween& spot_light::get_cosine_cutoff_tween() const -{ - return cosine_cutoff; -} - } // namespace scene #endif // ANTKEEPER_SCENE_SPOT_LIGHT_HPP diff --git a/src/engine/scene/model-instance.cpp b/src/engine/scene/static-mesh.cpp similarity index 50% rename from src/engine/scene/model-instance.cpp rename to src/engine/scene/static-mesh.cpp index d69af35..7d84dc8 100644 --- a/src/engine/scene/model-instance.cpp +++ b/src/engine/scene/static-mesh.cpp @@ -17,54 +17,69 @@ * along with Antkeeper source code. If not, see . */ -#include +#include #include #include namespace scene { -model_instance::model_instance(std::shared_ptr model) +static_mesh::static_mesh(std::shared_ptr model) { set_model(model); } -void model_instance::set_model(std::shared_ptr model) +void static_mesh::set_model(std::shared_ptr model) { this->model = model; if (model) { - materials.resize(model->get_groups().size()); - reset_materials(); + operations.resize(model->get_groups().size()); + for (std::size_t i = 0; i < operations.size(); ++i) + { + const auto& group = model->get_groups()[i]; + + auto& operation = operations[i]; + operation.vertex_array = model->get_vertex_array().get(); + operation.drawing_mode = group.drawing_mode; + operation.start_index = group.start_index; + operation.index_count = group.index_count; + operation.material = group.material; + } pose = model->get_skeleton().bind_pose; ::concatenate(pose, pose); } else { + operations.clear(); pose.clear(); } update_bounds(); } -void model_instance::set_material(std::size_t group_index, std::shared_ptr material) -{ - materials[group_index] = material; -} - -void model_instance::set_instanced(bool instanced, std::size_t instance_count) +void static_mesh::set_material(std::size_t index, std::shared_ptr material) { - this->instanced = instanced; - this->instance_count = (instanced) ? instance_count : 0; + if (material) + { + operations[index].material = material; + } + else + { + operations[index].material = model->get_groups()[index].material; + } } -void model_instance::reset_materials() +void static_mesh::reset_materials() { - std::fill(materials.begin(), materials.end(), nullptr); + for (std::size_t i = 0; i < operations.size(); ++i) + { + operations[i].material = model->get_groups()[i].material; + } } -void model_instance::update_bounds() +void static_mesh::update_bounds() { if (model) { @@ -78,22 +93,25 @@ void model_instance::update_bounds() } } -void model_instance::transformed() +void static_mesh::transformed() { world_bounds = aabb_type::transform(local_bounds, get_transform()); } -void model_instance::update_tweens() +void static_mesh::update_tweens() { object_base::update_tweens(); +} + +void static_mesh::render(render::context& ctx) const +{ + const float4x4 transform = math::matrix_cast(get_transform_tween().interpolate(ctx.alpha)); - // Update material override tweens - for (auto& material: materials) + for (auto& operation: operations) { - if (material) - { - //material->update_tweens(); - } + operation.transform = transform; + operation.depth = ctx.clip_near.signed_distance(float3(operation.transform[3])); + ctx.operations.push_back(&operation); } } diff --git a/src/engine/scene/model-instance.hpp b/src/engine/scene/static-mesh.hpp similarity index 66% rename from src/engine/scene/model-instance.hpp rename to src/engine/scene/static-mesh.hpp index 668eb5c..fe5c5a9 100644 --- a/src/engine/scene/model-instance.hpp +++ b/src/engine/scene/static-mesh.hpp @@ -17,48 +17,50 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_SCENE_MODEL_INSTANCE_HPP -#define ANTKEEPER_SCENE_MODEL_INSTANCE_HPP +#ifndef ANTKEEPER_SCENE_STATIC_MESH_HPP +#define ANTKEEPER_SCENE_STATIC_MESH_HPP #include #include #include #include +#include #include namespace scene { -class model_instance: public object +/** + * + */ +class static_mesh: public object { public: typedef geom::aabb aabb_type; /** - * Constructs a model instance and sets its model. + * Constructs a static mesh from a model. * - * @param model Model with which to associate the model instance. + * @param model Model from which the static mesh will be constructed. */ - explicit model_instance(std::shared_ptr model); + explicit static_mesh(std::shared_ptr model); /** * Constructs a model instance. */ - model_instance() = default; - + static_mesh() = default; + /** * Sets the model with which this model instance is associated. This will reset the pose and all overwritten materials. */ void set_model(std::shared_ptr model); - + /** * Overwrites the material of a model group for this model instance. * - * @param group_index Index of a model group. + * @param index Index of a model group. * @param material Pointer to the material which should overwrite the model group's material. A value of `nullptr` indicates the material will not be overwritten. */ - void set_material(std::size_t group_index, std::shared_ptr material); - - void set_instanced(bool instanced, std::size_t instance_count = 1); + void set_material(std::size_t index, std::shared_ptr material); /** * Resets all overwritten materials. @@ -103,46 +105,23 @@ public: } /// @} - /** - * Returns the materials of this model instance. - */ - [[nodiscard]] inline const std::vector>& get_materials() const noexcept - { - return materials; - } - - /** - * Returns `true` if the model instance is instanced, `false` otherwise. - */ - [[nodiscard]] inline bool is_instanced() const noexcept - { - return instanced; - } + void render(render::context& ctx) const override; - /** - * Returns the number of instances, if the model is instanced. - */ - [[nodiscard]] inline std::size_t get_instance_count() const noexcept - { - return instance_count; - } - - virtual void update_tweens(); + void update_tweens() override; void update_bounds(); private: - virtual void transformed(); + void transformed() override; std::shared_ptr model; + mutable std::vector operations; ::pose pose; - std::vector> materials; + aabb_type local_bounds{{0, 0, 0}, {0, 0, 0}}; aabb_type world_bounds{{0, 0, 0}, {0, 0, 0}}; - bool instanced{false}; - std::size_t instance_count{0}; }; } // namespace scene -#endif // ANTKEEPER_SCENE_MODEL_INSTANCE_HPP +#endif // ANTKEEPER_SCENE_STATIC_MESH_HPP diff --git a/src/engine/scene/text.cpp b/src/engine/scene/text.cpp index 0399bf4..2794ab3 100644 --- a/src/engine/scene/text.cpp +++ b/src/engine/scene/text.cpp @@ -27,7 +27,6 @@ namespace scene { text::text(): local_bounds{{0, 0, 0}, {0, 0, 0}}, world_bounds{{0, 0, 0}, {0, 0, 0}}, - material(nullptr), font(nullptr), direction(type::text_direction::ltr), content_u8(std::string()), @@ -81,26 +80,18 @@ text::text(): vao->bind(render::vertex_attribute::color, color_attribute); // Init render operation - render_op.material = nullptr; - render_op.bone_count = 0; - render_op.skinning_palette = nullptr; render_op.vertex_array = vao.get(); render_op.drawing_mode = gl::drawing_mode::triangles; - render_op.start_index = 0; - render_op.index_count = 0; - render_op.instance_count = 0; } -void text::render(const render::context& ctx, render::queue& queue) const +void text::render(render::context& ctx) const { - if (!vertex_count) + if (vertex_count) { - return; + render_op.transform = math::matrix_cast(get_transform_tween().interpolate(ctx.alpha)); + render_op.depth = ctx.clip_near.signed_distance(math::vector(render_op.transform[3])); + ctx.operations.push_back(&render_op); } - - render_op.transform = math::matrix_cast(get_transform_tween().interpolate(ctx.alpha)); - render_op.depth = ctx.clip_near.signed_distance(math::vector(render_op.transform[3])); - queue.push_back(render_op); } void text::refresh() @@ -110,8 +101,7 @@ void text::refresh() void text::set_material(std::shared_ptr material) { - this->material = material; - render_op.material = material.get(); + render_op.material = material; } void text::set_font(const type::bitmap_font* font) @@ -168,10 +158,6 @@ void text::transformed() void text::update_tweens() { object_base::update_tweens(); - if (material) - { - //material->update_tweens(); - } } void text::update_content() diff --git a/src/engine/scene/text.hpp b/src/engine/scene/text.hpp index 5c24de1..43993f2 100644 --- a/src/engine/scene/text.hpp +++ b/src/engine/scene/text.hpp @@ -42,8 +42,7 @@ public: /// Constructs a text object. text(); - /// @copydoc scene::object_base::render(const render::context&, render::queue&) const - virtual void render(const render::context& ctx, render::queue& queue) const; + void render(render::context& ctx) const override; /** * Manually updates the text object if its font has been updated or altered in any way. @@ -88,7 +87,7 @@ public: void set_color(const float4& color); /// Returns the text material. - const std::shared_ptr& get_material() const; + std::shared_ptr get_material() const; /// Returns the text font. const type::bitmap_font* get_font() const; @@ -120,7 +119,6 @@ private: mutable render::operation render_op; aabb_type local_bounds; aabb_type world_bounds; - std::shared_ptr material; const type::bitmap_font* font; type::text_direction direction; std::string content_u8; @@ -133,9 +131,9 @@ private: std::unique_ptr vbo; }; -inline const std::shared_ptr& text::get_material() const +inline std::shared_ptr text::get_material() const { - return material; + return render_op.material; } inline const type::bitmap_font* text::get_font() const diff --git a/src/game/ant/ant-cladogenesis.cpp b/src/game/ant/ant-cladogenesis.cpp index 1a6970e..a0b87db 100644 --- a/src/game/ant/ant-cladogenesis.cpp +++ b/src/game/ant/ant-cladogenesis.cpp @@ -19,34 +19,4 @@ #include "game/ant/ant-cladogenesis.hpp" -std::unique_ptr ant_cladogenesis(const ant_gene_pool& pool, std::random_device& rng) -{ - // Allocate genome - std::unique_ptr genome = std::make_unique(); - - // Randomly sample genes - genome->antennae = pool.antennae.sample(rng); - genome->body_size = pool.body_size.sample(rng); - genome->cocoon = pool.cocoon.sample(rng); - genome->diet = pool.diet.sample(rng); - genome->egg = pool.egg.sample(rng); - genome->eyes = pool.eyes.sample(rng); - genome->foraging_time = pool.foraging_time.sample(rng); - genome->founding_mode = pool.founding_mode.sample(rng); - genome->gaster = pool.gaster.sample(rng); - genome->head = pool.head.sample(rng); - genome->larva = pool.larva.sample(rng); - genome->legs = pool.legs.sample(rng); - genome->mandibles = pool.mandibles.sample(rng); - genome->mesosoma = pool.mesosoma.sample(rng); - genome->nest_site = pool.nest_site.sample(rng); - genome->ocelli = pool.ocelli.sample(rng); - genome->pigmentation = pool.pigmentation.sample(rng); - genome->pilosity = pool.pilosity.sample(rng); - genome->sculpturing = pool.sculpturing.sample(rng); - genome->sting = pool.sting.sample(rng); - genome->waist = pool.waist.sample(rng); - genome->wings = pool.wings.sample(rng); - - return genome; -} + diff --git a/src/game/ant/ant-cladogenesis.hpp b/src/game/ant/ant-cladogenesis.hpp index b3b9cd1..bd93d5d 100644 --- a/src/game/ant/ant-cladogenesis.hpp +++ b/src/game/ant/ant-cladogenesis.hpp @@ -26,13 +26,46 @@ #include /** - * Generates a genome from a gene pool. + * Generates an ant genome from a gene pool. * - * @param pool Gene pool. - * @param rng Random number generator. + * @tparam URBG Uniform random bit generator type. + * + * @param pool Ant gene pool. + * @param urbg Uniform random bit generator object. * * @return New genome. */ -[[nodiscard]] std::unique_ptr ant_cladogenesis(const ant_gene_pool& pool, std::random_device& rng); +template +[[nodiscard]] std::unique_ptr ant_cladogenesis(const ant_gene_pool& pool, URBG& urbg) +{ + // Allocate genome + std::unique_ptr genome = std::make_unique(); + + // Randomly sample genes + genome->antennae = pool.antennae.sample(urbg); + genome->body_size = pool.body_size.sample(urbg); + genome->cocoon = pool.cocoon.sample(urbg); + genome->diet = pool.diet.sample(urbg); + genome->egg = pool.egg.sample(urbg); + genome->eyes = pool.eyes.sample(urbg); + genome->foraging_time = pool.foraging_time.sample(urbg); + genome->founding_mode = pool.founding_mode.sample(urbg); + genome->gaster = pool.gaster.sample(urbg); + genome->head = pool.head.sample(urbg); + genome->larva = pool.larva.sample(urbg); + genome->legs = pool.legs.sample(urbg); + genome->mandibles = pool.mandibles.sample(urbg); + genome->mesosoma = pool.mesosoma.sample(urbg); + genome->nest_site = pool.nest_site.sample(urbg); + genome->ocelli = pool.ocelli.sample(urbg); + genome->pigmentation = pool.pigmentation.sample(urbg); + genome->pilosity = pool.pilosity.sample(urbg); + genome->sculpturing = pool.sculpturing.sample(urbg); + genome->sting = pool.sting.sample(urbg); + genome->waist = pool.waist.sample(urbg); + genome->wings = pool.wings.sample(urbg); + + return genome; +} #endif // ANTKEEPER_GAME_ANT_CLADOGENESIS_HPP diff --git a/src/game/ant/ant-gene-frequency-table.hpp b/src/game/ant/ant-gene-frequency-table.hpp index 76acab9..eff997d 100644 --- a/src/game/ant/ant-gene-frequency-table.hpp +++ b/src/game/ant/ant-gene-frequency-table.hpp @@ -41,14 +41,14 @@ struct ant_gene_frequency_table /** * Samples a gene from the frequency table. * - * @tparam Generator Uniform random bit generator type. + * @tparam URBG Uniform random bit generator type. * - * @param g Uniform random bit generator object. + * @param urbg Uniform random bit generator object. * * @return Randomly sampled gene. */ - template - [[nodiscard]] std::shared_ptr sample(Generator& g) const + template + [[nodiscard]] std::shared_ptr sample(URBG& urbg) const { if (genes.empty()) { @@ -57,7 +57,7 @@ struct ant_gene_frequency_table std::discrete_distribution distribution(weights.begin(), weights.end()); - return genes[distribution(g)]; + return genes[distribution(urbg)]; } }; diff --git a/src/game/ant/ant-swarm.cpp b/src/game/ant/ant-swarm.cpp index 219658a..186a415 100644 --- a/src/game/ant/ant-swarm.cpp +++ b/src/game/ant/ant-swarm.cpp @@ -20,7 +20,7 @@ #include "game/ant/ant-swarm.hpp" #include "game/components/transform-component.hpp" #include "game/components/steering-component.hpp" -#include "game/components/model-component.hpp" +#include "game/components/scene-component.hpp" #include "game/components/picking-component.hpp" #include "game/components/ant-caste-component.hpp" #include @@ -35,16 +35,16 @@ * * @see https://math.stackexchange.com/questions/87230/picking-random-points-in-the-volume-of-sphere-with-uniform-probability/87238#87238 */ -template -static math::vector3 sphere_random(Generator& rng) +template +static math::vector3 sphere_random(URBG& urbg) { std::uniform_real_distribution distribution(T{-1}, T{1}); math::vector3 position; for (std::size_t i = 0; i < 3; ++i) - position[i] = distribution(rng); + position[i] = distribution(urbg); - return math::normalize(position) * std::cbrt(distribution(rng)); + return math::normalize(position) * std::cbrt(distribution(urbg)); } entity::id create_ant_swarm(::game& ctx) @@ -78,17 +78,11 @@ entity::id create_ant_swarm(::game& ctx) transform.warp = true; ctx.entity_registry->emplace<::transform_component>(swarm_eid, transform); - // Init male model component - ::model_component male_model; - male_model.render_model = ctx.resource_manager->load("male-boid.mdl"); - male_model.instance_count = 0; - male_model.layers = 1; + // Load male model + std::shared_ptr male_model = ctx.resource_manager->load("male-boid.mdl"); - // Init queen model component - ::model_component queen_model; - queen_model.render_model = ctx.resource_manager->load("queen-boid.mdl"); - queen_model.instance_count = 0; - queen_model.layers = 1; + // Load queen model + std::shared_ptr queen_model = ctx.resource_manager->load("queen-boid.mdl"); // Init steering component ::steering_component steering; @@ -120,15 +114,11 @@ entity::id create_ant_swarm(::game& ctx) ant_caste_component male_caste; male_caste.caste_type = ant_caste_type::male; - // Construct and seed random number generator - std::random_device seed; - std::mt19937 rng(seed()); - // Create alates for (std::size_t i = 0; i < alate_count; ++i) { // Generate random position in swarm sphere - steering.agent.position = swarm_center + sphere_random(rng) * swarm_radius; + steering.agent.position = swarm_center + sphere_random(ctx.rng) * swarm_radius; transform.local.translation = steering.agent.position; entity::id alate_eid = ctx.entity_registry->create(); @@ -138,7 +128,7 @@ entity::id create_ant_swarm(::game& ctx) { // Create male ctx.entity_registry->emplace(alate_eid, male_caste); - ctx.entity_registry->emplace<::model_component>(alate_eid, male_model); + ctx.entity_registry->emplace<::scene_component>(alate_eid, std::make_unique(male_model), std::uint8_t{1}); transform.local.scale = male_scale; transform.world = transform.local; @@ -151,7 +141,7 @@ entity::id create_ant_swarm(::game& ctx) { // Create queen ctx.entity_registry->emplace(alate_eid, queen_caste); - ctx.entity_registry->emplace<::model_component>(alate_eid, queen_model); + ctx.entity_registry->emplace<::scene_component>(alate_eid, std::make_unique(queen_model), std::uint8_t{1}); transform.local.scale = queen_scale; transform.world = transform.local; diff --git a/src/game/commands/commands.cpp b/src/game/commands/commands.cpp index cb585fd..bc33e9a 100644 --- a/src/game/commands/commands.cpp +++ b/src/game/commands/commands.cpp @@ -18,13 +18,13 @@ */ #include "game/commands/commands.hpp" -#include "game/components/model-component.hpp" +#include "game/components/scene-component.hpp" #include "game/components/transform-component.hpp" #include "game/components/celestial-body-component.hpp" #include "game/components/terrain-component.hpp" #include #include - + namespace command { void translate(entity::registry& registry, entity::id eid, const float3& translation) @@ -130,17 +130,17 @@ void place(entity::registry& registry, entity::id eid, entity::id celestial_body } -void assign_render_layers(entity::registry& registry, entity::id eid, unsigned int layers) +void assign_render_layers(entity::registry& registry, entity::id eid, std::uint8_t layer_mask) { - const ::model_component* model = registry.try_get<::model_component>(eid); - if (model) + const ::scene_component* component = registry.try_get<::scene_component>(eid); + if (component) { - registry.patch<::model_component> + registry.patch<::scene_component> ( eid, - [layers](auto& model) + [layer_mask](auto& component) { - model.layers = layers; + component.layer_mask = layer_mask; } ); } diff --git a/src/game/components/light-component.hpp b/src/game/components/legged-locomotion-component.hpp similarity index 69% rename from src/game/components/light-component.hpp rename to src/game/components/legged-locomotion-component.hpp index 62b20b0..f3e4e2e 100644 --- a/src/game/components/light-component.hpp +++ b/src/game/components/legged-locomotion-component.hpp @@ -17,22 +17,18 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_GAME_LIGHT_COMPONENT_HPP -#define ANTKEEPER_GAME_LIGHT_COMPONENT_HPP +#ifndef ANTKEEPER_GAME_LEGGED_LOCOMOTION_COMPONENT_HPP +#define ANTKEEPER_GAME_LEGGED_LOCOMOTION_COMPONENT_HPP -#include -#include +#include - -struct light_component +/** + * Legged terrestrial locomotion. + */ +struct legged_locomotion_component { - scene::light_type type; - float3 color; - float intensity; - float3 attenuation; - float2 cutoff; + /// Force vector. + math::vector force{0.0f, 0.0f, 0.0f}; }; - -#endif // ANTKEEPER_GAME_LIGHT_COMPONENT_HPP - +#endif // ANTKEEPER_GAME_LEGGED_LOCOMOTION_COMPONENT_HPP diff --git a/src/game/components/model-component.hpp b/src/game/components/model-component.hpp deleted file mode 100644 index 5c36991..0000000 --- a/src/game/components/model-component.hpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_MODEL_COMPONENT_HPP -#define ANTKEEPER_GAME_MODEL_COMPONENT_HPP - -#include -#include -#include - -struct model_component -{ - std::shared_ptr render_model; - std::unordered_map> materials; - int instance_count; - unsigned int layers; -}; - - -#endif // ANTKEEPER_GAME_MODEL_COMPONENT_HPP diff --git a/src/game/components/physics-component.hpp b/src/game/components/physics-component.hpp new file mode 100644 index 0000000..61039da --- /dev/null +++ b/src/game/components/physics-component.hpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_PHYSICS_COMPONENT_HPP +#define ANTKEEPER_GAME_PHYSICS_COMPONENT_HPP + +#include + +struct physics_component +{ + /// Mass, in kg. + float mass; + + /// Inverse mass, in kg^-1. + //float inverse_mass{0.0f}; + + /// Force vector, in newtons. + math::vector force{0.0f, 0.0f, 0.0f}; + + /// Acceleration vector, in m/s^2. + math::vector acceleration{0.0f, 0.0f, 0.0f}; + + /// Velocity vector, in m/s. + math::vector velocity{0.0f, 0.0f, 0.0f}; +}; + +#endif // ANTKEEPER_GAME_PHYSICS_COMPONENT_HPP diff --git a/src/game/components/portal-component.hpp b/src/game/components/scene-component.hpp similarity index 71% rename from src/game/components/portal-component.hpp rename to src/game/components/scene-component.hpp index 8192e1a..8c4b396 100644 --- a/src/game/components/portal-component.hpp +++ b/src/game/components/scene-component.hpp @@ -17,14 +17,17 @@ * along with Antkeeper source code. If not, see . */ -#ifndef ANTKEEPER_GAME_PORTAL_COMPONENT_HPP -#define ANTKEEPER_GAME_PORTAL_COMPONENT_HPP +#ifndef ANTKEEPER_GAME_SCENE_COMPONENT_HPP +#define ANTKEEPER_GAME_SCENE_COMPONENT_HPP +#include +#include +#include -struct portal_component +struct scene_component { - + std::unique_ptr object; + std::uint8_t layer_mask{0b11111111}; }; - -#endif // ANTKEEPER_GAME_PORTAL_COMPONENT_HPP +#endif // ANTKEEPER_GAME_SCENE_COMPONENT_HPP diff --git a/src/game/controls.cpp b/src/game/controls.cpp index 2d67aeb..9053f10 100644 --- a/src/game/controls.cpp +++ b/src/game/controls.cpp @@ -130,6 +130,9 @@ void reset_control_profile(::control_profile& profile) // Mouse look mappings.emplace("mouse_look", std::make_unique(nullptr, input::mouse_button::right)); + + // Focus + mappings.emplace("focus", std::make_unique(nullptr, input::scancode::left_shift, 0, false)); } void apply_control_profile(::game& ctx, const ::control_profile& profile) @@ -172,6 +175,7 @@ void apply_control_profile(::game& ctx, const ::control_profile& profile) ctx.keeper_action_map.remove_mappings(); add_mappings(ctx.keeper_action_map, ctx.mouse_pick_action, "mouse_pick"); add_mappings(ctx.keeper_action_map, ctx.mouse_look_action, "mouse_look"); + add_mappings(ctx.keeper_action_map, ctx.focus_action, "focus"); } void update_control_profile(::game& ctx, ::control_profile& profile) @@ -238,6 +242,7 @@ void update_control_profile(::game& ctx, ::control_profile& profile) // Keeper controls add_mappings(ctx.keeper_action_map, ctx.mouse_pick_action, "mouse_pick"); add_mappings(ctx.keeper_action_map, ctx.mouse_look_action, "mouse_look"); + add_mappings(ctx.keeper_action_map, ctx.focus_action, "focus"); } void setup_window_controls(::game& ctx) @@ -548,5 +553,6 @@ void disable_keeper_controls(::game& ctx) ctx.mouse_pick_action.reset(); ctx.mouse_look_action.reset(); + ctx.focus_action.reset(); } diff --git a/src/game/game.cpp b/src/game/game.cpp index 16ea0c7..59d2fbf 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -41,6 +41,7 @@ #include "game/systems/spatial-system.hpp" #include "game/systems/spring-system.hpp" #include "game/systems/steering-system.hpp" +#include "game/systems/physics-system.hpp" #include "game/systems/subterrain-system.hpp" #include "game/systems/terrain-system.hpp" #include @@ -119,6 +120,7 @@ game::game(int argc, const char* const* argv) setup_scenes(); setup_animation(); setup_ui(); + setup_rng(); setup_entities(); setup_systems(); setup_controls(); @@ -578,7 +580,7 @@ void game::setup_input() if (gamepad_active) { gamepad_active = false; - input_manager->show_cursor(); + input_manager->set_cursor_visible(true); } }; @@ -590,7 +592,7 @@ void game::setup_input() if (!gamepad_active && std::abs(event.position) > 0.5f) { gamepad_active = true; - input_manager->hide_cursor(); + input_manager->set_cursor_visible(false); } } ); @@ -601,7 +603,7 @@ void game::setup_input() if (!gamepad_active) { gamepad_active = true; - input_manager->hide_cursor(); + input_manager->set_cursor_visible(false); } } ); @@ -624,7 +626,7 @@ void game::setup_input() if (!input_manager->get_gamepads().empty()) { gamepad_active = true; - input_manager->hide_cursor(); + input_manager->set_cursor_visible(false); } else { @@ -793,63 +795,8 @@ void game::setup_rendering() surface_compositor->add_pass(resample_pass.get()); } - // Create billboard VAO - { - const float billboard_vertex_data[] = - { - -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, - -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, - 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, - 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, - -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, - 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f - }; - - std::size_t billboard_vertex_size = 8; - std::size_t billboard_vertex_stride = sizeof(float) * billboard_vertex_size; - std::size_t billboard_vertex_count = 6; - - billboard_vbo = std::make_unique(gl::buffer_usage::static_draw, sizeof(float) * billboard_vertex_size * billboard_vertex_count, std::as_bytes(std::span{billboard_vertex_data})); - billboard_vao = std::make_unique(); - - std::size_t attribute_offset = 0; - - // Define position vertex attribute - gl::vertex_attribute position_attribute; - position_attribute.buffer = billboard_vbo.get(); - position_attribute.offset = attribute_offset; - position_attribute.stride = billboard_vertex_stride; - position_attribute.type = gl::vertex_attribute_type::float_32; - position_attribute.components = 3; - attribute_offset += position_attribute.components * sizeof(float); - - // Define UV vertex attribute - gl::vertex_attribute uv_attribute; - uv_attribute.buffer = billboard_vbo.get(); - uv_attribute.offset = attribute_offset; - uv_attribute.stride = billboard_vertex_stride; - uv_attribute.type = gl::vertex_attribute_type::float_32; - uv_attribute.components = 2; - attribute_offset += uv_attribute.components * sizeof(float); - - // Define barycentric vertex attribute - gl::vertex_attribute barycentric_attribute; - barycentric_attribute.buffer = billboard_vbo.get(); - barycentric_attribute.offset = attribute_offset; - barycentric_attribute.stride = billboard_vertex_stride; - barycentric_attribute.type = gl::vertex_attribute_type::float_32; - barycentric_attribute.components = 3; - //attribute_offset += barycentric_attribute.components * sizeof(float); - - // Bind vertex attributes to VAO - billboard_vao->bind(render::vertex_attribute::position, position_attribute); - billboard_vao->bind(render::vertex_attribute::uv, uv_attribute); - billboard_vao->bind(render::vertex_attribute::barycentric, barycentric_attribute); - } - // Create renderer renderer = std::make_unique(); - renderer->set_billboard_vao(billboard_vao.get()); debug::log::trace("Set up rendering"); } @@ -1089,6 +1036,12 @@ void game::setup_ui() ); } +void game::setup_rng() +{ + std::random_device rd; + rng.seed(rd()); +} + void game::setup_entities() { // Create entity registry @@ -1120,11 +1073,14 @@ void game::setup_systems() // Setup behavior system behavior_system = std::make_unique<::behavior_system>(*entity_registry); + // Setup steering system + steering_system = std::make_unique<::steering_system>(*entity_registry); + // Setup locomotion system locomotion_system = std::make_unique<::locomotion_system>(*entity_registry); - // Setup steering system - steering_system = std::make_unique<::steering_system>(*entity_registry); + // Setup physics system + physics_system = std::make_unique<::physics_system>(*entity_registry); // Setup spring system spring_system = std::make_unique<::spring_system>(*entity_registry); @@ -1287,6 +1243,7 @@ void game::setup_loop() behavior_system->update(time, timestep); steering_system->update(time, timestep); locomotion_system->update(time, timestep); + physics_system->update(time, timestep); camera_system->update(time, timestep); orbit_system->update(time, timestep); blackbody_system->update(time, timestep); diff --git a/src/game/game.hpp b/src/game/game.hpp index 2c10167..dd84426 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -35,8 +35,6 @@ #include #include #include -#include -#include #include #include #include @@ -58,6 +56,7 @@ #include #include #include +#include #include // Forward declarations @@ -106,6 +105,7 @@ class render_system; class spatial_system; class spring_system; class steering_system; +class physics_system; class subterrain_system; class terrain_system; @@ -215,6 +215,7 @@ public: input::action pause_action; input::action mouse_pick_action; input::action mouse_look_action; + input::action focus_action; std::vector> window_action_subscriptions; std::vector> menu_action_subscriptions; @@ -261,8 +262,6 @@ public: int2 render_resolution; float render_scale; int shadow_map_resolution; - std::unique_ptr billboard_vbo; - std::unique_ptr billboard_vao; std::unique_ptr ui_clear_pass; std::unique_ptr ui_material_pass; std::unique_ptr ui_compositor; @@ -343,6 +342,9 @@ public: bool captions; float captions_size; + // Random number generation + std::mt19937 rng; + // Entities std::unique_ptr entity_registry; std::unordered_map entities; @@ -352,8 +354,9 @@ public: std::unique_ptr<::camera_system> camera_system; std::unique_ptr<::collision_system> collision_system; std::unique_ptr<::constraint_system> constraint_system; - std::unique_ptr<::locomotion_system> locomotion_system; std::unique_ptr<::steering_system> steering_system; + std::unique_ptr<::locomotion_system> locomotion_system; + std::unique_ptr<::physics_system> physics_system; std::unique_ptr<::render_system> render_system; std::unique_ptr<::subterrain_system> subterrain_system; std::unique_ptr<::terrain_system> terrain_system; @@ -364,6 +367,8 @@ public: std::unique_ptr<::astronomy_system> astronomy_system; std::unique_ptr<::orbit_system> orbit_system; + + double3 rgb_wavelengths; std::shared_ptr active_ecoregion; @@ -382,6 +387,7 @@ private: void setup_scenes(); void setup_animation(); void setup_ui(); + void setup_rng(); void setup_entities(); void setup_systems(); void setup_controls(); diff --git a/src/game/loaders/entity-archetype-loader.cpp b/src/game/loaders/entity-archetype-loader.cpp index 8486a63..a8eb6a2 100644 --- a/src/game/loaders/entity-archetype-loader.cpp +++ b/src/game/loaders/entity-archetype-loader.cpp @@ -27,13 +27,14 @@ #include "game/components/diffuse-reflector-component.hpp" #include "game/components/terrain-component.hpp" #include "game/components/transform-component.hpp" -#include "game/components/model-component.hpp" +#include "game/components/scene-component.hpp" #include "game/components/orbit-component.hpp" #include "game/components/blackbody-component.hpp" #include "game/components/celestial-body-component.hpp" #include #include #include +#include #include static bool load_component_atmosphere(entity::archetype& archetype, const json& element) @@ -191,21 +192,21 @@ static bool load_component_diffuse_reflector(entity::archetype& archetype, const static bool load_component_model(entity::archetype& archetype, resource_manager& resource_manager, const json& element) { - ::model_component component; - component.instance_count = 0; - //component.layers = ~0; - component.layers = 1; - + std::shared_ptr model; if (element.contains("file")) { - component.render_model = resource_manager.load(element["file"].get()); + model = resource_manager.load(element["file"].get()); } archetype.stamps.push_back ( - [component](entt::handle& handle) + [model](entt::handle& handle) { - handle.emplace_or_replace(component); + handle.emplace_or_replace + ( + std::make_unique(model), + std::uint8_t{0b00000001} + ); } ); diff --git a/src/game/platform/windows/nvidia.cpp b/src/game/platform/windows/nvidia.cpp index 900b3a5..f5a008d 100644 --- a/src/game/platform/windows/nvidia.cpp +++ b/src/game/platform/windows/nvidia.cpp @@ -23,5 +23,5 @@ extern "C" { // Direct Nvidia Optimus to use high-performance graphics - _declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; + _declspec(dllexport) DWORD NvOptimusEnablement = 1; } diff --git a/src/game/spawn.cpp b/src/game/spawn.cpp index e056d91..4e69527 100644 --- a/src/game/spawn.cpp +++ b/src/game/spawn.cpp @@ -19,7 +19,7 @@ #include "game/spawn.hpp" #include "game/components/transform-component.hpp" -#include "game/components/model-component.hpp" +#include "game/components/scene-component.hpp" entity::id spawn_ant_egg(::game& ctx, const ant_genome& genome, bool fertilized, const float3& position) @@ -35,12 +35,8 @@ entity::id spawn_ant_egg(::game& ctx, const ant_genome& genome, bool fertilized, transform_component.warp = true; ctx.entity_registry->emplace<::transform_component>(egg_eid, transform_component); - // Construct model component - model_component model_component; - model_component.render_model = genome.egg->phenes.front().model; - model_component.instance_count = 0; - model_component.layers = ~0; - ctx.entity_registry->emplace<::model_component>(egg_eid, model_component); + // Construct scene component + ctx.entity_registry->emplace<::scene_component>(egg_eid, std::make_unique(genome.egg->phenes.front().model), std::uint8_t{1}); return egg_eid; } @@ -58,12 +54,8 @@ entity::id spawn_ant_larva(::game& ctx, const ant_genome& genome, const float3& transform_component.warp = true; ctx.entity_registry->emplace<::transform_component>(larva_eid, transform_component); - // Construct model component - model_component model_component; - model_component.render_model = genome.larva->phenes.front().model; - model_component.instance_count = 0; - model_component.layers = ~0; - ctx.entity_registry->emplace<::model_component>(larva_eid, model_component); + // Construct scene component + ctx.entity_registry->emplace<::scene_component>(larva_eid, std::make_unique(genome.larva->phenes.front().model), std::uint8_t{1}); return larva_eid; } diff --git a/src/game/states/main-menu-state.cpp b/src/game/states/main-menu-state.cpp index e499808..fc6c243 100644 --- a/src/game/states/main-menu-state.cpp +++ b/src/game/states/main-menu-state.cpp @@ -23,7 +23,6 @@ #include #include #include -#include "game/components/model-component.hpp" #include "game/components/steering-component.hpp" #include "game/components/transform-component.hpp" #include "game/controls.hpp" diff --git a/src/game/states/nest-selection-state.cpp b/src/game/states/nest-selection-state.cpp index a4b364e..135be64 100644 --- a/src/game/states/nest-selection-state.cpp +++ b/src/game/states/nest-selection-state.cpp @@ -24,12 +24,13 @@ #include "game/commands/commands.hpp" #include "game/components/camera-component.hpp" #include "game/components/constraint-stack-component.hpp" -#include "game/components/locomotion-component.hpp" -#include "game/components/model-component.hpp" +#include "game/components/scene-component.hpp" #include "game/components/picking-component.hpp" #include "game/components/spring-component.hpp" +#include "game/components/physics-component.hpp" #include "game/components/steering-component.hpp" #include "game/components/terrain-component.hpp" +#include "game/components/legged-locomotion-component.hpp" #include "game/components/transform-component.hpp" #include "game/constraints/child-of-constraint.hpp" #include "game/constraints/copy-rotation-constraint.hpp" @@ -84,8 +85,7 @@ nest_selection_state::nest_selection_state(::game& ctx): ::world::enter_ecoregion(ctx, *ctx.active_ecoregion); debug::log::trace("Generating genome..."); - std::random_device rng; - std::unique_ptr genome = ant_cladogenesis(ctx.active_ecoregion->gene_pools[0], rng); + std::unique_ptr genome = ant_cladogenesis(ctx.active_ecoregion->gene_pools[0], ctx.rng); debug::log::trace("Generated genome"); debug::log::trace("Building worker phenome..."); @@ -97,19 +97,15 @@ nest_selection_state::nest_selection_state(::game& ctx): debug::log::trace("Generated worker model"); // Create worker entity(s) - entity::id worker_eid = ctx.entity_registry->create(); + worker_ant_eid = ctx.entity_registry->create(); transform_component worker_transform_component; worker_transform_component.local = math::transform::identity; - worker_transform_component.local.translation = {0, 0, -4}; + worker_transform_component.local.translation = {0, 0.5f, -4}; worker_transform_component.world = worker_transform_component.local; worker_transform_component.warp = true; - ctx.entity_registry->emplace(worker_eid, worker_transform_component); + ctx.entity_registry->emplace(worker_ant_eid, worker_transform_component); - model_component worker_model_component; - worker_model_component.render_model = worker_model; - worker_model_component.instance_count = 0; - worker_model_component.layers = ~0; - ctx.entity_registry->emplace(worker_eid, worker_model_component); + ctx.entity_registry->emplace(worker_ant_eid, std::make_unique(worker_model), std::uint8_t{1}); // Disable UI color clear ctx.ui_clear_pass->set_cleared_buffers(false, true, false); @@ -164,9 +160,13 @@ nest_selection_state::nest_selection_state(::game& ctx): // color_checker_archetype->create(*ctx.entity_registry); // auto ruler_archetype = ctx.resource_manager->load("ruler-10cm.ent"); // ruler_archetype->create(*ctx.entity_registry); + + auto plane_archetype = ctx.resource_manager->load("desert-scrub-plane.ent"); + auto plane_eid = plane_archetype->create(*ctx.entity_registry); + auto yucca_archetype = ctx.resource_manager->load("yucca-plant-l.ent"); auto yucca_eid = yucca_archetype->create(*ctx.entity_registry); - ::command::warp_to(*ctx.entity_registry, yucca_eid, {0, 4, 30}); + ::command::warp_to(*ctx.entity_registry, yucca_eid, {0, 0, 30}); yucca_archetype = ctx.resource_manager->load("yucca-plant-m.ent"); yucca_eid = yucca_archetype->create(*ctx.entity_registry); @@ -174,7 +174,7 @@ nest_selection_state::nest_selection_state(::game& ctx): yucca_archetype = ctx.resource_manager->load("yucca-plant-s.ent"); yucca_eid = yucca_archetype->create(*ctx.entity_registry); - ::command::warp_to(*ctx.entity_registry, yucca_eid, {-300, 3, -300}); + ::command::warp_to(*ctx.entity_registry, yucca_eid, {-300, 0, -300}); auto cactus_plant_archetype = ctx.resource_manager->load("barrel-cactus-plant-l.ent"); auto cactus_plant_eid = cactus_plant_archetype->create(*ctx.entity_registry); @@ -182,15 +182,15 @@ nest_selection_state::nest_selection_state(::game& ctx): cactus_plant_archetype = ctx.resource_manager->load("barrel-cactus-plant-m.ent"); cactus_plant_eid = cactus_plant_archetype->create(*ctx.entity_registry); - ::command::warp_to(*ctx.entity_registry, cactus_plant_eid, {100, -2, -70}); + ::command::warp_to(*ctx.entity_registry, cactus_plant_eid, {100, 0, -70}); cactus_plant_archetype = ctx.resource_manager->load("barrel-cactus-plant-s.ent"); cactus_plant_eid = cactus_plant_archetype->create(*ctx.entity_registry); - ::command::warp_to(*ctx.entity_registry, cactus_plant_eid, {50, 2, 80}); + ::command::warp_to(*ctx.entity_registry, cactus_plant_eid, {50, 0, 80}); auto cactus_seed_archetype = ctx.resource_manager->load("barrel-cactus-seed.ent"); auto cactus_seed_eid = cactus_seed_archetype->create(*ctx.entity_registry); - ::command::warp_to(*ctx.entity_registry, cactus_seed_eid, {10, 5, 10}); + ::command::warp_to(*ctx.entity_registry, cactus_seed_eid, {10, 0, 10}); // Queue enable game controls ctx.function_queue.push @@ -223,7 +223,20 @@ nest_selection_state::~nest_selection_state() } void nest_selection_state::create_first_person_camera_rig() -{ +{ + // Construct first person camera rig track to constraint + track_to_constraint first_person_camera_rig_track_to; + first_person_camera_rig_track_to.target = worker_ant_eid; + first_person_camera_rig_track_to.up = {0.0f, 1.0f, 0.0f}; + + constraint_stack_node_component first_person_camera_rig_track_to_node; + first_person_camera_rig_track_to_node.active = false; + first_person_camera_rig_track_to_node.weight = 1.0f; + first_person_camera_rig_track_to_node.next = entt::null; + first_person_camera_rig_track_to_eid = ctx.entity_registry->create(); + ctx.entity_registry->emplace(first_person_camera_rig_track_to_eid, first_person_camera_rig_track_to); + ctx.entity_registry->emplace(first_person_camera_rig_track_to_eid, first_person_camera_rig_track_to_node); + // Construct first person camera rig spring rotation constraint spring_rotation_constraint first_person_camera_rig_spring_rotation; first_person_camera_rig_spring_rotation.spring = @@ -237,7 +250,7 @@ void nest_selection_state::create_first_person_camera_rig() constraint_stack_node_component first_person_camera_rig_spring_rotation_node; first_person_camera_rig_spring_rotation_node.active = true; first_person_camera_rig_spring_rotation_node.weight = 1.0f; - first_person_camera_rig_spring_rotation_node.next = entt::null; + first_person_camera_rig_spring_rotation_node.next = first_person_camera_rig_track_to_eid; first_person_camera_rig_spring_rotation_eid = ctx.entity_registry->create(); ctx.entity_registry->emplace(first_person_camera_rig_spring_rotation_eid, first_person_camera_rig_spring_rotation); ctx.entity_registry->emplace(first_person_camera_rig_spring_rotation_eid, first_person_camera_rig_spring_rotation_node); @@ -253,7 +266,7 @@ void nest_selection_state::create_first_person_camera_rig() first_person_camera_rig_translation_spring_angular_frequency }; constraint_stack_node_component first_person_camera_rig_spring_translation_node; - first_person_camera_rig_spring_translation_node.active = true; + first_person_camera_rig_spring_translation_node.active = false; first_person_camera_rig_spring_translation_node.weight = 1.0f; first_person_camera_rig_spring_translation_node.next = first_person_camera_rig_spring_rotation_eid; first_person_camera_rig_spring_translation_eid = ctx.entity_registry->create(); @@ -271,6 +284,13 @@ void nest_selection_state::create_first_person_camera_rig() first_person_camera_rig_transform.world = first_person_camera_rig_transform.local; first_person_camera_rig_transform.warp = true; + // Construct first person camera rig legged locomotion component + legged_locomotion_component first_person_camera_rig_locomotion; + + // Construct first person camera rig physics component + physics_component first_person_camera_rig_physics; + first_person_camera_rig_physics.mass = 90.0f; + // Construct first person camera rig camera component camera_component first_person_camera_rig_camera; first_person_camera_rig_camera.object = ctx.surface_camera.get(); @@ -279,6 +299,8 @@ void nest_selection_state::create_first_person_camera_rig() first_person_camera_rig_eid = ctx.entity_registry->create(); ctx.entity_registry->emplace(first_person_camera_rig_eid, first_person_camera_rig_camera); ctx.entity_registry->emplace(first_person_camera_rig_eid, first_person_camera_rig_transform); + ctx.entity_registry->emplace(first_person_camera_rig_eid, first_person_camera_rig_physics); + ctx.entity_registry->emplace(first_person_camera_rig_eid, first_person_camera_rig_locomotion); ctx.entity_registry->emplace(first_person_camera_rig_eid, first_person_camera_rig_constraint_stack); // Construct first person camera rig fov spring @@ -410,7 +432,8 @@ void nest_selection_state::setup_controls() mouse_look = true; } - //ctx.app->set_relative_mouse_mode(mouse_look); + //ctx.input_manager->set_cursor_visible(!mouse_look); + ctx.input_manager->set_relative_mouse_mode(mouse_look); } ) ); @@ -425,7 +448,8 @@ void nest_selection_state::setup_controls() if (!ctx.toggle_mouse_look && mouse_look) { mouse_look = false; - //ctx.app->set_relative_mouse_mode(false); + //ctx.input_manager->set_cursor_visible(true); + ctx.input_manager->set_relative_mouse_mode(false); } } ) @@ -457,14 +481,57 @@ void nest_selection_state::setup_controls() } ); + constexpr float movement_speed = 10000.0f; + + auto move_first_person_camera_rig = [&](const float2& direction, float speed) + { + const spring_rotation_constraint& first_person_camera_rig_spring_rotation = ctx.entity_registry->get(first_person_camera_rig_spring_rotation_eid); + + const math::quaternion yaw_rotation = math::angle_axis(first_person_camera_rig_spring_rotation.spring.x0[0], float3{0.0f, 1.0f, 0.0f}); + const float3 rotated_direction = yaw_rotation * float3{direction[0], 0.0f, direction[1]}; + + const float3 force = rotated_direction * speed; + + ctx.entity_registry->patch + ( + first_person_camera_rig_eid, + [&](auto& component) + { + component.force = force; + } + ); + }; + + auto stop_first_person_camera_rig = [&]() + { + ctx.entity_registry->patch + ( + first_person_camera_rig_eid, + [&](auto& component) + { + component.force = {0.0f, 0.0f, 0.0f}; + } + ); + }; + // Move forward action_subscriptions.emplace_back ( ctx.move_forward_action.get_active_channel().subscribe ( - [&](const auto& event) + [&, move_first_person_camera_rig](const auto& event) { - move_first_person_camera_rig({0, -1}, event.input_value); + move_first_person_camera_rig({0.0f, -1.0f}, movement_speed * event.input_value); + } + ) + ); + action_subscriptions.emplace_back + ( + ctx.move_forward_action.get_deactivated_channel().subscribe + ( + [&, stop_first_person_camera_rig](const auto& event) + { + stop_first_person_camera_rig(); } ) ); @@ -474,9 +541,19 @@ void nest_selection_state::setup_controls() ( ctx.move_back_action.get_active_channel().subscribe ( - [&](const auto& event) + [&, move_first_person_camera_rig](const auto& event) + { + move_first_person_camera_rig({0, 1}, movement_speed * event.input_value); + } + ) + ); + action_subscriptions.emplace_back + ( + ctx.move_back_action.get_deactivated_channel().subscribe + ( + [&, stop_first_person_camera_rig](const auto& event) { - move_first_person_camera_rig({0, 1}, event.input_value); + stop_first_person_camera_rig(); } ) ); @@ -486,9 +563,19 @@ void nest_selection_state::setup_controls() ( ctx.move_left_action.get_active_channel().subscribe ( - [&](const auto& event) + [&, move_first_person_camera_rig](const auto& event) { - move_first_person_camera_rig({-1, 0}, event.input_value); + move_first_person_camera_rig({-1, 0}, movement_speed * event.input_value); + } + ) + ); + action_subscriptions.emplace_back + ( + ctx.move_left_action.get_deactivated_channel().subscribe + ( + [&, stop_first_person_camera_rig](const auto& event) + { + stop_first_person_camera_rig(); } ) ); @@ -497,10 +584,94 @@ void nest_selection_state::setup_controls() action_subscriptions.emplace_back ( ctx.move_right_action.get_active_channel().subscribe + ( + [&, move_first_person_camera_rig](const auto& event) + { + move_first_person_camera_rig({1, 0}, movement_speed * event.input_value); + } + ) + ); + action_subscriptions.emplace_back + ( + ctx.move_right_action.get_deactivated_channel().subscribe + ( + [&, stop_first_person_camera_rig](const auto& event) + { + stop_first_person_camera_rig(); + } + ) + ); + + // Move up + action_subscriptions.emplace_back + ( + ctx.move_up_action.get_active_channel().subscribe + ( + [&](const auto& event) + { + ctx.entity_registry->patch + ( + first_person_camera_rig_eid, + [&](auto& component) + { + component.force += float3{0, movement_speed * event.input_value, 0}; + } + ); + } + ) + ); + + // Move down + action_subscriptions.emplace_back + ( + ctx.move_down_action.get_active_channel().subscribe + ( + [&](const auto& event) + { + ctx.entity_registry->patch + ( + first_person_camera_rig_eid, + [&](auto& component) + { + component.force -= float3{0, movement_speed * event.input_value, 0}; + } + ); + } + ) + ); + + // Focus + action_subscriptions.emplace_back + ( + ctx.focus_action.get_activated_channel().subscribe + ( + [&](const auto& event) + { + ctx.entity_registry->patch + ( + first_person_camera_rig_track_to_eid, + [&](auto& component) + { + component.active = true; + } + ); + } + ) + ); + action_subscriptions.emplace_back + ( + ctx.focus_action.get_deactivated_channel().subscribe ( [&](const auto& event) { - move_first_person_camera_rig({1, 0}, event.input_value); + ctx.entity_registry->patch + ( + first_person_camera_rig_track_to_eid, + [&](auto& component) + { + component.active = false; + } + ); } ) ); diff --git a/src/game/states/nest-selection-state.hpp b/src/game/states/nest-selection-state.hpp index b50bec0..ba058d7 100644 --- a/src/game/states/nest-selection-state.hpp +++ b/src/game/states/nest-selection-state.hpp @@ -47,9 +47,12 @@ private: bool mouse_look{false}; + entity::id worker_ant_eid; + entity::id first_person_camera_rig_eid; entity::id first_person_camera_rig_spring_translation_eid; entity::id first_person_camera_rig_spring_rotation_eid; + entity::id first_person_camera_rig_track_to_eid; entity::id first_person_camera_rig_fov_spring_eid; float first_person_camera_rig_translation_spring_angular_frequency; float first_person_camera_rig_rotation_spring_angular_frequency; diff --git a/src/game/states/nuptial-flight-state.cpp b/src/game/states/nuptial-flight-state.cpp index 44e8ed6..df95863 100644 --- a/src/game/states/nuptial-flight-state.cpp +++ b/src/game/states/nuptial-flight-state.cpp @@ -27,11 +27,9 @@ #include "game/systems/atmosphere-system.hpp" #include "game/systems/collision-system.hpp" #include "game/components/ant-caste-component.hpp" -#include "game/components/locomotion-component.hpp" #include "game/components/transform-component.hpp" #include "game/components/terrain-component.hpp" #include "game/components/camera-component.hpp" -#include "game/components/model-component.hpp" #include "game/components/name-component.hpp" #include "game/components/constraint-stack-component.hpp" #include "game/components/steering-component.hpp" @@ -110,10 +108,6 @@ nuptial_flight_state::nuptial_flight_state(::game& ctx): female_name_pool = ctx.resource_manager->load("female-names-en.txt"); male_name_pool = ctx.resource_manager->load("male-names-en.txt"); - // Init RNG - std::random_device random_device; - std::mt19937 rng(random_device()); - // Assign random ant names std::uniform_int_distribution<> female_name_pool_distribution(0, static_cast(female_name_pool->lines.size() - 1)); std::uniform_int_distribution<> male_name_pool_distribution(0, static_cast(male_name_pool->lines.size() - 1)); @@ -126,7 +120,7 @@ nuptial_flight_state::nuptial_flight_state(::game& ctx): ctx.entity_registry->emplace_or_replace ( entity_id, - male_name_pool->lines[male_name_pool_distribution(rng)] + male_name_pool->lines[male_name_pool_distribution(ctx.rng)] ); } else @@ -134,7 +128,7 @@ nuptial_flight_state::nuptial_flight_state(::game& ctx): ctx.entity_registry->emplace_or_replace ( entity_id, - female_name_pool->lines[female_name_pool_distribution(rng)] + female_name_pool->lines[female_name_pool_distribution(ctx.rng)] ); } } diff --git a/src/game/systems/astronomy-system.cpp b/src/game/systems/astronomy-system.cpp index bf84f5d..9a44864 100644 --- a/src/game/systems/astronomy-system.cpp +++ b/src/game/systems/astronomy-system.cpp @@ -124,40 +124,39 @@ void astronomy_system::update(float t, float dt) update_icrf_to_eus(*reference_body, *reference_orbit); // Set the transform component translations of orbiting bodies to their topocentric positions - registry.view().each( - [&](entity::id entity_id, const auto& body, const auto& orbit, auto& transform) - { - // Skip reference body entity - if (entity_id == reference_body_eid) - return; - - // Transform orbital Cartesian position (r) from the ICRF frame to the EUS frame - const double3 r_eus = icrf_to_eus * orbit.position; - - // Evaluate body orientation polynomials - const double body_pole_ra = math::polynomial::horner(body.pole_ra.begin(), body.pole_ra.end(), time_centuries); - const double body_pole_dec = math::polynomial::horner(body.pole_dec.begin(), body.pole_dec.end(), time_centuries); - const double body_prime_meridian = math::polynomial::horner(body.prime_meridian.begin(), body.prime_meridian.end(), time_days); - - // Determine body orientation in the ICRF frame - math::quaternion rotation_icrf = physics::orbit::frame::bcbf::to_bci - ( - body_pole_ra, - body_pole_dec, - body_prime_meridian - ).r; - - // Transform body orientation from the ICRF frame to the EUS frame. - math::quaternion rotation_eus = math::normalize(icrf_to_eus.r * rotation_icrf); - - // Update local transform - if (orbit.parent != entt::null) + registry.view().each + ( + [&](entity::id entity_id, const auto& body, const auto& orbit, auto& transform) { + // Skip reference body entity + if (entity_id == reference_body_eid || orbit.parent == entt::null) + return; + + // Transform orbital Cartesian position (r) from the ICRF frame to the EUS frame + const double3 r_eus = icrf_to_eus * orbit.position; + + // Evaluate body orientation polynomials + const double body_pole_ra = math::polynomial::horner(body.pole_ra.begin(), body.pole_ra.end(), time_centuries); + const double body_pole_dec = math::polynomial::horner(body.pole_dec.begin(), body.pole_dec.end(), time_centuries); + const double body_prime_meridian = math::polynomial::horner(body.prime_meridian.begin(), body.prime_meridian.end(), time_days); + + // Determine body orientation in the ICRF frame + math::quaternion rotation_icrf = physics::orbit::frame::bcbf::to_bci + ( + body_pole_ra, + body_pole_dec, + body_prime_meridian + ).r; + + // Transform body orientation from the ICRF frame to the EUS frame. + math::quaternion rotation_eus = math::normalize(icrf_to_eus.r * rotation_icrf); + + // Update local transform transform.local.translation = math::normalize(float3(r_eus)); transform.local.rotation = math::quaternion(rotation_eus); transform.local.scale = {1.0f, 1.0f, 1.0f}; } - }); + ); constexpr double3 bounce_normal = {0, 1, 0}; double3 bounce_illuminance = {0, 0, 0}; diff --git a/src/game/systems/constraint-system.cpp b/src/game/systems/constraint-system.cpp index c88bbf2..110e580 100644 --- a/src/game/systems/constraint-system.cpp +++ b/src/game/systems/constraint-system.cpp @@ -120,29 +120,29 @@ void constraint_system::on_constraint_stack_update(entity::registry& registry, e void constraint_system::handle_constraint(transform_component& transform, entity::id constraint_eid, float dt) { - if (auto constraint = registry.try_get(constraint_eid); constraint) + if (auto constraint = registry.try_get(constraint_eid)) handle_copy_translation_constraint(transform, *constraint); - else if (auto constraint = registry.try_get(constraint_eid); constraint) + else if (auto constraint = registry.try_get(constraint_eid)) handle_copy_rotation_constraint(transform, *constraint); - else if (auto constraint = registry.try_get(constraint_eid); constraint) + else if (auto constraint = registry.try_get(constraint_eid)) handle_copy_scale_constraint(transform, *constraint); - else if (auto constraint = registry.try_get(constraint_eid); constraint) + else if (auto constraint = registry.try_get(constraint_eid)) handle_copy_transform_constraint(transform, *constraint); - else if (auto constraint = registry.try_get(constraint_eid); constraint) + else if (auto constraint = registry.try_get(constraint_eid)) handle_track_to_constraint(transform, *constraint); - else if (auto constraint = registry.try_get(constraint_eid); constraint) + else if (auto constraint = registry.try_get(constraint_eid)) handle_three_dof_constraint(transform, *constraint); - else if (auto constraint = registry.try_get(constraint_eid); constraint) + else if (auto constraint = registry.try_get(constraint_eid)) handle_pivot_constraint(transform, *constraint); - else if (auto constraint = registry.try_get(constraint_eid); constraint) + else if (auto constraint = registry.try_get(constraint_eid)) handle_child_of_constraint(transform, *constraint); - else if (auto constraint = registry.try_get(constraint_eid); constraint) + else if (auto constraint = registry.try_get(constraint_eid)) handle_spring_to_constraint(transform, *constraint, dt); - else if (auto constraint = registry.try_get(constraint_eid); constraint) + else if (auto constraint = registry.try_get(constraint_eid)) handle_spring_translation_constraint(transform, *constraint, dt); - else if (auto constraint = registry.try_get(constraint_eid); constraint) + else if (auto constraint = registry.try_get(constraint_eid)) handle_spring_rotation_constraint(transform, *constraint, dt); - else if (auto constraint = registry.try_get(constraint_eid); constraint) + else if (auto constraint = registry.try_get(constraint_eid)) handle_ease_to_constraint(transform, *constraint, dt); } diff --git a/src/game/systems/locomotion-system.cpp b/src/game/systems/locomotion-system.cpp index a53f74d..8a12891 100644 --- a/src/game/systems/locomotion-system.cpp +++ b/src/game/systems/locomotion-system.cpp @@ -18,11 +18,11 @@ */ #include "game/systems/locomotion-system.hpp" -#include "game/components/collision-component.hpp" -#include "game/components/locomotion-component.hpp" -#include "game/components/transform-component.hpp" +#include "game/components/legged-locomotion-component.hpp" +#include "game/components/physics-component.hpp" #include - +#include +#include locomotion_system::locomotion_system(entity::registry& registry): updatable_system(registry) @@ -30,9 +30,23 @@ locomotion_system::locomotion_system(entity::registry& registry): void locomotion_system::update(float t, float dt) { - registry.view().each( - [&](entity::id entity_id, auto& transform, auto& locomotion) + auto group = registry.group(entt::get); + std::for_each + ( + std::execution::par_unseq, + group.begin(), + group.end(), + [&](auto entity_id) { - }); + const auto& locomotion = group.get(entity_id); + auto& body = group.get(entity_id); + + // Apply locomotion force + body.force += locomotion.force; + + // Apply friction + const float friction_coef = 2.0f; + body.force -= body.velocity * friction_coef * body.mass; + } + ); } - diff --git a/src/game/systems/locomotion-system.hpp b/src/game/systems/locomotion-system.hpp index 13750ea..3db4c96 100644 --- a/src/game/systems/locomotion-system.hpp +++ b/src/game/systems/locomotion-system.hpp @@ -22,14 +22,15 @@ #include "game/systems/updatable-system.hpp" - +/** + * + */ class locomotion_system: public updatable_system { public: explicit locomotion_system(entity::registry& registry); - virtual void update(float t, float dt); + void update(float t, float dt) override; }; - #endif // ANTKEEPER_GAME_LOCOMOTION_SYSTEM_HPP diff --git a/src/game/systems/orbit-system.cpp b/src/game/systems/orbit-system.cpp index 876e29e..3d8bd1d 100644 --- a/src/game/systems/orbit-system.cpp +++ b/src/game/systems/orbit-system.cpp @@ -50,19 +50,21 @@ void orbit_system::update(float t, float dt) positions[i] = ephemeris->trajectories[i].position(time) * 1000.0; // Propagate orbits - registry.view().each( - [&](entity::id entity_eid, auto& orbit) - { - orbit.position = positions[orbit.ephemeris_index] * orbit.scale; - - entity::id parent_id = orbit.parent; - while (parent_id != entt::null) + registry.view().each + ( + [&](entity::id entity_eid, auto& orbit) { - const orbit_component& parent_orbit = registry.get(parent_id); - orbit.position += positions[parent_orbit.ephemeris_index] * parent_orbit.scale; - parent_id = parent_orbit.parent; + orbit.position = positions[orbit.ephemeris_index] * orbit.scale; + + entity::id parent_id = orbit.parent; + while (parent_id != entt::null) + { + const orbit_component& parent_orbit = registry.get(parent_id); + orbit.position += positions[parent_orbit.ephemeris_index] * parent_orbit.scale; + parent_id = parent_orbit.parent; + } } - }); + ); } void orbit_system::set_ephemeris(std::shared_ptr> ephemeris) diff --git a/src/game/systems/physics-system.cpp b/src/game/systems/physics-system.cpp new file mode 100644 index 0000000..a0360c7 --- /dev/null +++ b/src/game/systems/physics-system.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include "game/systems/physics-system.hpp" +#include "game/components/transform-component.hpp" +#include "game/components/physics-component.hpp" +#include + +physics_system::physics_system(entity::registry& registry): + updatable_system(registry) +{} + +void physics_system::update(float t, float dt) +{ + integrate(dt); +} + +void physics_system::integrate(float dt) +{ + auto group = registry.group(entt::get); + for (auto entity_id: group) + { + auto& body = group.get(entity_id); + + // Air resistance + const float air_drag_coef = 0.58f; + body.force -= body.velocity * air_drag_coef * body.mass; + + // Gravity + const math::vector weight_force = gravity * body.mass; + //body.force += weight_force; + + body.acceleration = body.force / body.mass; + body.velocity += body.acceleration * dt; + + registry.patch<::transform_component> + ( + entity_id, + [&body, dt](auto& transform) + { + transform.local.translation += body.velocity * dt; + } + ); + + body.force = math::vector::zero(); + } +} diff --git a/src/game/systems/physics-system.hpp b/src/game/systems/physics-system.hpp new file mode 100644 index 0000000..8cc1fa2 --- /dev/null +++ b/src/game/systems/physics-system.hpp @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_PHYSICS_SYSTEM_HPP +#define ANTKEEPER_GAME_PHYSICS_SYSTEM_HPP + +#include "game/systems/updatable-system.hpp" +#include +#include + +/** + * + */ +class physics_system: + public updatable_system +{ +public: + explicit physics_system(entity::registry& registry); + void update(float t, float dt) override; + +private: + /** + * Semi-implicit Euler integration. + * + * @param dt Timestep, in seconds. + * + * @see https://gafferongames.com/post/integration_basics/ + */ + void integrate(float dt); + + math::vector gravity{0.0f, -9.80665f, 0.0f}; +}; + +#endif // ANTKEEPER_GAME_PHYSICS_SYSTEM_HPP diff --git a/src/game/systems/render-system.cpp b/src/game/systems/render-system.cpp index f2b4c9c..b4ca201 100644 --- a/src/game/systems/render-system.cpp +++ b/src/game/systems/render-system.cpp @@ -20,34 +20,30 @@ #include "game/systems/render-system.hpp" #include "game/components/transform-component.hpp" #include "game/components/camera-component.hpp" -#include -#include -#include -#include - +#include +#include render_system::render_system(entity::registry& registry): updatable_system(registry), + updated_scene_transforms(registry, entt::collector.update().where()), t(0.0), dt(0.0), renderer(nullptr) { - registry.on_construct().connect<&render_system::on_model_construct>(this); - registry.on_update().connect<&render_system::on_model_update>(this); - registry.on_destroy().connect<&render_system::on_model_destroy>(this); - registry.on_construct().connect<&render_system::on_light_construct>(this); - registry.on_update().connect<&render_system::on_light_update>(this); - registry.on_destroy().connect<&render_system::on_light_destroy>(this); + registry.on_construct().connect<&render_system::on_scene_construct>(this); + registry.on_update().connect<&render_system::on_scene_update>(this); + registry.on_destroy().connect<&render_system::on_scene_destroy>(this); + + registry.on_construct().connect<&render_system::on_transform_construct>(this); } render_system::~render_system() { - registry.on_construct().disconnect<&render_system::on_model_construct>(this); - registry.on_update().disconnect<&render_system::on_model_update>(this); - registry.on_destroy().disconnect<&render_system::on_model_destroy>(this); - registry.on_construct().disconnect<&render_system::on_light_construct>(this); - registry.on_update().disconnect<&render_system::on_light_update>(this); - registry.on_destroy().disconnect<&render_system::on_light_destroy>(this); + registry.on_construct().disconnect<&render_system::on_scene_construct>(this); + registry.on_update().disconnect<&render_system::on_scene_update>(this); + registry.on_destroy().disconnect<&render_system::on_scene_destroy>(this); + + registry.on_construct().disconnect<&render_system::on_transform_construct>(this); } void render_system::update(float t, float dt) @@ -55,22 +51,27 @@ void render_system::update(float t, float dt) this->t = t; this->dt = dt; - // Update model instance transforms - registry.view().each + std::for_each ( - [this](entity::id entity_id, auto& transform, auto& model) + std::execution::par_unseq, + updated_scene_transforms.begin(), + updated_scene_transforms.end(), + [&](auto entity_id) { - scene::model_instance& instance = *model_instances[entity_id]; - - instance.set_transform(transform.world); + auto& transform = registry.get(entity_id); + const auto& scene = registry.get(entity_id); + + // WARNING: could potentially lead to multithreading issues with scene::object_base::transformed() + scene.object->set_transform(transform.world); + if (transform.warp) { - instance.get_transform_tween().update(); - instance.update_tweens(); + scene.object->get_transform_tween().update(); transform.warp = false; } } ); + updated_scene_transforms.clear(); // Update camera transforms registry.view().each @@ -86,23 +87,6 @@ void render_system::update(float t, float dt) } } ); - - // Update light transforms - registry.view().each - ( - [this](entity::id entity_id, auto& transform, auto& light) - { - scene::light& light_object = *lights[entity_id]; - - light_object.set_transform(transform.world); - if (transform.warp) - { - light_object.get_transform_tween().update(); - light_object.update_tweens(); - transform.warp = false; - } - } - ); } void render_system::draw(float alpha) @@ -131,154 +115,65 @@ void render_system::set_renderer(render::renderer* renderer) this->renderer = renderer; } -scene::model_instance* render_system::get_model_instance(entity::id entity_id) +void render_system::on_scene_construct(entity::registry& registry, entity::id entity_id) { - if (auto it = model_instances.find(entity_id); it != model_instances.end()) - return it->second.get(); - return nullptr; -} - -scene::light* render_system::get_light(entity::id entity_id) -{ - if (auto it = lights.find(entity_id); it != lights.end()) - return it->second.get(); - return nullptr; -} - -void render_system::update_model_and_materials(entity::id entity_id, model_component& model) -{ - if (auto model_it = model_instances.find(entity_id); model_it != model_instances.end()) + const auto& component = registry.get<::scene_component>(entity_id); + + // Update scene object transform with pre-existing transform component + if (const auto transform = registry.try_get(entity_id)) { - model_it->second->set_model(model.render_model); - model_it->second->set_instanced((model.instance_count > 0), model.instance_count); - - for (auto material_it = model.materials.begin(); material_it != model.materials.end(); ++material_it) - { - model_it->second->set_material(material_it->first, material_it->second); - } - - // Add model instance to its specified layers - for (std::size_t i = 0; i < std::min(layers.size(), (sizeof(model.layers) << 3)); ++i) - { - layers[i]->remove_object(model_it->second.get()); - - if ((model.layers >> i) & 1) - { - layers[i]->add_object(model_it->second.get()); - } - } + component.object->set_transform(transform->world); + component.object->get_transform_tween().update(); } -} - -void render_system::update_light(entity::id entity_id, ::light_component& component) -{ - if (auto light_it = lights.find(entity_id); light_it != lights.end()) + + for (std::size_t i = 0; i < layers.size(); ++i) { - scene::light& light = *light_it->second; - - light.set_color(component.color); - light.set_intensity(component.intensity); - - switch (light.get_light_type()) + if (component.layer_mask & static_cast(1 << i)) { - case scene::light_type::point: - { - scene::point_light& point = static_cast(light); - point.set_attenuation(component.attenuation); - break; - } - - case scene::light_type::spot: - { - scene::spot_light& spot = static_cast(light); - spot.set_attenuation(component.attenuation); - spot.set_cutoff(component.cutoff); - break; - } - - default: - break; + layers[i]->add_object(component.object.get()); } } } -void render_system::on_model_construct(entity::registry& registry, entity::id entity_id) +void render_system::on_scene_update(entity::registry& registry, entity::id entity_id) { - ::model_component& component = registry.get<::model_component>(entity_id); + const auto& component = registry.get<::scene_component>(entity_id); - model_instances[entity_id] = std::make_unique(); - update_model_and_materials(entity_id, component); -} - -void render_system::on_model_update(entity::registry& registry, entity::id entity_id) -{ - ::model_component& component = registry.get<::model_component>(entity_id); - update_model_and_materials(entity_id, component); -} - -void render_system::on_model_destroy(entity::registry& registry, entity::id entity_id) -{ - if (auto it = model_instances.find(entity_id); it != model_instances.end()) + for (std::size_t i = 0; i < layers.size(); ++i) { - for (scene::collection* layer: layers) - layer->remove_object(it->second.get()); - - model_instances.erase(it); + // Remove from layer + scene::collection* layer = layers[i]; + layer->remove_object(component.object.get()); + + if (component.layer_mask & static_cast(1 << i)) + { + // Add to layer + layer->add_object(component.object.get()); + } } } -void render_system::on_light_construct(entity::registry& registry, entity::id entity_id) +void render_system::on_scene_destroy(entity::registry& registry, entity::id entity_id) { - ::light_component& component = registry.get<::light_component>(entity_id); - - std::unique_ptr light; + const auto& component = registry.get<::scene_component>(entity_id); - switch (component.type) + for (std::size_t i = 0; i < layers.size(); ++i) { - case scene::light_type::ambient: - light = std::make_unique(); - break; - - case scene::light_type::directional: - light = std::make_unique(); - break; - - case scene::light_type::point: - light = std::make_unique(); - break; - - case scene::light_type::spot: - light = std::make_unique(); - break; - - default: - break; - } - - if (light) - { - for (scene::collection* layer: layers) - layer->add_object(light.get()); - - lights[entity_id] = std::move(light); - - update_light(entity_id, component); + if (component.layer_mask & static_cast(1 << i)) + { + layers[i]->remove_object(component.object.get()); + } } } -void render_system::on_light_update(entity::registry& registry, entity::id entity_id) -{ - ::light_component& component = registry.get<::light_component>(entity_id); - update_light(entity_id, component); -} - -void render_system::on_light_destroy(entity::registry& registry, entity::id entity_id) +void render_system::on_transform_construct(entity::registry& registry, entity::id entity_id) { - if (auto it = lights.find(entity_id); it != lights.end()) + // Update pre-existing scene object transform withtransform component + if (const auto scene = registry.try_get(entity_id)) { - for (scene::collection* layer: layers) - layer->remove_object(it->second.get()); + const auto& transform = registry.get(entity_id); - lights.erase(it); + scene->object->set_transform(transform.world); + scene->object->get_transform_tween().update(); } } diff --git a/src/game/systems/render-system.hpp b/src/game/systems/render-system.hpp index 17bf612..e77c1a4 100644 --- a/src/game/systems/render-system.hpp +++ b/src/game/systems/render-system.hpp @@ -22,10 +22,9 @@ #include "game/systems/updatable-system.hpp" #include -#include +#include #include -#include "game/components/model-component.hpp" -#include "game/components/light-component.hpp" +#include "game/components/scene-component.hpp" #include #include #include @@ -42,32 +41,24 @@ public: void draw(float alpha); - void add_layer(scene::collection* layer); void remove_layers(); void set_renderer(::render::renderer* renderer); - - scene::model_instance* get_model_instance(entity::id entity_id); - scene::light* get_light(entity::id entity_id); -private: - void update_model_and_materials(entity::id entity_id, ::model_component& model); - void update_light(entity::id entity_id, ::light_component& component); +private: + void on_scene_construct(entity::registry& registry, entity::id entity_id); + void on_scene_update(entity::registry& registry, entity::id entity_id); + void on_scene_destroy(entity::registry& registry, entity::id entity_id); + + void on_transform_construct(entity::registry& registry, entity::id entity_id); - void on_model_construct(entity::registry& registry, entity::id entity_id); - void on_model_update(entity::registry& registry, entity::id entity_id); - void on_model_destroy(entity::registry& registry, entity::id entity_id); - void on_light_construct(entity::registry& registry, entity::id entity_id); - void on_light_update(entity::registry& registry, entity::id entity_id); - void on_light_destroy(entity::registry& registry, entity::id entity_id); + entt::observer updated_scene_transforms; float t; float dt; ::render::renderer* renderer; std::vector layers; - std::unordered_map> model_instances; - std::unordered_map> lights; }; diff --git a/src/game/systems/spatial-system.cpp b/src/game/systems/spatial-system.cpp index 4bf02a6..04e11b2 100644 --- a/src/game/systems/spatial-system.cpp +++ b/src/game/systems/spatial-system.cpp @@ -20,7 +20,8 @@ #include "game/systems/spatial-system.hpp" #include "game/components/transform-component.hpp" #include "game/components/constraint-stack-component.hpp" - +#include +#include spatial_system::spatial_system(entity::registry& registry): updatable_system(registry), @@ -29,12 +30,17 @@ spatial_system::spatial_system(entity::registry& registry): void spatial_system::update(float t, float dt) { - // Update world-space transforms of all updated, unconstrained transforms - for (const auto transform_eid: updated_unconstrained_transforms) - { - auto& transform = registry.get(transform_eid); - transform.world = transform.local; - } + // Update world-space transforms of all updated, unconstrained transform components + std::for_each + ( + std::execution::par_unseq, + updated_unconstrained_transforms.begin(), + updated_unconstrained_transforms.end(), + [&](auto entity_id) + { + auto& transform = registry.get(entity_id); + transform.world = transform.local; + } + ); updated_unconstrained_transforms.clear(); } - diff --git a/src/game/systems/steering-system.cpp b/src/game/systems/steering-system.cpp index 991c6e6..5af22c8 100644 --- a/src/game/systems/steering-system.cpp +++ b/src/game/systems/steering-system.cpp @@ -1,96 +1,95 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "game/systems/steering-system.hpp" -#include "game/components/steering-component.hpp" -#include "game/components/transform-component.hpp" -#include -#include -#include -#include -#include - -steering_system::steering_system(entity::registry& registry): - updatable_system(registry) -{} - -void steering_system::update(float t, float dt) -{ - registry.view().each - ( - [&](entity::id entity_id, auto& steering, auto& transform) - { - auto& agent = steering.agent; - - // Update agent orientation - agent.orientation = transform.local.rotation; - - // Accumulate forces - float3 force = {0, 0, 0}; - if (steering.wander_weight) - { - //force += ai::steering::behavior::wander_2d(agent, steering.wander_noise * dt, steering.wander_distance, steering.wander_radius, steering.wander_angle) * steering.wander_weight; - force += ai::steering::behavior::wander_3d(agent, steering.wander_noise * dt, steering.wander_distance, steering.wander_radius, steering.wander_angle, steering.wander_angle2) * steering.wander_weight; - } - if (steering.seek_weight) - { - force += ai::steering::behavior::seek(agent, steering.seek_target) * steering.seek_weight; - } - - // Normalize force - if (steering.sum_weights) - force /= steering.sum_weights; - - // Accelerate - agent.acceleration = force / agent.mass; - agent.velocity += agent.acceleration * dt; - - // Limit speed - const float speed_squared = math::sqr_length(agent.velocity); - if (speed_squared > agent.max_speed_squared) - { - const float speed = std::sqrt(speed_squared); - agent.velocity = (agent.velocity / speed) * agent.max_speed; - } - - // Move agent - agent.position += agent.velocity * dt; - - // Rotate agent - if (speed_squared) - { - agent.orientation = math::look_rotation(agent.velocity / std::sqrt(speed_squared), agent.up); - agent.forward = agent.orientation * config::global_forward; - agent.up = agent.orientation * config::global_up; - } - - // Update transform - registry.patch<::transform_component> - ( - entity_id, - [&agent](auto& component) - { - component.local.translation = agent.position; - component.local.rotation = agent.orientation; - } - ); - } - ); -} - +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include "game/systems/steering-system.hpp" +#include "game/components/steering-component.hpp" +#include "game/components/transform-component.hpp" +#include +#include +#include +#include +#include + +steering_system::steering_system(entity::registry& registry): + updatable_system(registry) +{} + +void steering_system::update(float t, float dt) +{ + registry.group(entt::get).each + ( + [&](entity::id entity_id, auto& steering, auto& transform) + { + auto& agent = steering.agent; + + // Update agent orientation + agent.orientation = transform.local.rotation; + + // Accumulate forces + float3 force = {0, 0, 0}; + if (steering.wander_weight) + { + //force += ai::steering::behavior::wander_2d(agent, steering.wander_noise * dt, steering.wander_distance, steering.wander_radius, steering.wander_angle) * steering.wander_weight; + force += ai::steering::behavior::wander_3d(agent, steering.wander_noise * dt, steering.wander_distance, steering.wander_radius, steering.wander_angle, steering.wander_angle2) * steering.wander_weight; + } + if (steering.seek_weight) + { + force += ai::steering::behavior::seek(agent, steering.seek_target) * steering.seek_weight; + } + + // Normalize force + if (steering.sum_weights) + force /= steering.sum_weights; + + // Accelerate + agent.acceleration = force / agent.mass; + agent.velocity += agent.acceleration * dt; + + // Limit speed + const float speed_squared = math::sqr_length(agent.velocity); + if (speed_squared > agent.max_speed_squared) + { + const float speed = std::sqrt(speed_squared); + agent.velocity = (agent.velocity / speed) * agent.max_speed; + } + + // Move agent + agent.position += agent.velocity * dt; + + // Rotate agent + if (speed_squared) + { + agent.orientation = math::look_rotation(agent.velocity / std::sqrt(speed_squared), agent.up); + agent.forward = agent.orientation * config::global_forward; + agent.up = agent.orientation * config::global_up; + } + + // Update transform + registry.patch<::transform_component> + ( + entity_id, + [&agent](auto& component) + { + component.local.translation = agent.position; + component.local.rotation = agent.orientation; + } + ); + } + ); +} diff --git a/src/game/systems/subterrain-system.cpp b/src/game/systems/subterrain-system.cpp index 08ab524..2aebcf3 100644 --- a/src/game/systems/subterrain-system.cpp +++ b/src/game/systems/subterrain-system.cpp @@ -18,7 +18,6 @@ */ #include "game/systems/subterrain-system.hpp" -#include "game/components/model-component.hpp" #include "game/components/cavity-component.hpp" #include #include @@ -297,8 +296,8 @@ void subterrain_system::update(float t, float dt) //auto subterrain_entity = registry.create(); //registry.assign(subterrain_entity, subterrain_model); - subterrain_model_instance = new scene::model_instance(subterrain_model); - collection->add_object(subterrain_model_instance); + subterrain_static_mesh = new scene::static_mesh(subterrain_model); + collection->add_object(subterrain_static_mesh); } bool digging = false; diff --git a/src/game/systems/subterrain-system.hpp b/src/game/systems/subterrain-system.hpp index 3234f71..13edf62 100644 --- a/src/game/systems/subterrain-system.hpp +++ b/src/game/systems/subterrain-system.hpp @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include #include #include @@ -122,7 +122,7 @@ private: vector_equals> subterrain_vertex_map; scene::collection* collection; - scene::model_instance* subterrain_model_instance; + scene::static_mesh* subterrain_static_mesh; }; diff --git a/src/game/systems/terrain-system.cpp b/src/game/systems/terrain-system.cpp index 9fda362..5370d80 100644 --- a/src/game/systems/terrain-system.cpp +++ b/src/game/systems/terrain-system.cpp @@ -107,7 +107,7 @@ void terrain_system::update(float t, float dt) { patch* node_patch = generate_patch(node); patches[node] = node_patch; - scene_collection->add_object(node_patch->model_instance); + scene_collection->add_object(node_patch->static_mesh); } } } @@ -119,7 +119,7 @@ void terrain_system::update(float t, float dt) for (auto it = patches.begin(); it != patches.end(); ++it) { bool active = (quadtree.contains(it->first) && quadtree.is_leaf(it->first)); - it->second->model_instance->set_active(active); + it->second->static_mesh->set_active(active); } */ } @@ -645,7 +645,7 @@ terrain_system::patch* terrain_system::generate_patch(quadtree_node_type node) patch* node_patch = new patch(); node_patch->mesh = nullptr;//generate_patch_mesh(node); node_patch->model = generate_patch_model(node); - node_patch->model_instance = new scene::model_instance(node_patch->model); + node_patch->static_mesh = new scene::static_mesh(node_patch->model); return node_patch; */ return nullptr; diff --git a/src/game/systems/terrain-system.hpp b/src/game/systems/terrain-system.hpp index acd353e..6761f3a 100644 --- a/src/game/systems/terrain-system.hpp +++ b/src/game/systems/terrain-system.hpp @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include #include @@ -90,7 +90,7 @@ private: { geom::mesh* mesh; ::render::model* model; - scene::model_instance* model_instance; + scene::static_mesh* static_mesh; }; void on_terrain_construct(entity::registry& registry, entity::id entity_id);