Browse Source

Re-add support for relative mouse mode. Revise and optimize renderer and render operations. Rename model_instance to static_mesh. Replace model_component and light_component with scene_component. Add physics system and component.

master
C. J. Howard 1 year ago
parent
commit
a0657d8d4c
87 changed files with 1411 additions and 1495 deletions
  1. +8
    -4
      src/engine/app/input-manager.hpp
  2. +6
    -6
      src/engine/app/sdl/sdl-input-manager.cpp
  3. +4
    -4
      src/engine/app/sdl/sdl-input-manager.hpp
  4. +2
    -2
      src/engine/render/compositor.cpp
  5. +1
    -2
      src/engine/render/compositor.hpp
  6. +7
    -3
      src/engine/render/context.hpp
  7. +16
    -14
      src/engine/render/operation.hpp
  8. +1
    -2
      src/engine/render/pass.hpp
  9. +1
    -1
      src/engine/render/passes/bloom-pass.cpp
  10. +1
    -1
      src/engine/render/passes/bloom-pass.hpp
  11. +1
    -1
      src/engine/render/passes/clear-pass.cpp
  12. +1
    -1
      src/engine/render/passes/clear-pass.hpp
  13. +1
    -1
      src/engine/render/passes/final-pass.cpp
  14. +1
    -1
      src/engine/render/passes/final-pass.hpp
  15. +1
    -1
      src/engine/render/passes/fxaa-pass.cpp
  16. +1
    -1
      src/engine/render/passes/fxaa-pass.hpp
  17. +1
    -1
      src/engine/render/passes/ground-pass.cpp
  18. +1
    -1
      src/engine/render/passes/ground-pass.hpp
  19. +74
    -68
      src/engine/render/passes/material-pass.cpp
  20. +1
    -1
      src/engine/render/passes/material-pass.hpp
  21. +4
    -1
      src/engine/render/passes/outline-pass.cpp
  22. +1
    -1
      src/engine/render/passes/outline-pass.hpp
  23. +1
    -1
      src/engine/render/passes/resample-pass.cpp
  24. +1
    -1
      src/engine/render/passes/resample-pass.hpp
  25. +23
    -22
      src/engine/render/passes/shadow-map-pass.cpp
  26. +2
    -2
      src/engine/render/passes/shadow-map-pass.hpp
  27. +1
    -1
      src/engine/render/passes/sky-pass.cpp
  28. +1
    -1
      src/engine/render/passes/sky-pass.hpp
  29. +26
    -173
      src/engine/render/renderer.cpp
  30. +7
    -28
      src/engine/render/renderer.hpp
  31. +1
    -4
      src/engine/render/stage.cpp
  32. +0
    -16
      src/engine/render/stage.hpp
  33. +4
    -7
      src/engine/render/stages/culling-stage.cpp
  34. +0
    -0
      src/engine/render/stages/culling-stage.hpp
  35. +19
    -14
      src/engine/render/stages/queue-stage.cpp
  36. +12
    -7
      src/engine/render/stages/queue-stage.hpp
  37. +4
    -8
      src/engine/scene/ambient-light.hpp
  38. +80
    -18
      src/engine/scene/billboard.cpp
  39. +15
    -9
      src/engine/scene/billboard.hpp
  40. +4
    -4
      src/engine/scene/collection.cpp
  41. +17
    -19
      src/engine/scene/collection.hpp
  42. +1
    -7
      src/engine/scene/directional-light.cpp
  43. +60
    -80
      src/engine/scene/directional-light.hpp
  44. +36
    -50
      src/engine/scene/light.hpp
  45. +1
    -11
      src/engine/scene/object.cpp
  46. +73
    -109
      src/engine/scene/object.hpp
  47. +13
    -20
      src/engine/scene/point-light.hpp
  48. +1
    -1
      src/engine/scene/scene.hpp
  49. +38
    -57
      src/engine/scene/spot-light.hpp
  50. +42
    -24
      src/engine/scene/static-mesh.cpp
  51. +21
    -42
      src/engine/scene/static-mesh.hpp
  52. +6
    -20
      src/engine/scene/text.cpp
  53. +4
    -6
      src/engine/scene/text.hpp
  54. +1
    -31
      src/game/ant/ant-cladogenesis.cpp
  55. +37
    -4
      src/game/ant/ant-cladogenesis.hpp
  56. +5
    -5
      src/game/ant/ant-gene-frequency-table.hpp
  57. +12
    -22
      src/game/ant/ant-swarm.cpp
  58. +8
    -8
      src/game/commands/commands.cpp
  59. +10
    -14
      src/game/components/legged-locomotion-component.hpp
  60. +0
    -36
      src/game/components/model-component.hpp
  61. +43
    -0
      src/game/components/physics-component.hpp
  62. +9
    -6
      src/game/components/scene-component.hpp
  63. +6
    -0
      src/game/controls.cpp
  64. +18
    -61
      src/game/game.cpp
  65. +11
    -5
      src/game/game.hpp
  66. +10
    -9
      src/game/loaders/entity-archetype-loader.cpp
  67. +1
    -1
      src/game/platform/windows/nvidia.cpp
  68. +5
    -13
      src/game/spawn.cpp
  69. +0
    -1
      src/game/states/main-menu-state.cpp
  70. +200
    -29
      src/game/states/nest-selection-state.cpp
  71. +3
    -0
      src/game/states/nest-selection-state.hpp
  72. +2
    -8
      src/game/states/nuptial-flight-state.cpp
  73. +28
    -29
      src/game/systems/astronomy-system.cpp
  74. +12
    -12
      src/game/systems/constraint-system.cpp
  75. +22
    -8
      src/game/systems/locomotion-system.cpp
  76. +4
    -3
      src/game/systems/locomotion-system.hpp
  77. +13
    -11
      src/game/systems/orbit-system.cpp
  78. +63
    -0
      src/game/systems/physics-system.cpp
  79. +50
    -0
      src/game/systems/physics-system.hpp
  80. +62
    -167
      src/game/systems/render-system.cpp
  81. +9
    -18
      src/game/systems/render-system.hpp
  82. +14
    -8
      src/game/systems/spatial-system.cpp
  83. +95
    -96
      src/game/systems/steering-system.cpp
  84. +2
    -3
      src/game/systems/subterrain-system.cpp
  85. +2
    -2
      src/game/systems/subterrain-system.hpp
  86. +3
    -3
      src/game/systems/terrain-system.cpp
  87. +2
    -2
      src/game/systems/terrain-system.hpp

+ 8
- 4
src/engine/app/input-manager.hpp View File

@ -51,14 +51,18 @@ public:
virtual void update() = 0; 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. * Returns the event queue associated with registered input devices.

+ 6
- 6
src/engine/app/sdl/sdl-input-manager.cpp View File

@ -317,20 +317,20 @@ void sdl_input_manager::update()
this->event_queue.flush(); 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(); 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(); SDL_ClearError();
} }
} }

+ 4
- 4
src/engine/app/sdl/sdl-input-manager.hpp View File

@ -28,7 +28,7 @@ namespace app {
class sdl_window; class sdl_window;
/** /**
*
* Input manager implementation using SDL2.
*/ */
class sdl_input_manager: public input_manager class sdl_input_manager: public input_manager
{ {
@ -43,9 +43,9 @@ public:
*/ */
virtual ~sdl_input_manager(); 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: private:
input::keyboard keyboard; input::keyboard keyboard;

+ 2
- 2
src/engine/render/compositor.cpp View File

@ -37,13 +37,13 @@ void compositor::remove_passes()
passes.clear(); passes.clear();
} }
void compositor::composite(const render::context& ctx, render::queue& queue)
void compositor::composite(render::context& ctx)
{ {
for (pass* pass: passes) for (pass* pass: passes)
{ {
if (pass->is_enabled()) if (pass->is_enabled())
{ {
pass->render(ctx, queue);
pass->render(ctx);
} }
} }
} }

+ 1
- 2
src/engine/render/compositor.hpp View File

@ -21,7 +21,6 @@
#define ANTKEEPER_RENDER_COMPOSITOR_HPP #define ANTKEEPER_RENDER_COMPOSITOR_HPP
#include <engine/render/context.hpp> #include <engine/render/context.hpp>
#include <engine/render/queue.hpp>
#include <list> #include <list>
namespace render { namespace render {
@ -38,7 +37,7 @@ public:
void remove_pass(pass* pass); void remove_pass(pass* pass);
void remove_passes(); void remove_passes();
void composite(const render::context& ctx, render::queue& queue);
void composite(render::context& ctx);
const std::list<pass*>* get_passes() const; const std::list<pass*>* get_passes() const;

+ 7
- 3
src/engine/render/context.hpp View File

@ -24,7 +24,8 @@
#include <engine/geom/bounding-volume.hpp> #include <engine/geom/bounding-volume.hpp>
#include <engine/utility/fundamental-types.hpp> #include <engine/utility/fundamental-types.hpp>
#include <engine/math/transform-operators.hpp> #include <engine/math/transform-operators.hpp>
#include <list>
#include <engine/render/operation.hpp>
#include <vector>
namespace scene namespace scene
{ {
@ -82,8 +83,11 @@ struct context
/// Subframe interpolation factor. /// Subframe interpolation factor.
float alpha; float alpha;
/// List of objects visible to the active camera.
std::list<scene::object_base*> visible_objects;
/// Objects visible to the active camera.
std::vector<scene::object_base*> objects;
/// Render operations generated by visible objects.
std::vector<const operation*> operations;
}; };
} // namespace render } // namespace render

+ 16
- 14
src/engine/render/operation.hpp View File

@ -24,27 +24,29 @@
#include <engine/animation/pose.hpp> #include <engine/animation/pose.hpp>
#include <engine/gl/vertex-array.hpp> #include <engine/gl/vertex-array.hpp>
#include <engine/gl/drawing-mode.hpp> #include <engine/gl/drawing-mode.hpp>
#include <cstddef>
#include <engine/render/material.hpp>
#include <cstdint>
#include <memory>
#include <span>
namespace render { namespace render {
class material;
/** /**
* Encapsulates an atomic render operation.
* Atomic render operation.
*/ */
struct 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<render::material> material;
float4x4 transform{float4x4::identity()};
float depth{0.0f};
std::size_t instance_count{0};
std::span<const float4x4> skinning_palette{};
}; };
} // namespace render } // namespace render

+ 1
- 2
src/engine/render/pass.hpp View File

@ -23,7 +23,6 @@
#include <engine/gl/rasterizer.hpp> #include <engine/gl/rasterizer.hpp>
#include <engine/gl/framebuffer.hpp> #include <engine/gl/framebuffer.hpp>
#include <engine/render/context.hpp> #include <engine/render/context.hpp>
#include <engine/render/queue.hpp>
namespace render { namespace render {
@ -36,7 +35,7 @@ public:
pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer); pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer);
virtual ~pass(); 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); void set_enabled(bool enabled);
bool is_enabled() const; bool is_enabled() const;

+ 1
- 1
src/engine/render/passes/bloom-pass.cpp View File

@ -94,7 +94,7 @@ bloom_pass::bloom_pass(gl::rasterizer* rasterizer, resource_manager* resource_ma
quad_vao->bind(render::vertex_attribute::position, position_attribute); 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 // Execute command buffer
for (const auto& command: command_buffer) for (const auto& command: command_buffer)

+ 1
- 1
src/engine/render/passes/bloom-pass.hpp View File

@ -57,7 +57,7 @@ public:
* @param ctx Render context. * @param ctx Render context.
* @param queue Render queue. * @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. * Resizes the mip chain resolution according to the resolution of the source texture.

+ 1
- 1
src/engine/render/passes/clear-pass.cpp View File

@ -34,7 +34,7 @@ clear_pass::clear_pass(gl::rasterizer* rasterizer, const gl::framebuffer* frameb
clear_stencil(0) 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) for (const auto& command: command_buffer)
{ {

+ 1
- 1
src/engine/render/passes/clear-pass.hpp View File

@ -34,7 +34,7 @@ class clear_pass: public pass
public: public:
clear_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer); 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. * Sets the buffers to be cleared.

+ 1
- 1
src/engine/render/passes/final-pass.cpp View File

@ -79,7 +79,7 @@ final_pass::final_pass(gl::rasterizer* rasterizer, const gl::framebuffer* frameb
quad_vao->bind(render::vertex_attribute::position, position_attribute); 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 // Update resolution
const auto viewport_size = framebuffer->get_dimensions(); const auto viewport_size = framebuffer->get_dimensions();

+ 1
- 1
src/engine/render/passes/final-pass.hpp View File

@ -41,7 +41,7 @@ class final_pass: public pass
{ {
public: public:
final_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager); 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_color_texture(const gl::texture_2d* texture);
void set_bloom_texture(const gl::texture_2d* texture) noexcept; void set_bloom_texture(const gl::texture_2d* texture) noexcept;

+ 1
- 1
src/engine/render/passes/fxaa-pass.cpp View File

@ -73,7 +73,7 @@ fxaa_pass::fxaa_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuf
quad_vao->bind(render::vertex_attribute::position, position_attribute); 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) for (const auto& command: command_buffer)
{ {

+ 1
- 1
src/engine/render/passes/fxaa-pass.hpp View File

@ -56,7 +56,7 @@ public:
* @param ctx Render context. * @param ctx Render context.
* @param queue Render queue. * @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. * Sets the FXAA source texture.

+ 1
- 1
src/engine/render/passes/ground-pass.cpp View File

@ -58,7 +58,7 @@ ground_pass::ground_pass(gl::rasterizer* rasterizer, const gl::framebuffer* fram
ground_pass::~ground_pass() 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) if (!ground_model)

+ 1
- 1
src/engine/render/passes/ground-pass.hpp View File

@ -44,7 +44,7 @@ class ground_pass: public pass
public: public:
ground_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager); ground_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager);
virtual ~ground_pass(); 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<render::model> model); void set_ground_model(std::shared_ptr<render::model> model);

+ 74
- 68
src/engine/render/passes/material-pass.cpp View File

@ -48,16 +48,78 @@
#include <engine/utility/hash/combine.hpp> #include <engine/utility/hash/combine.hpp>
#include <cmath> #include <cmath>
#include <glad/glad.h> #include <glad/glad.h>
#include <execution>
namespace render { 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): material_pass::material_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager):
pass(rasterizer, framebuffer) 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); rasterizer->use_framebuffer(*framebuffer);
@ -88,13 +150,13 @@ void material_pass::render(const render::context& ctx, render::queue& queue)
evaluate_lighting(ctx); evaluate_lighting(ctx);
evaluate_misc(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 // Get operation material
const render::material* material = operation.material;
const render::material* material = operation->material.get();
if (!material) if (!material)
{ {
if (!fallback_material) if (!fallback_material)
@ -204,20 +266,20 @@ void material_pass::render(const render::context& ctx, render::queue& queue)
} }
// Update geometry-dependent shader variables // Update geometry-dependent shader variables
model = &operation.transform;
model = &operation->transform;
for (const auto& command: active_cache_entry->geometry_command_buffer) for (const auto& command: active_cache_entry->geometry_command_buffer)
{ {
command(); command();
} }
// Draw geometry // 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 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; directional_shadow_count = 0;
spot_light_count = 0; spot_light_count = 0;
const std::list<scene::object_base*>* 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 // Ignore inactive lights
if (!object->is_active()) if (!object->is_active())
@ -965,60 +1027,4 @@ void material_pass::build_material_command_buffer(std::vector
} }
} }
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 render } // namespace render

+ 1
- 1
src/engine/render/passes/material-pass.hpp View File

@ -41,7 +41,7 @@ class material_pass: public pass
public: public:
material_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager); 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. /// 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<render::material> fallback); void set_fallback_material(std::shared_ptr<render::material> fallback);

+ 4
- 1
src/engine/render/passes/outline-pass.cpp View File

@ -60,8 +60,9 @@ outline_pass::outline_pass(gl::rasterizer* rasterizer, const gl::framebuffer* fr
stroke_color_var = stroke_shader->variable("color"); 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); rasterizer->use_framebuffer(*framebuffer);
// Determine viewport based on framebuffer resolution // Determine viewport based on framebuffer resolution
@ -90,6 +91,7 @@ void outline_pass::render(const render::context& ctx, render::queue& queue)
// Setup fill shader // Setup fill shader
rasterizer->use_program(*fill_shader); rasterizer->use_program(*fill_shader);
// Render fills // Render fills
for (const render::operation& operation: queue) 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); glDisable(GL_STENCIL_TEST);
*/
} }
void outline_pass::set_outline_width(float width) void outline_pass::set_outline_width(float width)

+ 1
- 1
src/engine/render/passes/outline-pass.hpp View File

@ -38,7 +38,7 @@ class outline_pass: public pass
public: public:
outline_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager); outline_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager);
virtual ~outline_pass() = default; 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_width(float width);
void set_outline_color(const float4& color); void set_outline_color(const float4& color);

+ 1
- 1
src/engine/render/passes/resample-pass.cpp View File

@ -73,7 +73,7 @@ resample_pass::resample_pass(gl::rasterizer* rasterizer, const gl::framebuffer*
quad_vao->bind(render::vertex_attribute::position, position_attribute); 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) for (const auto& command: command_buffer)
{ {

+ 1
- 1
src/engine/render/passes/resample-pass.hpp View File

@ -55,7 +55,7 @@ public:
* @param ctx Render context. * @param ctx Render context.
* @param queue Render queue. * @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. * Sets the resample source texture.

+ 23
- 22
src/engine/render/passes/shadow-map-pass.cpp View File

@ -39,10 +39,11 @@
#include <engine/math/projection.hpp> #include <engine/math/projection.hpp>
#include <cmath> #include <cmath>
#include <glad/glad.h> #include <glad/glad.h>
#include <execution>
namespace render { 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): shadow_map_pass::shadow_map_pass(gl::rasterizer* rasterizer, resource_manager* resource_manager):
pass(rasterizer, nullptr) 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 // For each light
const std::list<scene::object_base*>* 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 // Ignore inactive lights
if (!object->is_active()) 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 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()); 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 cropped_view_projection;
float4x4 model_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; 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 // Calculate world-space to cascade texture-space transformation matrix
cascade_matrices[i] = bias_tile_matrices[i] * cropped_view_projection; 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) if (material)
{ {
// Skip materials which don't cast shadows // 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 // 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) if (active_shader_program != shader_program)
{ {
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 // 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 // Upload operation-dependent parameters to shader program
if (active_shader_program == unskinned_shader_program.get()) 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 // 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) if (skinned_a)
{ {
@ -310,7 +311,7 @@ bool operation_compare(const render::operation& a, const render::operation& b)
if (two_sided_b) if (two_sided_b)
{ {
// A and B are both two-sided, sort by VAO // 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 else
{ {
@ -328,7 +329,7 @@ bool operation_compare(const render::operation& a, const render::operation& b)
else else
{ {
// A and B are both one-sided, sort by VAO // 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) if (two_sided_b)
{ {
// A and B are both two-sided, sort by VAO // 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 else
{ {
@ -371,7 +372,7 @@ bool operation_compare(const render::operation& a, const render::operation& b)
else else
{ {
// A and B are both one-sided, sort by VAO // A and B are both one-sided, sort by VAO
return (a.vertex_array < b.vertex_array);
return (a->vertex_array < b->vertex_array);
} }
} }
} }

+ 2
- 2
src/engine/render/passes/shadow-map-pass.hpp View File

@ -52,7 +52,7 @@ public:
* @param ctx Render context. * @param ctx Render context.
* @param queue Render queue. * @param queue Render queue.
*/ */
void render(const render::context& ctx, render::queue& queue) override;
void render(render::context& ctx) override;
private: private:
/** /**
@ -62,7 +62,7 @@ private:
* @param ctx Render context. * @param ctx Render context.
* @param queue Render queue. * @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<gl::shader_program> unskinned_shader_program; std::unique_ptr<gl::shader_program> unskinned_shader_program;
const gl::shader_variable* unskinned_model_view_projection_var; const gl::shader_variable* unskinned_model_view_projection_var;

+ 1
- 1
src/engine/render/passes/sky-pass.cpp View File

@ -168,7 +168,7 @@ sky_pass::sky_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffe
rebuild_sky_lut_command_buffer(); 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_BLEND);
glDisable(GL_DEPTH_TEST); glDisable(GL_DEPTH_TEST);

+ 1
- 1
src/engine/render/passes/sky-pass.hpp View File

@ -49,7 +49,7 @@ class sky_pass: public pass
public: public:
sky_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager); sky_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager);
virtual ~sky_pass() = default; virtual ~sky_pass() = default;
void render(const render::context& ctx, render::queue& queue) override;
void render(render::context& ctx) override;
void update_tweens(); void update_tweens();

+ 26
- 173
src/engine/render/renderer.cpp View File

@ -22,7 +22,7 @@
#include <engine/render/compositor.hpp> #include <engine/render/compositor.hpp>
#include <engine/scene/collection.hpp> #include <engine/scene/collection.hpp>
#include <engine/scene/camera.hpp> #include <engine/scene/camera.hpp>
#include <engine/scene/model-instance.hpp>
#include <engine/scene/static-mesh.hpp>
#include <engine/scene/billboard.hpp> #include <engine/scene/billboard.hpp>
#include <engine/scene/lod-group.hpp> #include <engine/scene/lod-group.hpp>
#include <engine/scene/text.hpp> #include <engine/scene/text.hpp>
@ -39,211 +39,64 @@
namespace render { namespace render {
renderer::renderer() 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<render::culling_stage>(); culling_stage = std::make_unique<render::culling_stage>();
queue_stage = std::make_unique<render::queue_stage>();
} }
void renderer::render(float t, float dt, float alpha, const scene::collection& collection) void renderer::render(float t, float dt, float alpha, const scene::collection& collection)
{ {
// Get list of all objects in the collection
const std::list<scene::object_base*>* objects = collection.get_objects();
// Build list of cameras to be sorted
const std::list<scene::object_base*>* cameras = collection.get_objects(scene::camera::object_type_id);
std::list<scene::camera*> sorted_cameras;
for (scene::object_base* object: *cameras)
{
sorted_cameras.push_back(static_cast<scene::camera*>(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 // Init render context
render::context ctx;
ctx.collection = &collection; ctx.collection = &collection;
ctx.t = t; ctx.t = t;
ctx.dt = dt; ctx.dt = dt;
ctx.alpha = alpha; 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 // Process cameras in order
for (scene::camera* camera: sorted_cameras)
for (scene::object_base* camera_object: cameras)
{ {
// Skip inactive cameras // Skip inactive cameras
if (!camera->is_active())
if (!camera_object->is_active())
{ {
continue; continue;
} }
scene::camera& camera = static_cast<scene::camera&>(*camera_object);
// Skip cameras with no compositors // Skip cameras with no compositors
compositor* compositor = camera->get_compositor();
compositor* compositor = camera.get_compositor();
if (!compositor) if (!compositor)
{ {
continue; 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_forward = ctx.camera_transform.rotation * config::global_forward;
ctx.camera_up = ctx.camera_transform.rotation * config::global_up; 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.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 // Execute culling stage
culling_stage->execute(ctx); 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 // 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<const scene::model_instance*>(object));
else if (type == scene::billboard::object_type_id)
process_billboard(ctx, queue, static_cast<const scene::billboard*>(object));
else if (type == scene::lod_group::object_type_id)
process_lod_group(ctx, queue, static_cast<const scene::lod_group*>(object));
else if (type == scene::text::object_type_id)
process_text(ctx, queue, static_cast<const scene::text*>(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<float> 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<scene::object_base*>& 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 } // namespace render

+ 7
- 28
src/engine/render/renderer.hpp View File

@ -20,22 +20,11 @@
#ifndef ANTKEEPER_RENDER_RENDERER_HPP #ifndef ANTKEEPER_RENDER_RENDERER_HPP
#define ANTKEEPER_RENDER_RENDERER_HPP #define ANTKEEPER_RENDER_RENDERER_HPP
#include <engine/render/operation.hpp>
#include <engine/render/context.hpp> #include <engine/render/context.hpp>
#include <engine/render/queue.hpp>
#include <engine/render/stage/culling-stage.hpp>
#include <engine/gl/vertex-array.hpp>
#include <vector>
namespace scene
{
class collection;
class object_base;
class model_instance;
class billboard;
class lod_group;
class text;
}
#include <engine/render/stages/culling-stage.hpp>
#include <engine/render/stages/queue-stage.hpp>
#include <engine/scene/collection.hpp>
#include <memory>
namespace render { namespace render {
@ -60,21 +49,11 @@ public:
*/ */
void render(float t, float dt, float alpha, const scene::collection& collection); 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: 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<float4x4> skinning_palette;
render::context ctx;
std::unique_ptr<render::culling_stage> culling_stage; std::unique_ptr<render::culling_stage> culling_stage;
std::unique_ptr<render::queue_stage> queue_stage;
}; };
} // namespace render } // namespace render

+ 1
- 4
src/engine/render/stage.cpp View File

@ -21,9 +21,6 @@
namespace render { namespace render {
void stage::set_priority(int priority)
{
this->priority = priority;
}
} // namespace render } // namespace render

+ 0
- 16
src/engine/render/stage.hpp View File

@ -39,22 +39,6 @@ public:
* @param ctx Render context. * @param ctx Render context.
*/ */
virtual void execute(render::context& ctx) = 0; 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 } // namespace render

src/engine/render/stage/culling-stage.cpp → src/engine/render/stages/culling-stage.cpp View File

@ -17,7 +17,7 @@
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>. * along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <engine/render/stage/culling-stage.hpp>
#include <engine/render/stages/culling-stage.hpp>
#include <engine/scene/camera.hpp> #include <engine/scene/camera.hpp>
#include <engine/scene/collection.hpp> #include <engine/scene/collection.hpp>
#include <algorithm> #include <algorithm>
@ -28,8 +28,8 @@ namespace render {
void culling_stage::execute(render::context& ctx) void culling_stage::execute(render::context& ctx)
{ {
// Get list of all objects in the collection
const std::list<scene::object_base*>& objects = *(ctx.collection->get_objects());
// Get all objects in the collection
const auto& objects = ctx.collection->get_objects();
// Get camera culling volume // Get camera culling volume
ctx.camera_culling_volume = ctx.camera->get_culling_mask(); 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(); 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 // Construct mutex to guard set of visible objects
std::mutex mutex; std::mutex mutex;
@ -77,7 +74,7 @@ void culling_stage::execute(render::context& ctx)
// Insert object into set of visible objects // Insert object into set of visible objects
std::lock_guard<std::mutex> guard(mutex); std::lock_guard<std::mutex> guard(mutex);
ctx.visible_objects.push_back(object);
ctx.objects.push_back(object);
} }
); );
} }

src/engine/render/stage/culling-stage.hpp → src/engine/render/stages/culling-stage.hpp View File


src/game/components/locomotion-component.hpp → src/engine/render/stages/queue-stage.cpp View File

@ -17,21 +17,26 @@
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>. * along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef ANTKEEPER_GAME_PLACEMENT_COMPONENT_HPP
#define ANTKEEPER_GAME_PLACEMENT_COMPONENT_HPP
#include <engine/render/stages/queue-stage.hpp>
#include <engine/scene/object.hpp>
#include <algorithm>
#include <execution>
#include <engine/geom/mesh.hpp>
#include <engine/utility/fundamental-types.hpp>
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

src/engine/render/queue.hpp → src/engine/render/stages/queue-stage.hpp View File

@ -17,17 +17,22 @@
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>. * along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef ANTKEEPER_RENDER_QUEUE_HPP
#define ANTKEEPER_RENDER_QUEUE_HPP
#ifndef ANTKEEPER_RENDER_QUEUE_STAGE_HPP
#define ANTKEEPER_RENDER_QUEUE_STAGE_HPP
#include <engine/render/operation.hpp>
#include <list>
#include <engine/render/stage.hpp>
namespace render { namespace render {
/// Queue of render operations
typedef std::list<render::operation> queue;
/**
* Builds render queues.
*/
class queue_stage: public stage
{
public:
void execute(render::context& ctx) override;
};
} // namespace render } // namespace render
#endif // ANTKEEPER_RENDER_QUEUE_HPP
#endif // ANTKEEPER_RENDER_QUEUE_STAGE_HPP

+ 4
- 8
src/engine/scene/ambient-light.hpp View File

@ -27,16 +27,12 @@ namespace scene {
class ambient_light: public light class ambient_light: public light
{ {
public: 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 } // namespace scene
#endif // ANTKEEPER_SCENE_AMBIENT_LIGHT_HPP #endif // ANTKEEPER_SCENE_AMBIENT_LIGHT_HPP

+ 80
- 18
src/engine/scene/billboard.cpp View File

@ -19,6 +19,8 @@
#include <engine/scene/billboard.hpp> #include <engine/scene/billboard.hpp>
#include <engine/config.hpp> #include <engine/config.hpp>
#include <engine/render/vertex-attribute.hpp>
#include <engine/geom/projection.hpp>
namespace scene { namespace scene {
@ -26,30 +28,94 @@ const typename billboard::aabb_type billboard::local_bounds = {{-1, -1, -1}, {1,
billboard::billboard(): billboard::billboard():
world_bounds(local_bounds), world_bounds(local_bounds),
material(nullptr),
type(billboard_type::flat), type(billboard_type::flat),
alignment_axis(config::global_up) 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::vertex_buffer>(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<gl::vertex_array>();
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<render::material> material) void billboard::set_material(std::shared_ptr<render::material> material)
{ {
this->material = material;
render_op.material = material;
} }
void billboard::set_billboard_type(billboard_type type) void billboard::set_billboard_type(billboard_type type)
@ -70,10 +136,6 @@ void billboard::transformed()
void billboard::update_tweens() void billboard::update_tweens()
{ {
object_base::update_tweens(); object_base::update_tweens();
if (material)
{
//material->update_tweens();
}
} }
} // namespace scene } // namespace scene

+ 15
- 9
src/engine/scene/billboard.hpp View File

@ -24,6 +24,9 @@
#include <engine/geom/aabb.hpp> #include <engine/geom/aabb.hpp>
#include <engine/utility/fundamental-types.hpp> #include <engine/utility/fundamental-types.hpp>
#include <engine/render/material.hpp> #include <engine/render/material.hpp>
#include <engine/render/operation.hpp>
#include <engine/gl/vertex-array.hpp>
#include <engine/gl/vertex-buffer.hpp>
#include <cstdint> #include <cstdint>
#include <memory> #include <memory>
@ -32,13 +35,13 @@ namespace scene {
/// Enumerates billboard types. /// Enumerates billboard types.
enum class billboard_type: std::uint8_t enum class billboard_type: std::uint8_t
{ {
// No alignment
/// No alignment
flat, flat,
// Aligns to face camera
/// Aligns to face camera
spherical, spherical,
// Rotates about an alignment axis to face camera
/// Rotates about an alignment axis to face camera
cylindrical cylindrical
}; };
@ -51,8 +54,8 @@ public:
typedef geom::aabb<float> aabb_type; typedef geom::aabb<float> aabb_type;
billboard(); billboard();
billboard(const billboard& other);
billboard& operator=(const billboard& other);
void render(render::context& ctx) const override;
void set_material(std::shared_ptr<render::material> material); void set_material(std::shared_ptr<render::material> material);
@ -72,9 +75,9 @@ public:
return world_bounds; return world_bounds;
} }
[[nodiscard]] inline const std::shared_ptr<render::material>& get_material() const noexcept
[[nodiscard]] inline std::shared_ptr<render::material> get_material() const noexcept
{ {
return material;
return render_op.material;
} }
[[nodiscard]] inline billboard_type get_billboard_type() const noexcept [[nodiscard]] inline billboard_type get_billboard_type() const noexcept
@ -93,9 +96,12 @@ private:
static const aabb_type local_bounds; static const aabb_type local_bounds;
virtual void transformed(); virtual void transformed();
std::unique_ptr<gl::vertex_buffer> vbo;
std::unique_ptr<gl::vertex_array> vao;
mutable render::operation render_op;
aabb_type world_bounds; aabb_type world_bounds;
std::shared_ptr<render::material> material;
billboard_type type; billboard_type type;
float3 alignment_axis; float3 alignment_axis;
}; };

+ 4
- 4
src/engine/scene/collection.cpp View File

@ -24,14 +24,14 @@ namespace scene {
void collection::add_object(object_base* object) 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) 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() void collection::remove_objects()

+ 17
- 19
src/engine/scene/collection.hpp View File

@ -20,7 +20,7 @@
#ifndef ANTKEEPER_SCENE_COLLECTION_HPP #ifndef ANTKEEPER_SCENE_COLLECTION_HPP
#define ANTKEEPER_SCENE_COLLECTION_HPP #define ANTKEEPER_SCENE_COLLECTION_HPP
#include <list>
#include <vector>
#include <unordered_map> #include <unordered_map>
namespace scene { namespace scene {
@ -52,33 +52,31 @@ public:
/// Updates the tweens of all objects in the collection. /// Updates the tweens of all objects in the collection.
void update_tweens(); void update_tweens();
/// Returns a list of all objects in the collection.
const std::list<object_base*>* get_objects() const;
/// Returns all objects in the collection.
[[nodiscard]] inline const std::vector<object_base*>& 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. * @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<object_base*>* get_objects(std::size_t type_id) const;
[[nodiscard]] inline const std::vector<object_base*>& get_objects(std::size_t type_id) const
{
return object_map[type_id];
}
private: private:
std::list<object_base*> objects;
mutable std::unordered_map<std::size_t, std::list<object_base*>> object_map;
std::vector<object_base*> objects;
mutable std::unordered_map<std::size_t, std::vector<object_base*>> object_map;
}; };
inline const std::list<object_base*>* collection::get_objects() const
{
return &objects;
}
inline const std::list<object_base*>* collection::get_objects(std::size_t type_id) const
{
return &object_map[type_id];
}
} // namespace scene } // namespace scene
#endif // ANTKEEPER_SCENE_COLLECTION_HPP #endif // ANTKEEPER_SCENE_COLLECTION_HPP

+ 1
- 7
src/engine/scene/directional-light.cpp View File

@ -32,13 +32,7 @@ static float3 interpolate_direction(const float3& x, const float3& y, float a)
} }
directional_light::directional_light(): 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_distances.resize(shadow_cascade_count);
shadow_cascade_matrices.resize(shadow_cascade_count); shadow_cascade_matrices.resize(shadow_cascade_count);

+ 60
- 80
src/engine/scene/directional-light.hpp View File

@ -37,10 +37,16 @@ public:
directional_light(); directional_light();
/// Returns light_type::directional. /// 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. /// 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<float3>& get_direction_tween() const noexcept inline const tween<float3>& get_direction_tween() const noexcept
{ {
@ -48,7 +54,7 @@ public:
} }
/// @copydoc object_base::update_tweens(); /// @copydoc object_base::update_tweens();
virtual void update_tweens();
void update_tweens() override;
/// @name Shadow /// @name Shadow
/// @{ /// @{
@ -96,108 +102,82 @@ public:
void set_shadow_cascade_distribution(float weight) noexcept; void set_shadow_cascade_distribution(float weight) noexcept;
/// Returns `true` if the light casts shadows, `false` otherwise. /// 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. /// 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. /// 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. /// 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. /// 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. /// 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. /// Returns the array of shadow cascade far clipping plane distances.
const std::vector<float>& get_shadow_cascade_distances() const noexcept;
std::vector<float>& get_shadow_cascade_distances() noexcept;
/// @{
[[nodiscard]] inline const std::vector<float>& get_shadow_cascade_distances() const noexcept
{
return shadow_cascade_distances;
}
[[nodiscard]] inline std::vector<float>& get_shadow_cascade_distances() noexcept
{
return shadow_cascade_distances;
}
/// @}
/// Returns the array of world-space to cascade texture-space transformation matrices. /// Returns the array of world-space to cascade texture-space transformation matrices.
const std::vector<float4x4>& get_shadow_cascade_matrices() const noexcept;
std::vector<float4x4>& get_shadow_cascade_matrices() noexcept;
/// @{
[[nodiscard]] inline const std::vector<float4x4>& get_shadow_cascade_matrices() const noexcept
{
return shadow_cascade_matrices;
}
[[nodiscard]] inline std::vector<float4x4>& get_shadow_cascade_matrices() noexcept
{
return shadow_cascade_matrices;
}
/// @}
/// @} /// @}
private: private:
virtual void transformed();
void transformed() override;
tween<float3> direction; tween<float3> 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<float> shadow_cascade_distances; mutable std::vector<float> shadow_cascade_distances;
mutable std::vector<float4x4> shadow_cascade_matrices; mutable std::vector<float4x4> 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<float>& directional_light::get_shadow_cascade_distances() const noexcept
{
return shadow_cascade_distances;
}
inline std::vector<float>& directional_light::get_shadow_cascade_distances() noexcept
{
return shadow_cascade_distances;
}
inline const std::vector<float4x4>& directional_light::get_shadow_cascade_matrices() const noexcept
{
return shadow_cascade_matrices;
}
inline std::vector<float4x4>& directional_light::get_shadow_cascade_matrices() noexcept
{
return shadow_cascade_matrices;
}
} // namespace scene } // namespace scene
#endif // ANTKEEPER_SCENE_DIRECTIONAL_LIGHT_HPP #endif // ANTKEEPER_SCENE_DIRECTIONAL_LIGHT_HPP

+ 36
- 50
src/engine/scene/light.hpp View File

@ -54,7 +54,7 @@ public:
light(); light();
/// Returns an enumeration denoting the light object type. /// 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. * Sets the color of the light.
@ -71,23 +71,49 @@ public:
void set_intensity(float intensity); void set_intensity(float intensity);
/// Returns the local-space bounding volume of the light. /// 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. /// 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. /// Returns the light color.
const float3& get_color() const;
[[nodiscard]] inline const float3& get_color() const noexcept
{
return color[1];
}
/// Returns the light intensity. /// Returns the light intensity.
float get_intensity() const;
[[nodiscard]] inline float get_intensity() const noexcept
{
return intensity[1];
}
/// Returns the intensity-scaled light color. /// Returns the intensity-scaled light color.
const float3& get_scaled_color() const;
const tween<float3>& get_color_tween() const;
const tween<float>& get_intensity_tween() const;
const tween<float3>& get_scaled_color_tween() const;
[[nodiscard]] inline const float3& get_scaled_color() const noexcept
{
return scaled_color[1];
}
[[nodiscard]] inline const tween<float3>& get_color_tween() const noexcept
{
return color;
}
[[nodiscard]] inline const tween<float>& get_intensity_tween() const noexcept
{
return intensity;
}
[[nodiscard]] inline const tween<float3>& get_scaled_color_tween() const noexcept
{
return scaled_color;
}
/// @copydoc object_base::update_tweens(); /// @copydoc object_base::update_tweens();
virtual void update_tweens(); virtual void update_tweens();
@ -102,46 +128,6 @@ private:
sphere_type world_bounds; 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<float3>& light::get_color_tween() const
{
return color;
}
inline const tween<float>& light::get_intensity_tween() const
{
return intensity;
}
inline const tween<float3>& light::get_scaled_color_tween() const
{
return scaled_color;
}
} // namespace scene } // namespace scene
#endif // ANTKEEPER_SCENE_LIGHT_HPP #endif // ANTKEEPER_SCENE_LIGHT_HPP

+ 1
- 11
src/engine/scene/object.cpp View File

@ -33,25 +33,15 @@ typename object_base::transform_type object_base::interpolate_transforms(const t
} }
object_base::object_base(): object_base::object_base():
active(true),
transform(math::transform<float>::identity, interpolate_transforms),
culling_mask(nullptr)
transform(math::transform<float>::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() std::size_t object_base::next_object_type_id()
{ {
static std::atomic<std::size_t> id{0}; static std::atomic<std::size_t> id{0};
return id++; return id++;
} }
void object_base::render(const render::context& ctx, render::queue& queue) const
{}
void object_base::update_tweens() void object_base::update_tweens()
{ {
transform.update(); transform.update();

+ 73
- 109
src/engine/scene/object.hpp View File

@ -26,7 +26,6 @@
#include <engine/math/quaternion.hpp> #include <engine/math/quaternion.hpp>
#include <engine/math/transform-type.hpp> #include <engine/math/transform-type.hpp>
#include <engine/render/context.hpp> #include <engine/render/context.hpp>
#include <engine/render/queue.hpp>
#include <atomic> #include <atomic>
#include <cstddef> #include <cstddef>
@ -44,28 +43,19 @@ public:
typedef geom::bounding_volume<float> bounding_volume_type; typedef geom::bounding_volume<float> bounding_volume_type;
/// Returns the type ID for this scene object 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. * Creates a scene object base.
*/ */
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 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. * Updates all tweens in the scene object.
@ -75,7 +65,10 @@ public:
/** /**
* Activates or deactivates the scene object. * 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. * 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. * 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. * 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. * 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. * 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. /// Returns whether the scene object is active.
bool is_active() const;
[[nodiscard]] inline bool is_active() const noexcept
{
return active;
}
/** /**
* Returns the transform. * 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. * 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. * 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. * 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. * Returns the transform tween.
*/ */
const tween<transform_type>& get_transform_tween() const;
tween<transform_type>& get_transform_tween();
/// @{
[[nodiscard]] inline const tween<transform_type>& get_transform_tween() const noexcept
{
return transform;
}
[[nodiscard]] inline tween<transform_type>& get_transform_tween() noexcept
{
return transform;
}
/// @}
/** /**
* Returns the local-space (untransformed) bounds of the object. * 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. * 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. * 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: protected:
static std::size_t next_object_type_id(); static std::size_t next_object_type_id();
@ -163,80 +201,11 @@ private:
*/ */
virtual void transformed(); virtual void transformed();
bool active;
bool active{true};
tween<transform_type> transform; tween<transform_type> 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<typename object_base::transform_type>& object_base::get_transform_tween() const
{
return transform;
}
inline tween<typename object_base::transform_type>& 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. * 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. /// Unique type ID for this scene object type.
static const std::atomic<std::size_t> object_type_id; static const std::atomic<std::size_t> 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 <typename T> template <typename T>
const std::atomic<std::size_t> object<T>::object_type_id{object_base::next_object_type_id()}; const std::atomic<std::size_t> object<T>::object_type_id{object_base::next_object_type_id()};
template <typename T>
inline const std::size_t object<T>::get_object_type_id() const
{
return object_type_id;
}
} // namespace scene } // namespace scene
#endif // ANTKEEPER_SCENE_OBJECT_HPP #endif // ANTKEEPER_SCENE_OBJECT_HPP

+ 13
- 20
src/engine/scene/point-light.hpp View File

@ -34,7 +34,10 @@ public:
point_light(); point_light();
/// Returns light_type::point /// 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. * Sets the attenuation factors of the light.
@ -44,33 +47,23 @@ public:
void set_attenuation(const float3& attenuation); void set_attenuation(const float3& attenuation);
/// Returns the attenuation factors of the light. /// 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. /// Returns the attenuation tween.
const tween<float3>& get_attenuation_tween() const;
[[nodiscard]] inline const tween<float3>& get_attenuation_tween() const noexcept
{
return attenuation;
}
/// @copydoc object_base::update_tweens();
virtual void update_tweens();
void update_tweens() override;
private: private:
tween<float3> attenuation; tween<float3> 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<float3>& point_light::get_attenuation_tween() const
{
return attenuation;
}
} // namespace scene } // namespace scene
#endif // ANTKEEPER_SCENE_POINT_LIGHT_HPP #endif // ANTKEEPER_SCENE_POINT_LIGHT_HPP

+ 1
- 1
src/engine/scene/scene.hpp View File

@ -30,7 +30,7 @@ namespace scene {}
#include <engine/scene/directional-light.hpp> #include <engine/scene/directional-light.hpp>
#include <engine/scene/light.hpp> #include <engine/scene/light.hpp>
#include <engine/scene/lod-group.hpp> #include <engine/scene/lod-group.hpp>
#include <engine/scene/model-instance.hpp>
#include <engine/scene/static-mesh.hpp>
#include <engine/scene/object.hpp> #include <engine/scene/object.hpp>
#include <engine/scene/point-light.hpp> #include <engine/scene/point-light.hpp>
#include <engine/scene/spot-light.hpp> #include <engine/scene/spot-light.hpp>

+ 38
- 57
src/engine/scene/spot-light.hpp View File

@ -35,7 +35,10 @@ public:
spot_light(); spot_light();
/// Returns light_type::spot /// 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. * Sets the attenuation factors of the light.
@ -52,34 +55,57 @@ public:
void set_cutoff(const float2& cutoff); void set_cutoff(const float2& cutoff);
/// Returns the direction vector. /// 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. /// 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. /// 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. /// 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. /// Returns the direction tween.
const tween<float3>& get_direction_tween() const;
[[nodiscard]] inline const tween<float3>& get_direction_tween() const noexcept
{
return direction;
}
/// Returns the attenuation tween. /// Returns the attenuation tween.
const tween<float3>& get_attenuation_tween() const;
[[nodiscard]] inline const tween<float3>& get_attenuation_tween() const noexcept
{
return attenuation;
}
/// Returns the cutoff tween. /// Returns the cutoff tween.
const tween<float2>& get_cutoff_tween() const;
[[nodiscard]] inline const tween<float2>& get_cutoff_tween() const noexcept
{
return cutoff;
}
/// Returns the cosine cutoff tween. /// Returns the cosine cutoff tween.
const tween<float2>& get_cosine_cutoff_tween() const;
[[nodiscard]] inline const tween<float2>& get_cosine_cutoff_tween() const noexcept
{
return cosine_cutoff;
}
/// @copydoc object_base::update_tweens();
virtual void update_tweens();
void update_tweens() override;
private: private:
virtual void transformed();
void transformed() override;
tween<float3> direction; tween<float3> direction;
tween<float3> attenuation; tween<float3> attenuation;
@ -87,51 +113,6 @@ private:
tween<float2> cosine_cutoff; tween<float2> 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<float3>& spot_light::get_direction_tween() const
{
return direction;
}
inline const tween<float3>& spot_light::get_attenuation_tween() const
{
return attenuation;
}
inline const tween<float2>& spot_light::get_cutoff_tween() const
{
return cutoff;
}
inline const tween<float2>& spot_light::get_cosine_cutoff_tween() const
{
return cosine_cutoff;
}
} // namespace scene } // namespace scene
#endif // ANTKEEPER_SCENE_SPOT_LIGHT_HPP #endif // ANTKEEPER_SCENE_SPOT_LIGHT_HPP

src/engine/scene/model-instance.cpp → src/engine/scene/static-mesh.cpp View File

@ -17,54 +17,69 @@
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>. * along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <engine/scene/model-instance.hpp>
#include <engine/scene/static-mesh.hpp>
#include <engine/render/model.hpp> #include <engine/render/model.hpp>
#include <engine/render/material.hpp> #include <engine/render/material.hpp>
namespace scene { namespace scene {
model_instance::model_instance(std::shared_ptr<render::model> model)
static_mesh::static_mesh(std::shared_ptr<render::model> model)
{ {
set_model(model); set_model(model);
} }
void model_instance::set_model(std::shared_ptr<render::model> model)
void static_mesh::set_model(std::shared_ptr<render::model> model)
{ {
this->model = model; this->model = model;
if (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; pose = model->get_skeleton().bind_pose;
::concatenate(pose, pose); ::concatenate(pose, pose);
} }
else else
{ {
operations.clear();
pose.clear(); pose.clear();
} }
update_bounds(); update_bounds();
} }
void model_instance::set_material(std::size_t group_index, std::shared_ptr<render::material> 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<render::material> 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) 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()); world_bounds = aabb_type::transform(local_bounds, get_transform());
} }
void model_instance::update_tweens()
void static_mesh::update_tweens()
{ {
object_base::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);
} }
} }

src/engine/scene/model-instance.hpp → src/engine/scene/static-mesh.hpp View File

@ -17,48 +17,50 @@
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>. * along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/ */
#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 <engine/scene/object.hpp> #include <engine/scene/object.hpp>
#include <engine/animation/pose.hpp> #include <engine/animation/pose.hpp>
#include <engine/geom/aabb.hpp> #include <engine/geom/aabb.hpp>
#include <engine/render/model.hpp> #include <engine/render/model.hpp>
#include <engine/render/operation.hpp>
#include <vector> #include <vector>
namespace scene { namespace scene {
class model_instance: public object<model_instance>
/**
*
*/
class static_mesh: public object<static_mesh>
{ {
public: public:
typedef geom::aabb<float> aabb_type; typedef geom::aabb<float> 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<render::model> model);
explicit static_mesh(std::shared_ptr<render::model> model);
/** /**
* Constructs a model instance. * 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. * 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<render::model> model); void set_model(std::shared_ptr<render::model> model);
/** /**
* Overwrites the material of a model group for this model instance. * 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. * @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<render::material> material);
void set_instanced(bool instanced, std::size_t instance_count = 1);
void set_material(std::size_t index, std::shared_ptr<render::material> material);
/** /**
* Resets all overwritten materials. * Resets all overwritten materials.
@ -103,46 +105,23 @@ public:
} }
/// @} /// @}
/**
* Returns the materials of this model instance.
*/
[[nodiscard]] inline const std::vector<std::shared_ptr<render::material>>& 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(); void update_bounds();
private: private:
virtual void transformed();
void transformed() override;
std::shared_ptr<render::model> model; std::shared_ptr<render::model> model;
mutable std::vector<render::operation> operations;
::pose pose; ::pose pose;
std::vector<std::shared_ptr<render::material>> materials;
aabb_type local_bounds{{0, 0, 0}, {0, 0, 0}}; aabb_type local_bounds{{0, 0, 0}, {0, 0, 0}};
aabb_type world_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 } // namespace scene
#endif // ANTKEEPER_SCENE_MODEL_INSTANCE_HPP
#endif // ANTKEEPER_SCENE_STATIC_MESH_HPP

+ 6
- 20
src/engine/scene/text.cpp View File

@ -27,7 +27,6 @@ namespace scene {
text::text(): text::text():
local_bounds{{0, 0, 0}, {0, 0, 0}}, local_bounds{{0, 0, 0}, {0, 0, 0}},
world_bounds{{0, 0, 0}, {0, 0, 0}}, world_bounds{{0, 0, 0}, {0, 0, 0}},
material(nullptr),
font(nullptr), font(nullptr),
direction(type::text_direction::ltr), direction(type::text_direction::ltr),
content_u8(std::string()), content_u8(std::string()),
@ -81,26 +80,18 @@ text::text():
vao->bind(render::vertex_attribute::color, color_attribute); vao->bind(render::vertex_attribute::color, color_attribute);
// Init render operation // 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.vertex_array = vao.get();
render_op.drawing_mode = gl::drawing_mode::triangles; 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<float, 3>(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<float, 3>(render_op.transform[3]));
queue.push_back(render_op);
} }
void text::refresh() void text::refresh()
@ -110,8 +101,7 @@ void text::refresh()
void text::set_material(std::shared_ptr<render::material> material) void text::set_material(std::shared_ptr<render::material> material)
{ {
this->material = material;
render_op.material = material.get();
render_op.material = material;
} }
void text::set_font(const type::bitmap_font* font) void text::set_font(const type::bitmap_font* font)
@ -168,10 +158,6 @@ void text::transformed()
void text::update_tweens() void text::update_tweens()
{ {
object_base::update_tweens(); object_base::update_tweens();
if (material)
{
//material->update_tweens();
}
} }
void text::update_content() void text::update_content()

+ 4
- 6
src/engine/scene/text.hpp View File

@ -42,8 +42,7 @@ public:
/// Constructs a text object. /// Constructs a text object.
text(); 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. * 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); void set_color(const float4& color);
/// Returns the text material. /// Returns the text material.
const std::shared_ptr<render::material>& get_material() const;
std::shared_ptr<render::material> get_material() const;
/// Returns the text font. /// Returns the text font.
const type::bitmap_font* get_font() const; const type::bitmap_font* get_font() const;
@ -120,7 +119,6 @@ private:
mutable render::operation render_op; mutable render::operation render_op;
aabb_type local_bounds; aabb_type local_bounds;
aabb_type world_bounds; aabb_type world_bounds;
std::shared_ptr<render::material> material;
const type::bitmap_font* font; const type::bitmap_font* font;
type::text_direction direction; type::text_direction direction;
std::string content_u8; std::string content_u8;
@ -133,9 +131,9 @@ private:
std::unique_ptr<gl::vertex_buffer> vbo; std::unique_ptr<gl::vertex_buffer> vbo;
}; };
inline const std::shared_ptr<render::material>& text::get_material() const
inline std::shared_ptr<render::material> text::get_material() const
{ {
return material;
return render_op.material;
} }
inline const type::bitmap_font* text::get_font() const inline const type::bitmap_font* text::get_font() const

+ 1
- 31
src/game/ant/ant-cladogenesis.cpp View File

@ -19,34 +19,4 @@
#include "game/ant/ant-cladogenesis.hpp" #include "game/ant/ant-cladogenesis.hpp"
std::unique_ptr<ant_genome> ant_cladogenesis(const ant_gene_pool& pool, std::random_device& rng)
{
// Allocate genome
std::unique_ptr<ant_genome> genome = std::make_unique<ant_genome>();
// 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;
}

+ 37
- 4
src/game/ant/ant-cladogenesis.hpp View File

@ -26,13 +26,46 @@
#include <random> #include <random>
/** /**
* 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. * @return New genome.
*/ */
[[nodiscard]] std::unique_ptr<ant_genome> ant_cladogenesis(const ant_gene_pool& pool, std::random_device& rng);
template <class URBG>
[[nodiscard]] std::unique_ptr<ant_genome> ant_cladogenesis(const ant_gene_pool& pool, URBG& urbg)
{
// Allocate genome
std::unique_ptr<ant_genome> genome = std::make_unique<ant_genome>();
// 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 #endif // ANTKEEPER_GAME_ANT_CLADOGENESIS_HPP

+ 5
- 5
src/game/ant/ant-gene-frequency-table.hpp View File

@ -41,14 +41,14 @@ struct ant_gene_frequency_table
/** /**
* Samples a gene from the 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. * @return Randomly sampled gene.
*/ */
template <class Generator>
[[nodiscard]] std::shared_ptr<T> sample(Generator& g) const
template <class URBG>
[[nodiscard]] std::shared_ptr<T> sample(URBG& urbg) const
{ {
if (genes.empty()) if (genes.empty())
{ {
@ -57,7 +57,7 @@ struct ant_gene_frequency_table
std::discrete_distribution<std::size_t> distribution(weights.begin(), weights.end()); std::discrete_distribution<std::size_t> distribution(weights.begin(), weights.end());
return genes[distribution(g)];
return genes[distribution(urbg)];
} }
}; };

+ 12
- 22
src/game/ant/ant-swarm.cpp View File

@ -20,7 +20,7 @@
#include "game/ant/ant-swarm.hpp" #include "game/ant/ant-swarm.hpp"
#include "game/components/transform-component.hpp" #include "game/components/transform-component.hpp"
#include "game/components/steering-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/picking-component.hpp"
#include "game/components/ant-caste-component.hpp" #include "game/components/ant-caste-component.hpp"
#include <engine/resources/resource-manager.hpp> #include <engine/resources/resource-manager.hpp>
@ -35,16 +35,16 @@
* *
* @see https://math.stackexchange.com/questions/87230/picking-random-points-in-the-volume-of-sphere-with-uniform-probability/87238#87238 * @see https://math.stackexchange.com/questions/87230/picking-random-points-in-the-volume-of-sphere-with-uniform-probability/87238#87238
*/ */
template <class T, class Generator>
static math::vector3<T> sphere_random(Generator& rng)
template <class T, class URBG>
static math::vector3<T> sphere_random(URBG& urbg)
{ {
std::uniform_real_distribution<T> distribution(T{-1}, T{1}); std::uniform_real_distribution<T> distribution(T{-1}, T{1});
math::vector3<T> position; math::vector3<T> position;
for (std::size_t i = 0; i < 3; ++i) 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) entity::id create_ant_swarm(::game& ctx)
@ -78,17 +78,11 @@ entity::id create_ant_swarm(::game& ctx)
transform.warp = true; transform.warp = true;
ctx.entity_registry->emplace<::transform_component>(swarm_eid, transform); 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<render::model>("male-boid.mdl");
male_model.instance_count = 0;
male_model.layers = 1;
// Load male model
std::shared_ptr<render::model> male_model = ctx.resource_manager->load<render::model>("male-boid.mdl");
// Init queen model component
::model_component queen_model;
queen_model.render_model = ctx.resource_manager->load<render::model>("queen-boid.mdl");
queen_model.instance_count = 0;
queen_model.layers = 1;
// Load queen model
std::shared_ptr<render::model> queen_model = ctx.resource_manager->load<render::model>("queen-boid.mdl");
// Init steering component // Init steering component
::steering_component steering; ::steering_component steering;
@ -120,15 +114,11 @@ entity::id create_ant_swarm(::game& ctx)
ant_caste_component male_caste; ant_caste_component male_caste;
male_caste.caste_type = ant_caste_type::male; male_caste.caste_type = ant_caste_type::male;
// Construct and seed random number generator
std::random_device seed;
std::mt19937 rng(seed());
// Create alates // Create alates
for (std::size_t i = 0; i < alate_count; ++i) for (std::size_t i = 0; i < alate_count; ++i)
{ {
// Generate random position in swarm sphere // Generate random position in swarm sphere
steering.agent.position = swarm_center + sphere_random<float>(rng) * swarm_radius;
steering.agent.position = swarm_center + sphere_random<float>(ctx.rng) * swarm_radius;
transform.local.translation = steering.agent.position; transform.local.translation = steering.agent.position;
entity::id alate_eid = ctx.entity_registry->create(); entity::id alate_eid = ctx.entity_registry->create();
@ -138,7 +128,7 @@ entity::id create_ant_swarm(::game& ctx)
{ {
// Create male // Create male
ctx.entity_registry->emplace<ant_caste_component>(alate_eid, male_caste); ctx.entity_registry->emplace<ant_caste_component>(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<scene::static_mesh>(male_model), std::uint8_t{1});
transform.local.scale = male_scale; transform.local.scale = male_scale;
transform.world = transform.local; transform.world = transform.local;
@ -151,7 +141,7 @@ entity::id create_ant_swarm(::game& ctx)
{ {
// Create queen // Create queen
ctx.entity_registry->emplace<ant_caste_component>(alate_eid, queen_caste); ctx.entity_registry->emplace<ant_caste_component>(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<scene::static_mesh>(queen_model), std::uint8_t{1});
transform.local.scale = queen_scale; transform.local.scale = queen_scale;
transform.world = transform.local; transform.world = transform.local;

+ 8
- 8
src/game/commands/commands.cpp View File

@ -18,13 +18,13 @@
*/ */
#include "game/commands/commands.hpp" #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/transform-component.hpp"
#include "game/components/celestial-body-component.hpp" #include "game/components/celestial-body-component.hpp"
#include "game/components/terrain-component.hpp" #include "game/components/terrain-component.hpp"
#include <engine/math/quaternion.hpp> #include <engine/math/quaternion.hpp>
#include <limits> #include <limits>
namespace command { namespace command {
void translate(entity::registry& registry, entity::id eid, const float3& translation) 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, eid,
[layers](auto& model)
[layer_mask](auto& component)
{ {
model.layers = layers;
component.layer_mask = layer_mask;
} }
); );
} }

src/game/components/light-component.hpp → src/game/components/legged-locomotion-component.hpp View File

@ -17,22 +17,18 @@
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>. * along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/ */
#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 <engine/utility/fundamental-types.hpp>
#include <engine/scene/light.hpp>
#include <engine/math/vector.hpp>
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<float, 3> force{0.0f, 0.0f, 0.0f};
}; };
#endif // ANTKEEPER_GAME_LIGHT_COMPONENT_HPP
#endif // ANTKEEPER_GAME_LEGGED_LOCOMOTION_COMPONENT_HPP

+ 0
- 36
src/game/components/model-component.hpp View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GAME_MODEL_COMPONENT_HPP
#define ANTKEEPER_GAME_MODEL_COMPONENT_HPP
#include <engine/render/model.hpp>
#include <unordered_map>
#include <memory>
struct model_component
{
std::shared_ptr<render::model> render_model;
std::unordered_map<int, std::shared_ptr<render::material>> materials;
int instance_count;
unsigned int layers;
};
#endif // ANTKEEPER_GAME_MODEL_COMPONENT_HPP

+ 43
- 0
src/game/components/physics-component.hpp View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GAME_PHYSICS_COMPONENT_HPP
#define ANTKEEPER_GAME_PHYSICS_COMPONENT_HPP
#include <engine/math/vector.hpp>
struct physics_component
{
/// Mass, in kg.
float mass;
/// Inverse mass, in kg^-1.
//float inverse_mass{0.0f};
/// Force vector, in newtons.
math::vector<float, 3> force{0.0f, 0.0f, 0.0f};
/// Acceleration vector, in m/s^2.
math::vector<float, 3> acceleration{0.0f, 0.0f, 0.0f};
/// Velocity vector, in m/s.
math::vector<float, 3> velocity{0.0f, 0.0f, 0.0f};
};
#endif // ANTKEEPER_GAME_PHYSICS_COMPONENT_HPP

src/game/components/portal-component.hpp → src/game/components/scene-component.hpp View File

@ -17,14 +17,17 @@
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>. * along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/ */
#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 <engine/scene/object.hpp>
#include <cstdint>
#include <memory>
struct portal_component
struct scene_component
{ {
std::unique_ptr<scene::object_base> object;
std::uint8_t layer_mask{0b11111111};
}; };
#endif // ANTKEEPER_GAME_PORTAL_COMPONENT_HPP
#endif // ANTKEEPER_GAME_SCENE_COMPONENT_HPP

+ 6
- 0
src/game/controls.cpp View File

@ -130,6 +130,9 @@ void reset_control_profile(::control_profile& profile)
// Mouse look // Mouse look
mappings.emplace("mouse_look", std::make_unique<input::mouse_button_mapping>(nullptr, input::mouse_button::right)); mappings.emplace("mouse_look", std::make_unique<input::mouse_button_mapping>(nullptr, input::mouse_button::right));
// Focus
mappings.emplace("focus", std::make_unique<input::key_mapping>(nullptr, input::scancode::left_shift, 0, false));
} }
void apply_control_profile(::game& ctx, const ::control_profile& profile) 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(); 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_pick_action, "mouse_pick");
add_mappings(ctx.keeper_action_map, ctx.mouse_look_action, "mouse_look"); 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) void update_control_profile(::game& ctx, ::control_profile& profile)
@ -238,6 +242,7 @@ void update_control_profile(::game& ctx, ::control_profile& profile)
// Keeper controls // Keeper controls
add_mappings(ctx.keeper_action_map, ctx.mouse_pick_action, "mouse_pick"); 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.mouse_look_action, "mouse_look");
add_mappings(ctx.keeper_action_map, ctx.focus_action, "focus");
} }
void setup_window_controls(::game& ctx) void setup_window_controls(::game& ctx)
@ -548,5 +553,6 @@ void disable_keeper_controls(::game& ctx)
ctx.mouse_pick_action.reset(); ctx.mouse_pick_action.reset();
ctx.mouse_look_action.reset(); ctx.mouse_look_action.reset();
ctx.focus_action.reset();
} }

+ 18
- 61
src/game/game.cpp View File

@ -41,6 +41,7 @@
#include "game/systems/spatial-system.hpp" #include "game/systems/spatial-system.hpp"
#include "game/systems/spring-system.hpp" #include "game/systems/spring-system.hpp"
#include "game/systems/steering-system.hpp" #include "game/systems/steering-system.hpp"
#include "game/systems/physics-system.hpp"
#include "game/systems/subterrain-system.hpp" #include "game/systems/subterrain-system.hpp"
#include "game/systems/terrain-system.hpp" #include "game/systems/terrain-system.hpp"
#include <algorithm> #include <algorithm>
@ -119,6 +120,7 @@ game::game(int argc, const char* const* argv)
setup_scenes(); setup_scenes();
setup_animation(); setup_animation();
setup_ui(); setup_ui();
setup_rng();
setup_entities(); setup_entities();
setup_systems(); setup_systems();
setup_controls(); setup_controls();
@ -578,7 +580,7 @@ void game::setup_input()
if (gamepad_active) if (gamepad_active)
{ {
gamepad_active = false; 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) if (!gamepad_active && std::abs(event.position) > 0.5f)
{ {
gamepad_active = true; gamepad_active = true;
input_manager->hide_cursor();
input_manager->set_cursor_visible(false);
} }
} }
); );
@ -601,7 +603,7 @@ void game::setup_input()
if (!gamepad_active) if (!gamepad_active)
{ {
gamepad_active = true; 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()) if (!input_manager->get_gamepads().empty())
{ {
gamepad_active = true; gamepad_active = true;
input_manager->hide_cursor();
input_manager->set_cursor_visible(false);
} }
else else
{ {
@ -793,63 +795,8 @@ void game::setup_rendering()
surface_compositor->add_pass(resample_pass.get()); 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::vertex_buffer>(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<gl::vertex_array>();
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 // Create renderer
renderer = std::make_unique<render::renderer>(); renderer = std::make_unique<render::renderer>();
renderer->set_billboard_vao(billboard_vao.get());
debug::log::trace("Set up rendering"); 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() void game::setup_entities()
{ {
// Create entity registry // Create entity registry
@ -1120,11 +1073,14 @@ void game::setup_systems()
// Setup behavior system // Setup behavior system
behavior_system = std::make_unique<::behavior_system>(*entity_registry); behavior_system = std::make_unique<::behavior_system>(*entity_registry);
// Setup steering system
steering_system = std::make_unique<::steering_system>(*entity_registry);
// Setup locomotion system // Setup locomotion system
locomotion_system = std::make_unique<::locomotion_system>(*entity_registry); 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 // Setup spring system
spring_system = std::make_unique<::spring_system>(*entity_registry); spring_system = std::make_unique<::spring_system>(*entity_registry);
@ -1287,6 +1243,7 @@ void game::setup_loop()
behavior_system->update(time, timestep); behavior_system->update(time, timestep);
steering_system->update(time, timestep); steering_system->update(time, timestep);
locomotion_system->update(time, timestep); locomotion_system->update(time, timestep);
physics_system->update(time, timestep);
camera_system->update(time, timestep); camera_system->update(time, timestep);
orbit_system->update(time, timestep); orbit_system->update(time, timestep);
blackbody_system->update(time, timestep); blackbody_system->update(time, timestep);

+ 11
- 5
src/game/game.hpp View File

@ -35,8 +35,6 @@
#include <engine/gl/framebuffer.hpp> #include <engine/gl/framebuffer.hpp>
#include <engine/gl/rasterizer.hpp> #include <engine/gl/rasterizer.hpp>
#include <engine/gl/texture-2d.hpp> #include <engine/gl/texture-2d.hpp>
#include <engine/gl/vertex-array.hpp>
#include <engine/gl/vertex-buffer.hpp>
#include <engine/i18n/string-map.hpp> #include <engine/i18n/string-map.hpp>
#include <engine/input/action-map.hpp> #include <engine/input/action-map.hpp>
#include <engine/input/action.hpp> #include <engine/input/action.hpp>
@ -58,6 +56,7 @@
#include <queue> #include <queue>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include <random>
#include <vector> #include <vector>
// Forward declarations // Forward declarations
@ -106,6 +105,7 @@ class render_system;
class spatial_system; class spatial_system;
class spring_system; class spring_system;
class steering_system; class steering_system;
class physics_system;
class subterrain_system; class subterrain_system;
class terrain_system; class terrain_system;
@ -215,6 +215,7 @@ public:
input::action pause_action; input::action pause_action;
input::action mouse_pick_action; input::action mouse_pick_action;
input::action mouse_look_action; input::action mouse_look_action;
input::action focus_action;
std::vector<std::shared_ptr<::event::subscription>> window_action_subscriptions; std::vector<std::shared_ptr<::event::subscription>> window_action_subscriptions;
std::vector<std::shared_ptr<::event::subscription>> menu_action_subscriptions; std::vector<std::shared_ptr<::event::subscription>> menu_action_subscriptions;
@ -261,8 +262,6 @@ public:
int2 render_resolution; int2 render_resolution;
float render_scale; float render_scale;
int shadow_map_resolution; int shadow_map_resolution;
std::unique_ptr<gl::vertex_buffer> billboard_vbo;
std::unique_ptr<gl::vertex_array> billboard_vao;
std::unique_ptr<render::clear_pass> ui_clear_pass; std::unique_ptr<render::clear_pass> ui_clear_pass;
std::unique_ptr<render::material_pass> ui_material_pass; std::unique_ptr<render::material_pass> ui_material_pass;
std::unique_ptr<render::compositor> ui_compositor; std::unique_ptr<render::compositor> ui_compositor;
@ -343,6 +342,9 @@ public:
bool captions; bool captions;
float captions_size; float captions_size;
// Random number generation
std::mt19937 rng;
// Entities // Entities
std::unique_ptr<entity::registry> entity_registry; std::unique_ptr<entity::registry> entity_registry;
std::unordered_map<hash::fnv1a32_t, entity::id> entities; std::unordered_map<hash::fnv1a32_t, entity::id> entities;
@ -352,8 +354,9 @@ public:
std::unique_ptr<::camera_system> camera_system; std::unique_ptr<::camera_system> camera_system;
std::unique_ptr<::collision_system> collision_system; std::unique_ptr<::collision_system> collision_system;
std::unique_ptr<::constraint_system> constraint_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<::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<::render_system> render_system;
std::unique_ptr<::subterrain_system> subterrain_system; std::unique_ptr<::subterrain_system> subterrain_system;
std::unique_ptr<::terrain_system> terrain_system; std::unique_ptr<::terrain_system> terrain_system;
@ -364,6 +367,8 @@ public:
std::unique_ptr<::astronomy_system> astronomy_system; std::unique_ptr<::astronomy_system> astronomy_system;
std::unique_ptr<::orbit_system> orbit_system; std::unique_ptr<::orbit_system> orbit_system;
double3 rgb_wavelengths; double3 rgb_wavelengths;
std::shared_ptr<ecoregion> active_ecoregion; std::shared_ptr<ecoregion> active_ecoregion;
@ -382,6 +387,7 @@ private:
void setup_scenes(); void setup_scenes();
void setup_animation(); void setup_animation();
void setup_ui(); void setup_ui();
void setup_rng();
void setup_entities(); void setup_entities();
void setup_systems(); void setup_systems();
void setup_controls(); void setup_controls();

+ 10
- 9
src/game/loaders/entity-archetype-loader.cpp View File

@ -27,13 +27,14 @@
#include "game/components/diffuse-reflector-component.hpp" #include "game/components/diffuse-reflector-component.hpp"
#include "game/components/terrain-component.hpp" #include "game/components/terrain-component.hpp"
#include "game/components/transform-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/orbit-component.hpp"
#include "game/components/blackbody-component.hpp" #include "game/components/blackbody-component.hpp"
#include "game/components/celestial-body-component.hpp" #include "game/components/celestial-body-component.hpp"
#include <engine/entity/archetype.hpp> #include <engine/entity/archetype.hpp>
#include <engine/physics/orbit/elements.hpp> #include <engine/physics/orbit/elements.hpp>
#include <engine/utility/json.hpp> #include <engine/utility/json.hpp>
#include <engine/scene/static-mesh.hpp>
#include <stdexcept> #include <stdexcept>
static bool load_component_atmosphere(entity::archetype& archetype, const json& element) 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) 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<render::model> model;
if (element.contains("file")) if (element.contains("file"))
{ {
component.render_model = resource_manager.load<render::model>(element["file"].get<std::string>());
model = resource_manager.load<render::model>(element["file"].get<std::string>());
} }
archetype.stamps.push_back archetype.stamps.push_back
( (
[component](entt::handle& handle)
[model](entt::handle& handle)
{ {
handle.emplace_or_replace<decltype(component)>(component);
handle.emplace_or_replace<scene_component>
(
std::make_unique<scene::static_mesh>(model),
std::uint8_t{0b00000001}
);
} }
); );

+ 1
- 1
src/game/platform/windows/nvidia.cpp View File

@ -23,5 +23,5 @@
extern "C" extern "C"
{ {
// Direct Nvidia Optimus to use high-performance graphics // Direct Nvidia Optimus to use high-performance graphics
_declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
_declspec(dllexport) DWORD NvOptimusEnablement = 1;
} }

+ 5
- 13
src/game/spawn.cpp View File

@ -19,7 +19,7 @@
#include "game/spawn.hpp" #include "game/spawn.hpp"
#include "game/components/transform-component.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) 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; transform_component.warp = true;
ctx.entity_registry->emplace<::transform_component>(egg_eid, transform_component); 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<scene::static_mesh>(genome.egg->phenes.front().model), std::uint8_t{1});
return egg_eid; return egg_eid;
} }
@ -58,12 +54,8 @@ entity::id spawn_ant_larva(::game& ctx, const ant_genome& genome, const float3&
transform_component.warp = true; transform_component.warp = true;
ctx.entity_registry->emplace<::transform_component>(larva_eid, transform_component); 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<scene::static_mesh>(genome.larva->phenes.front().model), std::uint8_t{1});
return larva_eid; return larva_eid;
} }

+ 0
- 1
src/game/states/main-menu-state.cpp View File

@ -23,7 +23,6 @@
#include <engine/animation/ease.hpp> #include <engine/animation/ease.hpp>
#include <engine/animation/screen-transition.hpp> #include <engine/animation/screen-transition.hpp>
#include <engine/config.hpp> #include <engine/config.hpp>
#include "game/components/model-component.hpp"
#include "game/components/steering-component.hpp" #include "game/components/steering-component.hpp"
#include "game/components/transform-component.hpp" #include "game/components/transform-component.hpp"
#include "game/controls.hpp" #include "game/controls.hpp"

+ 200
- 29
src/game/states/nest-selection-state.cpp View File

@ -24,12 +24,13 @@
#include "game/commands/commands.hpp" #include "game/commands/commands.hpp"
#include "game/components/camera-component.hpp" #include "game/components/camera-component.hpp"
#include "game/components/constraint-stack-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/picking-component.hpp"
#include "game/components/spring-component.hpp" #include "game/components/spring-component.hpp"
#include "game/components/physics-component.hpp"
#include "game/components/steering-component.hpp" #include "game/components/steering-component.hpp"
#include "game/components/terrain-component.hpp" #include "game/components/terrain-component.hpp"
#include "game/components/legged-locomotion-component.hpp"
#include "game/components/transform-component.hpp" #include "game/components/transform-component.hpp"
#include "game/constraints/child-of-constraint.hpp" #include "game/constraints/child-of-constraint.hpp"
#include "game/constraints/copy-rotation-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); ::world::enter_ecoregion(ctx, *ctx.active_ecoregion);
debug::log::trace("Generating genome..."); debug::log::trace("Generating genome...");
std::random_device rng;
std::unique_ptr<ant_genome> genome = ant_cladogenesis(ctx.active_ecoregion->gene_pools[0], rng);
std::unique_ptr<ant_genome> genome = ant_cladogenesis(ctx.active_ecoregion->gene_pools[0], ctx.rng);
debug::log::trace("Generated genome"); debug::log::trace("Generated genome");
debug::log::trace("Building worker phenome..."); debug::log::trace("Building worker phenome...");
@ -97,19 +97,15 @@ nest_selection_state::nest_selection_state(::game& ctx):
debug::log::trace("Generated worker model"); debug::log::trace("Generated worker model");
// Create worker entity(s) // Create worker entity(s)
entity::id worker_eid = ctx.entity_registry->create();
worker_ant_eid = ctx.entity_registry->create();
transform_component worker_transform_component; transform_component worker_transform_component;
worker_transform_component.local = math::transform<float>::identity; worker_transform_component.local = math::transform<float>::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.world = worker_transform_component.local;
worker_transform_component.warp = true; worker_transform_component.warp = true;
ctx.entity_registry->emplace<transform_component>(worker_eid, worker_transform_component);
ctx.entity_registry->emplace<transform_component>(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<model_component>(worker_eid, worker_model_component);
ctx.entity_registry->emplace<scene_component>(worker_ant_eid, std::make_unique<scene::static_mesh>(worker_model), std::uint8_t{1});
// Disable UI color clear // Disable UI color clear
ctx.ui_clear_pass->set_cleared_buffers(false, true, false); 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); // color_checker_archetype->create(*ctx.entity_registry);
// auto ruler_archetype = ctx.resource_manager->load<entity::archetype>("ruler-10cm.ent"); // auto ruler_archetype = ctx.resource_manager->load<entity::archetype>("ruler-10cm.ent");
// ruler_archetype->create(*ctx.entity_registry); // ruler_archetype->create(*ctx.entity_registry);
auto plane_archetype = ctx.resource_manager->load<entity::archetype>("desert-scrub-plane.ent");
auto plane_eid = plane_archetype->create(*ctx.entity_registry);
auto yucca_archetype = ctx.resource_manager->load<entity::archetype>("yucca-plant-l.ent"); auto yucca_archetype = ctx.resource_manager->load<entity::archetype>("yucca-plant-l.ent");
auto yucca_eid = yucca_archetype->create(*ctx.entity_registry); 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<entity::archetype>("yucca-plant-m.ent"); yucca_archetype = ctx.resource_manager->load<entity::archetype>("yucca-plant-m.ent");
yucca_eid = yucca_archetype->create(*ctx.entity_registry); 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<entity::archetype>("yucca-plant-s.ent"); yucca_archetype = ctx.resource_manager->load<entity::archetype>("yucca-plant-s.ent");
yucca_eid = yucca_archetype->create(*ctx.entity_registry); 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<entity::archetype>("barrel-cactus-plant-l.ent"); auto cactus_plant_archetype = ctx.resource_manager->load<entity::archetype>("barrel-cactus-plant-l.ent");
auto cactus_plant_eid = cactus_plant_archetype->create(*ctx.entity_registry); 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<entity::archetype>("barrel-cactus-plant-m.ent"); cactus_plant_archetype = ctx.resource_manager->load<entity::archetype>("barrel-cactus-plant-m.ent");
cactus_plant_eid = cactus_plant_archetype->create(*ctx.entity_registry); 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<entity::archetype>("barrel-cactus-plant-s.ent"); cactus_plant_archetype = ctx.resource_manager->load<entity::archetype>("barrel-cactus-plant-s.ent");
cactus_plant_eid = cactus_plant_archetype->create(*ctx.entity_registry); 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<entity::archetype>("barrel-cactus-seed.ent"); auto cactus_seed_archetype = ctx.resource_manager->load<entity::archetype>("barrel-cactus-seed.ent");
auto cactus_seed_eid = cactus_seed_archetype->create(*ctx.entity_registry); 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 // Queue enable game controls
ctx.function_queue.push ctx.function_queue.push
@ -223,7 +223,20 @@ nest_selection_state::~nest_selection_state()
} }
void nest_selection_state::create_first_person_camera_rig() 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<track_to_constraint>(first_person_camera_rig_track_to_eid, first_person_camera_rig_track_to);
ctx.entity_registry->emplace<constraint_stack_node_component>(first_person_camera_rig_track_to_eid, first_person_camera_rig_track_to_node);
// Construct first person camera rig spring rotation constraint // Construct first person camera rig spring rotation constraint
spring_rotation_constraint first_person_camera_rig_spring_rotation; spring_rotation_constraint first_person_camera_rig_spring_rotation;
first_person_camera_rig_spring_rotation.spring = 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; 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.active = true;
first_person_camera_rig_spring_rotation_node.weight = 1.0f; 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(); first_person_camera_rig_spring_rotation_eid = ctx.entity_registry->create();
ctx.entity_registry->emplace<spring_rotation_constraint>(first_person_camera_rig_spring_rotation_eid, first_person_camera_rig_spring_rotation); ctx.entity_registry->emplace<spring_rotation_constraint>(first_person_camera_rig_spring_rotation_eid, first_person_camera_rig_spring_rotation);
ctx.entity_registry->emplace<constraint_stack_node_component>(first_person_camera_rig_spring_rotation_eid, first_person_camera_rig_spring_rotation_node); ctx.entity_registry->emplace<constraint_stack_node_component>(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 first_person_camera_rig_translation_spring_angular_frequency
}; };
constraint_stack_node_component first_person_camera_rig_spring_translation_node; 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.weight = 1.0f;
first_person_camera_rig_spring_translation_node.next = first_person_camera_rig_spring_rotation_eid; 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(); 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.world = first_person_camera_rig_transform.local;
first_person_camera_rig_transform.warp = true; 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 // Construct first person camera rig camera component
camera_component first_person_camera_rig_camera; camera_component first_person_camera_rig_camera;
first_person_camera_rig_camera.object = ctx.surface_camera.get(); 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(); first_person_camera_rig_eid = ctx.entity_registry->create();
ctx.entity_registry->emplace<camera_component>(first_person_camera_rig_eid, first_person_camera_rig_camera); ctx.entity_registry->emplace<camera_component>(first_person_camera_rig_eid, first_person_camera_rig_camera);
ctx.entity_registry->emplace<transform_component>(first_person_camera_rig_eid, first_person_camera_rig_transform); ctx.entity_registry->emplace<transform_component>(first_person_camera_rig_eid, first_person_camera_rig_transform);
ctx.entity_registry->emplace<physics_component>(first_person_camera_rig_eid, first_person_camera_rig_physics);
ctx.entity_registry->emplace<legged_locomotion_component>(first_person_camera_rig_eid, first_person_camera_rig_locomotion);
ctx.entity_registry->emplace<constraint_stack_component>(first_person_camera_rig_eid, first_person_camera_rig_constraint_stack); ctx.entity_registry->emplace<constraint_stack_component>(first_person_camera_rig_eid, first_person_camera_rig_constraint_stack);
// Construct first person camera rig fov spring // Construct first person camera rig fov spring
@ -410,7 +432,8 @@ void nest_selection_state::setup_controls()
mouse_look = true; 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) if (!ctx.toggle_mouse_look && mouse_look)
{ {
mouse_look = false; 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<spring_rotation_constraint>(first_person_camera_rig_spring_rotation_eid);
const math::quaternion<float> 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<legged_locomotion_component>
(
first_person_camera_rig_eid,
[&](auto& component)
{
component.force = force;
}
);
};
auto stop_first_person_camera_rig = [&]()
{
ctx.entity_registry->patch<legged_locomotion_component>
(
first_person_camera_rig_eid,
[&](auto& component)
{
component.force = {0.0f, 0.0f, 0.0f};
}
);
};
// Move forward // Move forward
action_subscriptions.emplace_back action_subscriptions.emplace_back
( (
ctx.move_forward_action.get_active_channel().subscribe 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 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 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 action_subscriptions.emplace_back
( (
ctx.move_right_action.get_active_channel().subscribe 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<physics_component>
(
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<physics_component>
(
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<constraint_stack_node_component>
(
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) [&](const auto& event)
{ {
move_first_person_camera_rig({1, 0}, event.input_value);
ctx.entity_registry->patch<constraint_stack_node_component>
(
first_person_camera_rig_track_to_eid,
[&](auto& component)
{
component.active = false;
}
);
} }
) )
); );

+ 3
- 0
src/game/states/nest-selection-state.hpp View File

@ -47,9 +47,12 @@ private:
bool mouse_look{false}; bool mouse_look{false};
entity::id worker_ant_eid;
entity::id first_person_camera_rig_eid; entity::id first_person_camera_rig_eid;
entity::id first_person_camera_rig_spring_translation_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_spring_rotation_eid;
entity::id first_person_camera_rig_track_to_eid;
entity::id first_person_camera_rig_fov_spring_eid; entity::id first_person_camera_rig_fov_spring_eid;
float first_person_camera_rig_translation_spring_angular_frequency; float first_person_camera_rig_translation_spring_angular_frequency;
float first_person_camera_rig_rotation_spring_angular_frequency; float first_person_camera_rig_rotation_spring_angular_frequency;

+ 2
- 8
src/game/states/nuptial-flight-state.cpp View File

@ -27,11 +27,9 @@
#include "game/systems/atmosphere-system.hpp" #include "game/systems/atmosphere-system.hpp"
#include "game/systems/collision-system.hpp" #include "game/systems/collision-system.hpp"
#include "game/components/ant-caste-component.hpp" #include "game/components/ant-caste-component.hpp"
#include "game/components/locomotion-component.hpp"
#include "game/components/transform-component.hpp" #include "game/components/transform-component.hpp"
#include "game/components/terrain-component.hpp" #include "game/components/terrain-component.hpp"
#include "game/components/camera-component.hpp" #include "game/components/camera-component.hpp"
#include "game/components/model-component.hpp"
#include "game/components/name-component.hpp" #include "game/components/name-component.hpp"
#include "game/components/constraint-stack-component.hpp" #include "game/components/constraint-stack-component.hpp"
#include "game/components/steering-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<text_file>("female-names-en.txt"); female_name_pool = ctx.resource_manager->load<text_file>("female-names-en.txt");
male_name_pool = ctx.resource_manager->load<text_file>("male-names-en.txt"); male_name_pool = ctx.resource_manager->load<text_file>("male-names-en.txt");
// Init RNG
std::random_device random_device;
std::mt19937 rng(random_device());
// Assign random ant names // Assign random ant names
std::uniform_int_distribution<> female_name_pool_distribution(0, static_cast<int>(female_name_pool->lines.size() - 1)); std::uniform_int_distribution<> female_name_pool_distribution(0, static_cast<int>(female_name_pool->lines.size() - 1));
std::uniform_int_distribution<> male_name_pool_distribution(0, static_cast<int>(male_name_pool->lines.size() - 1)); std::uniform_int_distribution<> male_name_pool_distribution(0, static_cast<int>(male_name_pool->lines.size() - 1));
@ -126,7 +120,7 @@ nuptial_flight_state::nuptial_flight_state(::game& ctx):
ctx.entity_registry->emplace_or_replace<name_component> ctx.entity_registry->emplace_or_replace<name_component>
( (
entity_id, entity_id,
male_name_pool->lines[male_name_pool_distribution(rng)]
male_name_pool->lines[male_name_pool_distribution(ctx.rng)]
); );
} }
else else
@ -134,7 +128,7 @@ nuptial_flight_state::nuptial_flight_state(::game& ctx):
ctx.entity_registry->emplace_or_replace<name_component> ctx.entity_registry->emplace_or_replace<name_component>
( (
entity_id, entity_id,
female_name_pool->lines[female_name_pool_distribution(rng)]
female_name_pool->lines[female_name_pool_distribution(ctx.rng)]
); );
} }
} }

+ 28
- 29
src/game/systems/astronomy-system.cpp View File

@ -124,40 +124,39 @@ void astronomy_system::update(float t, float dt)
update_icrf_to_eus(*reference_body, *reference_orbit); update_icrf_to_eus(*reference_body, *reference_orbit);
// Set the transform component translations of orbiting bodies to their topocentric positions // Set the transform component translations of orbiting bodies to their topocentric positions
registry.view<celestial_body_component, orbit_component, transform_component>().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<double> 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<double> rotation_eus = math::normalize(icrf_to_eus.r * rotation_icrf);
// Update local transform
if (orbit.parent != entt::null)
registry.view<celestial_body_component, orbit_component, transform_component>().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<double> 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<double> rotation_eus = math::normalize(icrf_to_eus.r * rotation_icrf);
// Update local transform
transform.local.translation = math::normalize(float3(r_eus)); transform.local.translation = math::normalize(float3(r_eus));
transform.local.rotation = math::quaternion<float>(rotation_eus); transform.local.rotation = math::quaternion<float>(rotation_eus);
transform.local.scale = {1.0f, 1.0f, 1.0f}; transform.local.scale = {1.0f, 1.0f, 1.0f};
} }
});
);
constexpr double3 bounce_normal = {0, 1, 0}; constexpr double3 bounce_normal = {0, 1, 0};
double3 bounce_illuminance = {0, 0, 0}; double3 bounce_illuminance = {0, 0, 0};

+ 12
- 12
src/game/systems/constraint-system.cpp View File

@ -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) void constraint_system::handle_constraint(transform_component& transform, entity::id constraint_eid, float dt)
{ {
if (auto constraint = registry.try_get<copy_translation_constraint>(constraint_eid); constraint)
if (auto constraint = registry.try_get<copy_translation_constraint>(constraint_eid))
handle_copy_translation_constraint(transform, *constraint); handle_copy_translation_constraint(transform, *constraint);
else if (auto constraint = registry.try_get<copy_rotation_constraint>(constraint_eid); constraint)
else if (auto constraint = registry.try_get<copy_rotation_constraint>(constraint_eid))
handle_copy_rotation_constraint(transform, *constraint); handle_copy_rotation_constraint(transform, *constraint);
else if (auto constraint = registry.try_get<copy_scale_constraint>(constraint_eid); constraint)
else if (auto constraint = registry.try_get<copy_scale_constraint>(constraint_eid))
handle_copy_scale_constraint(transform, *constraint); handle_copy_scale_constraint(transform, *constraint);
else if (auto constraint = registry.try_get<copy_transform_constraint>(constraint_eid); constraint)
else if (auto constraint = registry.try_get<copy_transform_constraint>(constraint_eid))
handle_copy_transform_constraint(transform, *constraint); handle_copy_transform_constraint(transform, *constraint);
else if (auto constraint = registry.try_get<track_to_constraint>(constraint_eid); constraint)
else if (auto constraint = registry.try_get<track_to_constraint>(constraint_eid))
handle_track_to_constraint(transform, *constraint); handle_track_to_constraint(transform, *constraint);
else if (auto constraint = registry.try_get<three_dof_constraint>(constraint_eid); constraint)
else if (auto constraint = registry.try_get<three_dof_constraint>(constraint_eid))
handle_three_dof_constraint(transform, *constraint); handle_three_dof_constraint(transform, *constraint);
else if (auto constraint = registry.try_get<pivot_constraint>(constraint_eid); constraint)
else if (auto constraint = registry.try_get<pivot_constraint>(constraint_eid))
handle_pivot_constraint(transform, *constraint); handle_pivot_constraint(transform, *constraint);
else if (auto constraint = registry.try_get<child_of_constraint>(constraint_eid); constraint)
else if (auto constraint = registry.try_get<child_of_constraint>(constraint_eid))
handle_child_of_constraint(transform, *constraint); handle_child_of_constraint(transform, *constraint);
else if (auto constraint = registry.try_get<spring_to_constraint>(constraint_eid); constraint)
else if (auto constraint = registry.try_get<spring_to_constraint>(constraint_eid))
handle_spring_to_constraint(transform, *constraint, dt); handle_spring_to_constraint(transform, *constraint, dt);
else if (auto constraint = registry.try_get<spring_translation_constraint>(constraint_eid); constraint)
else if (auto constraint = registry.try_get<spring_translation_constraint>(constraint_eid))
handle_spring_translation_constraint(transform, *constraint, dt); handle_spring_translation_constraint(transform, *constraint, dt);
else if (auto constraint = registry.try_get<spring_rotation_constraint>(constraint_eid); constraint)
else if (auto constraint = registry.try_get<spring_rotation_constraint>(constraint_eid))
handle_spring_rotation_constraint(transform, *constraint, dt); handle_spring_rotation_constraint(transform, *constraint, dt);
else if (auto constraint = registry.try_get<ease_to_constraint>(constraint_eid); constraint)
else if (auto constraint = registry.try_get<ease_to_constraint>(constraint_eid))
handle_ease_to_constraint(transform, *constraint, dt); handle_ease_to_constraint(transform, *constraint, dt);
} }

+ 22
- 8
src/game/systems/locomotion-system.cpp View File

@ -18,11 +18,11 @@
*/ */
#include "game/systems/locomotion-system.hpp" #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 <engine/entity/id.hpp> #include <engine/entity/id.hpp>
#include <algorithm>
#include <execution>
locomotion_system::locomotion_system(entity::registry& registry): locomotion_system::locomotion_system(entity::registry& registry):
updatable_system(registry) updatable_system(registry)
@ -30,9 +30,23 @@ locomotion_system::locomotion_system(entity::registry& registry):
void locomotion_system::update(float t, float dt) void locomotion_system::update(float t, float dt)
{ {
registry.view<transform_component, locomotion_component>().each(
[&](entity::id entity_id, auto& transform, auto& locomotion)
auto group = registry.group<legged_locomotion_component>(entt::get<physics_component>);
std::for_each
(
std::execution::par_unseq,
group.begin(),
group.end(),
[&](auto entity_id)
{ {
});
const auto& locomotion = group.get<legged_locomotion_component>(entity_id);
auto& body = group.get<physics_component>(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;
}
);
} }

+ 4
- 3
src/game/systems/locomotion-system.hpp View File

@ -22,14 +22,15 @@
#include "game/systems/updatable-system.hpp" #include "game/systems/updatable-system.hpp"
/**
*
*/
class locomotion_system: class locomotion_system:
public updatable_system public updatable_system
{ {
public: public:
explicit locomotion_system(entity::registry& registry); 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 #endif // ANTKEEPER_GAME_LOCOMOTION_SYSTEM_HPP

+ 13
- 11
src/game/systems/orbit-system.cpp View File

@ -50,19 +50,21 @@ void orbit_system::update(float t, float dt)
positions[i] = ephemeris->trajectories[i].position(time) * 1000.0; positions[i] = ephemeris->trajectories[i].position(time) * 1000.0;
// Propagate orbits // Propagate orbits
registry.view<orbit_component>().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<orbit_component>().each
(
[&](entity::id entity_eid, auto& orbit)
{ {
const orbit_component& parent_orbit = registry.get<orbit_component>(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<orbit_component>(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<physics::orbit::ephemeris<double>> ephemeris) void orbit_system::set_ephemeris(std::shared_ptr<physics::orbit::ephemeris<double>> ephemeris)

+ 63
- 0
src/game/systems/physics-system.cpp View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include "game/systems/physics-system.hpp"
#include "game/components/transform-component.hpp"
#include "game/components/physics-component.hpp"
#include <engine/entity/id.hpp>
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<physics_component>(entt::get<transform_component>);
for (auto entity_id: group)
{
auto& body = group.get<physics_component>(entity_id);
// Air resistance
const float air_drag_coef = 0.58f;
body.force -= body.velocity * air_drag_coef * body.mass;
// Gravity
const math::vector<float, 3> 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<float, 3>::zero();
}
}

+ 50
- 0
src/game/systems/physics-system.hpp View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_GAME_PHYSICS_SYSTEM_HPP
#define ANTKEEPER_GAME_PHYSICS_SYSTEM_HPP
#include "game/systems/updatable-system.hpp"
#include <entt/entt.hpp>
#include <engine/math/vector.hpp>
/**
*
*/
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<float, 3> gravity{0.0f, -9.80665f, 0.0f};
};
#endif // ANTKEEPER_GAME_PHYSICS_SYSTEM_HPP

+ 62
- 167
src/game/systems/render-system.cpp View File

@ -20,34 +20,30 @@
#include "game/systems/render-system.hpp" #include "game/systems/render-system.hpp"
#include "game/components/transform-component.hpp" #include "game/components/transform-component.hpp"
#include "game/components/camera-component.hpp" #include "game/components/camera-component.hpp"
#include <engine/scene/point-light.hpp>
#include <engine/scene/directional-light.hpp>
#include <engine/scene/ambient-light.hpp>
#include <engine/scene/spot-light.hpp>
#include <algorithm>
#include <execution>
render_system::render_system(entity::registry& registry): render_system::render_system(entity::registry& registry):
updatable_system(registry), updatable_system(registry),
updated_scene_transforms(registry, entt::collector.update<transform_component>().where<scene_component>()),
t(0.0), t(0.0),
dt(0.0), dt(0.0),
renderer(nullptr) renderer(nullptr)
{ {
registry.on_construct<model_component>().connect<&render_system::on_model_construct>(this);
registry.on_update<model_component>().connect<&render_system::on_model_update>(this);
registry.on_destroy<model_component>().connect<&render_system::on_model_destroy>(this);
registry.on_construct<light_component>().connect<&render_system::on_light_construct>(this);
registry.on_update<light_component>().connect<&render_system::on_light_update>(this);
registry.on_destroy<light_component>().connect<&render_system::on_light_destroy>(this);
registry.on_construct<scene_component>().connect<&render_system::on_scene_construct>(this);
registry.on_update<scene_component>().connect<&render_system::on_scene_update>(this);
registry.on_destroy<scene_component>().connect<&render_system::on_scene_destroy>(this);
registry.on_construct<transform_component>().connect<&render_system::on_transform_construct>(this);
} }
render_system::~render_system() render_system::~render_system()
{ {
registry.on_construct<model_component>().disconnect<&render_system::on_model_construct>(this);
registry.on_update<model_component>().disconnect<&render_system::on_model_update>(this);
registry.on_destroy<model_component>().disconnect<&render_system::on_model_destroy>(this);
registry.on_construct<light_component>().disconnect<&render_system::on_light_construct>(this);
registry.on_update<light_component>().disconnect<&render_system::on_light_update>(this);
registry.on_destroy<light_component>().disconnect<&render_system::on_light_destroy>(this);
registry.on_construct<scene_component>().disconnect<&render_system::on_scene_construct>(this);
registry.on_update<scene_component>().disconnect<&render_system::on_scene_update>(this);
registry.on_destroy<scene_component>().disconnect<&render_system::on_scene_destroy>(this);
registry.on_construct<transform_component>().disconnect<&render_system::on_transform_construct>(this);
} }
void render_system::update(float t, float dt) void render_system::update(float t, float dt)
@ -55,22 +51,27 @@ void render_system::update(float t, float dt)
this->t = t; this->t = t;
this->dt = dt; this->dt = dt;
// Update model instance transforms
registry.view<transform_component, model_component>().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<transform_component>(entity_id);
const auto& scene = registry.get<scene_component>(entity_id);
// WARNING: could potentially lead to multithreading issues with scene::object_base::transformed()
scene.object->set_transform(transform.world);
if (transform.warp) if (transform.warp)
{ {
instance.get_transform_tween().update();
instance.update_tweens();
scene.object->get_transform_tween().update();
transform.warp = false; transform.warp = false;
} }
} }
); );
updated_scene_transforms.clear();
// Update camera transforms // Update camera transforms
registry.view<transform_component, camera_component>().each registry.view<transform_component, camera_component>().each
@ -86,23 +87,6 @@ void render_system::update(float t, float dt)
} }
} }
); );
// Update light transforms
registry.view<transform_component, light_component>().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) void render_system::draw(float alpha)
@ -131,154 +115,65 @@ void render_system::set_renderer(render::renderer* renderer)
this->renderer = renderer; this->renderer = renderer;
} }
scene::model_instance* render_system::get_model_instance(entity::id entity_id)
void pan class="n">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<transform_component>(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<std::size_t>(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<std::uint8_t>(1 << i))
{ {
case scene::light_type::point:
{
scene::point_light& point = static_cast<scene::point_light&>(light);
point.set_attenuation(component.attenuation);
break;
}
case scene::light_type::spot:
{
scene::spot_light& spot = static_cast<scene::spot_light&>(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<scene::model_instance>();
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<std::uint8_t>(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<scene::light> 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<scene::ambient_light>();
break;
case scene::light_type::directional:
light = std::make_unique<scene::directional_light>();
break;
case scene::light_type::point:
light = std::make_unique<scene::point_light>();
break;
case scene::light_type::spot:
light = std::make_unique<scene::spot_light>();
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<std::uint8_t>(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<scene_component>(entity_id))
{ {
for (scene::collection* layer: layers)
layer->remove_object(it->second.get());
const auto& transform = registry.get<transform_component>(entity_id);
lights.erase(it);
scene->object->set_transform(transform.world);
scene->object->get_transform_tween().update();
} }
} }

+ 9
- 18
src/game/systems/render-system.hpp View File

@ -22,10 +22,9 @@
#include "game/systems/updatable-system.hpp" #include "game/systems/updatable-system.hpp"
#include <engine/scene/collection.hpp> #include <engine/scene/collection.hpp>
#include <engine/scene/model-instance.hpp>
#include <engine/scene/static-mesh.hpp>
#include <engine/scene/light.hpp> #include <engine/scene/light.hpp>
#include "game/components/model-component.hpp"
#include "game/components/light-component.hpp"
#include "game/components/scene-component.hpp"
#include <engine/entity/id.hpp> #include <engine/entity/id.hpp>
#include <engine/render/renderer.hpp> #include <engine/render/renderer.hpp>
#include <unordered_map> #include <unordered_map>
@ -42,32 +41,24 @@ public:
void draw(float alpha); void draw(float alpha);
void add_layer(scene::collection* layer); void add_layer(scene::collection* layer);
void remove_layers(); void remove_layers();
void set_renderer(::render::renderer* renderer); 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 t;
float dt; float dt;
::render::renderer* renderer; ::render::renderer* renderer;
std::vector<scene::collection*> layers; std::vector<scene::collection*> layers;
std::unordered_map<entity::id, std::unique_ptr<scene::model_instance>> model_instances;
std::unordered_map<entity::id, std::unique_ptr<scene::light>> lights;
}; };

+ 14
- 8
src/game/systems/spatial-system.cpp View File

@ -20,7 +20,8 @@
#include "game/systems/spatial-system.hpp" #include "game/systems/spatial-system.hpp"
#include "game/components/transform-component.hpp" #include "game/components/transform-component.hpp"
#include "game/components/constraint-stack-component.hpp" #include "game/components/constraint-stack-component.hpp"
#include <algorithm>
#include <execution>
spatial_system::spatial_system(entity::registry& registry): spatial_system::spatial_system(entity::registry& registry):
updatable_system(registry), updatable_system(registry),
@ -29,12 +30,17 @@ spatial_system::spatial_system(entity::registry& registry):
void spatial_system::update(float t, float dt) 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_component>(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<transform_component>(entity_id);
transform.world = transform.local;
}
);
updated_unconstrained_transforms.clear(); updated_unconstrained_transforms.clear();
} }

+ 95
- 96
src/game/systems/steering-system.cpp View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include "game/systems/steering-system.hpp"
#include "game/components/steering-component.hpp"
#include "game/components/transform-component.hpp"
#include <engine/entity/id.hpp>
#include <engine/ai/steering/behavior/wander.hpp>
#include <engine/ai/steering/behavior/seek.hpp>
#include <engine/math/quaternion.hpp>
#include <engine/config.hpp>
steering_system::steering_system(entity::registry& registry):
updatable_system(registry)
{}
void steering_system::update(float t, float dt)
{
registry.view<steering_component, transform_component>().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 <http://www.gnu.org/licenses/>.
*/
#include "game/systems/steering-system.hpp"
#include "game/components/steering-component.hpp"
#include "game/components/transform-component.hpp"
#include <engine/entity/id.hpp>
#include <engine/ai/steering/behavior/wander.hpp>
#include <engine/ai/steering/behavior/seek.hpp>
#include <engine/math/quaternion.hpp>
#include <engine/config.hpp>
steering_system::steering_system(entity::registry& registry):
updatable_system(registry)
{}
void steering_system::update(float t, float dt)
{
registry.group<steering_component>(entt::get<transform_component>).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;
}
);
}
);
}

+ 2
- 3
src/game/systems/subterrain-system.cpp View File

@ -18,7 +18,6 @@
*/ */
#include "game/systems/subterrain-system.hpp" #include "game/systems/subterrain-system.hpp"
#include "game/components/model-component.hpp"
#include "game/components/cavity-component.hpp" #include "game/components/cavity-component.hpp"
#include <engine/entity/id.hpp> #include <engine/entity/id.hpp>
#include <engine/render/model.hpp> #include <engine/render/model.hpp>
@ -297,8 +296,8 @@ void subterrain_system::update(float t, float dt)
//auto subterrain_entity = registry.create(); //auto subterrain_entity = registry.create();
//registry.assign<model_component>(subterrain_entity, subterrain_model); //registry.assign<model_component>(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; bool digging = false;

+ 2
- 2
src/game/systems/subterrain-system.hpp View File

@ -24,7 +24,7 @@
#include <engine/geom/mesh.hpp> #include <engine/geom/mesh.hpp>
#include <engine/geom/aabb.hpp> #include <engine/geom/aabb.hpp>
#include <engine/scene/collection.hpp> #include <engine/scene/collection.hpp>
#include <engine/scene/model-instance.hpp>
#include <engine/scene/static-mesh.hpp>
#include <engine/render/model.hpp> #include <engine/render/model.hpp>
#include <engine/utility/fundamental-types.hpp> #include <engine/utility/fundamental-types.hpp>
#include <unordered_map> #include <unordered_map>
@ -122,7 +122,7 @@ private:
vector_equals<epsilon_1en5, float, 3>> subterrain_vertex_map; vector_equals<epsilon_1en5, float, 3>> subterrain_vertex_map;
scene::collection* collection; scene::collection* collection;
scene::model_instance* subterrain_model_instance;
scene::static_mesh* subterrain_static_mesh;
}; };

+ 3
- 3
src/game/systems/terrain-system.cpp View File

@ -107,7 +107,7 @@ void terrain_system::update(float t, float dt)
{ {
patch* node_patch = generate_patch(node); patch* node_patch = generate_patch(node);
patches[node] = node_patch; 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) for (auto it = patches.begin(); it != patches.end(); ++it)
{ {
bool active = (quadtree.contains(it->first) && quadtree.is_leaf(it->first)); 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(); patch* node_patch = new patch();
node_patch->mesh = nullptr;//generate_patch_mesh(node); node_patch->mesh = nullptr;//generate_patch_mesh(node);
node_patch->model = generate_patch_model(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 node_patch;
*/ */
return nullptr; return nullptr;

+ 2
- 2
src/game/systems/terrain-system.hpp View File

@ -29,7 +29,7 @@
#include <engine/utility/fundamental-types.hpp> #include <engine/utility/fundamental-types.hpp>
#include <engine/render/model.hpp> #include <engine/render/model.hpp>
#include <engine/render/material.hpp> #include <engine/render/material.hpp>
#include <engine/scene/model-instance.hpp>
#include <engine/scene/static-mesh.hpp>
#include <engine/scene/collection.hpp> #include <engine/scene/collection.hpp>
#include <engine/geom/view-frustum.hpp> #include <engine/geom/view-frustum.hpp>
#include <unordered_map> #include <unordered_map>
@ -90,7 +90,7 @@ private:
{ {
geom::mesh* mesh; geom::mesh* mesh;
::render::model* model; ::render::model* model;
scene::model_instance* model_instance;
scene::static_mesh* static_mesh;
}; };
void on_terrain_construct(entity::registry& registry, entity::id entity_id); void on_terrain_construct(entity::registry& registry, entity::id entity_id);

Loading…
Cancel
Save