Browse Source

Improve constraint system and add more constraint types. Improve nuptial flight camera. Improve spatial system and atmosphere system. Update EnTT to version 3.10.3

master
C. J. Howard 2 years ago
parent
commit
ce5c8b6743
68 changed files with 2411 additions and 1220 deletions
  1. +8
    -8
      src/animation/spring.hpp
  2. +3
    -3
      src/color/color.hpp
  3. +1
    -1
      src/color/index.hpp
  4. +1
    -1
      src/color/rgb.hpp
  5. +1
    -1
      src/color/ucs.hpp
  6. +1
    -1
      src/color/xyy.hpp
  7. +1
    -1
      src/color/xyz.hpp
  8. +18
    -17
      src/entity/archetype.cpp
  9. +23
    -3
      src/entity/archetype.hpp
  10. +13
    -11
      src/entity/clone.cpp
  11. +39
    -0
      src/entity/clone.hpp
  12. +80
    -45
      src/entity/commands.cpp
  13. +0
    -1
      src/entity/commands.hpp
  14. +11
    -2
      src/entity/ebt.cpp
  15. +27
    -14
      src/game/ant/swarm.cpp
  16. +3
    -2
      src/game/ant/swarm.hpp
  17. +3
    -0
      src/game/component/constraint-stack.hpp
  18. +43
    -0
      src/game/component/constraint/child-of.hpp
  19. +44
    -0
      src/game/component/constraint/constraint.hpp
  20. +0
    -0
      src/game/component/constraint/copy-rotation.hpp
  21. +0
    -0
      src/game/component/constraint/copy-scale.hpp
  22. +0
    -0
      src/game/component/constraint/copy-transform.hpp
  23. +0
    -0
      src/game/component/constraint/copy-translation.hpp
  24. +46
    -0
      src/game/component/constraint/pivot.hpp
  25. +43
    -0
      src/game/component/constraint/spring-rotation.hpp
  26. +0
    -0
      src/game/component/constraint/spring-to.hpp
  27. +43
    -0
      src/game/component/constraint/spring-translation.hpp
  28. +1
    -1
      src/game/component/constraint/three-dof.hpp
  29. +0
    -0
      src/game/component/constraint/track-to.hpp
  30. +2
    -3
      src/game/context.hpp
  31. +4
    -11
      src/game/controls.cpp
  32. +0
    -3
      src/game/controls.hpp
  33. +11
    -11
      src/game/loop.cpp
  34. +20
    -4
      src/game/loop.hpp
  35. +3
    -3
      src/game/state/boot.cpp
  36. +2
    -2
      src/game/state/main-menu.cpp
  37. +3
    -0
      src/game/state/main-menu.hpp
  38. +1028
    -0
      src/game/state/nest-selection.cpp
  39. +49
    -0
      src/game/state/nest-selection.hpp
  40. +296
    -668
      src/game/state/nuptial-flight.cpp
  41. +13
    -6
      src/game/state/nuptial-flight.hpp
  42. +12
    -12
      src/game/system/astronomy.cpp
  43. +4
    -4
      src/game/system/astronomy.hpp
  44. +69
    -33
      src/game/system/atmosphere.cpp
  45. +12
    -3
      src/game/system/atmosphere.hpp
  46. +22
    -24
      src/game/system/blackbody.cpp
  47. +5
    -4
      src/game/system/blackbody.hpp
  48. +8
    -4
      src/game/system/collision.cpp
  49. +2
    -2
      src/game/system/collision.hpp
  50. +208
    -115
      src/game/system/constraint.cpp
  51. +16
    -8
      src/game/system/constraint.hpp
  52. +6
    -4
      src/game/system/orbit.cpp
  53. +2
    -2
      src/game/system/orbit.hpp
  54. +13
    -5
      src/game/system/proteome.cpp
  55. +3
    -2
      src/game/system/proteome.hpp
  56. +25
    -10
      src/game/system/render.cpp
  57. +6
    -5
      src/game/system/render.hpp
  58. +11
    -26
      src/game/system/spatial.cpp
  59. +5
    -0
      src/game/system/spatial.hpp
  60. +9
    -2
      src/game/system/steering.cpp
  61. +5
    -2
      src/game/system/terrain.cpp
  62. +1
    -1
      src/game/system/terrain.hpp
  63. +5
    -2
      src/game/system/vegetation.cpp
  64. +1
    -1
      src/game/system/vegetation.hpp
  65. +0
    -108
      src/game/tools.cpp
  66. +6
    -2
      src/game/world.cpp
  67. +2
    -2
      src/render/material-property.hpp
  68. +69
    -14
      src/resources/entity-archetype-loader.cpp

+ 8
- 8
src/animation/spring.hpp View File

@ -40,7 +40,7 @@ struct numeric_spring
}; };
/** /**
* Performs numeric, damped springing on a value and velocity.
* Solves a number spring using the implicit Euler method.
* *
* @tparam T Value type. * @tparam T Value type.
* @tparam S Scalar type. * @tparam S Scalar type.
@ -56,7 +56,7 @@ template
void spring(T& x0, T& v, const T& x1, S z, S w, S dt); void spring(T& x0, T& v, const T& x1, S z, S w, S dt);
/** /**
* Solves a numeric spring using the spring() function.
* Solves a number spring using the implicit Euler method.
* *
* @param[in,out] ns Numeric spring to be sovled. * @param[in,out] ns Numeric spring to be sovled.
* @param dt Delta time, in seconds. * @param dt Delta time, in seconds.
@ -87,12 +87,12 @@ T rads_to_hz(T rads);
template <typename T, typename S> template <typename T, typename S>
void spring(T& x0, T& v, const T& x1, S z, S w, S dt) void spring(T& x0, T& v, const T& x1, S z, S w, S dt)
{ {
const S w2_dt = w * w * dt;
const S w2_dt2 = w2_dt * dt;
const S f = z * w * dt * S(2) + S(1);
const T det_x = x0 * f + v * dt + x1 * w2_dt2;
const T det_v = v + (x1 - x0) * w2_dt;
const S inv_det = S(1) / (f + w2_dt2);
const S ww_dt = w * w * dt;
const S ww_dtdt = ww_dt * dt;
const S f = z * w * dt * S{2} + S{1};
const T det_x = x0 * f + v * dt + x1 * ww_dtdt;
const T det_v = v + (x1 - x0) * ww_dt;
const S inv_det = S{1} / (f + ww_dtdt);
x0 = det_x * inv_det; x0 = det_x * inv_det;
v = det_v * inv_det; v = det_v * inv_det;

+ 3
- 3
src/color/color.hpp View File

@ -20,18 +20,18 @@
#ifndef ANTKEEPER_COLOR_HPP #ifndef ANTKEEPER_COLOR_HPP
#define ANTKEEPER_COLOR_HPP #define ANTKEEPER_COLOR_HPP
/// Color manipulation functions.
/// Color manipulation.
namespace color {} namespace color {}
#include "aces.hpp" #include "aces.hpp"
#include "cat.hpp"
#include "cct.hpp" #include "cct.hpp"
#include "illuminant.hpp"
#include "index.hpp" #include "index.hpp"
#include "rgb.hpp" #include "rgb.hpp"
#include "srgb.hpp" #include "srgb.hpp"
#include "ucs.hpp" #include "ucs.hpp"
#include "xyy.hpp" #include "xyy.hpp"
#include "xyz.hpp" #include "xyz.hpp"
#include "cat.hpp"
#include "illuminant.hpp"
#endif // ANTKEEPER_COLOR_HPP #endif // ANTKEEPER_COLOR_HPP

+ 1
- 1
src/color/index.hpp View File

@ -22,7 +22,7 @@
namespace color { namespace color {
/// Functions which operate on color indices.
/// Color indices.
namespace index { namespace index {
/** /**

+ 1
- 1
src/color/rgb.hpp View File

@ -25,7 +25,7 @@
namespace color { namespace color {
/// Functions which operate on various RGB colorspaces.
/// RGB color spaces.
namespace rgb { namespace rgb {
/** /**

+ 1
- 1
src/color/ucs.hpp View File

@ -24,7 +24,7 @@
namespace color { namespace color {
/// Functions which operate in the CIE 1960 UCS colorspace.
/// CIE 1960 UCS color space.
namespace ucs { namespace ucs {
/** /**

+ 1
- 1
src/color/xyy.hpp View File

@ -24,7 +24,7 @@
namespace color { namespace color {
/// Functions which operate in the CIE xyY colorspace.
/// CIE xyY color space.
namespace xyy { namespace xyy {
/** /**

+ 1
- 1
src/color/xyz.hpp View File

@ -25,7 +25,7 @@
namespace color { namespace color {
/** /**
* Functions which operate in the CIE XYZ color space.
* CIE XYZ color space.
* *
* @see https://en.wikipedia.org/wiki/CIE_1931_color_space * @see https://en.wikipedia.org/wiki/CIE_1931_color_space
*/ */

src/game/component/constraints/constraints.hpp → src/entity/archetype.cpp View File

@ -17,24 +17,25 @@
* 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_COMPONENT_CONSTRAINTS_HPP
#define ANTKEEPER_GAME_COMPONENT_CONSTRAINTS_HPP
#include "entity/archetype.hpp"
#include "entity/clone.hpp"
namespace game {
namespace component {
namespace entity {
/// Transform constraint components
namespace constraint {}
entity::id archetype::create(entity::registry& registry) const
{
entt::handle instance_handle(registry, registry.create());
for (const auto& function: stamps)
function(instance_handle);
return instance_handle.entity();
}
} // namespace component
} // namespace game
void archetype::stamp(entt::handle& handle) const
{
for (const auto& function: stamps)
function(handle);
}
#include "copy-translation.hpp"
#include "copy-rotation.hpp"
#include "copy-scale.hpp"
#include "copy-transform.hpp"
#include "track-to.hpp"
#include "spring-to.hpp"
#include "three-dof.hpp"
#endif // ANTKEEPER_GAME_COMPONENT_CONSTRAINTS_HPP
} // namespace entity

+ 23
- 3
src/entity/archetype.hpp View File

@ -20,13 +20,33 @@
#ifndef ANTKEEPER_ENTITY_ARCHETYPE_HPP #ifndef ANTKEEPER_ENTITY_ARCHETYPE_HPP
#define ANTKEEPER_ENTITY_ARCHETYPE_HPP #define ANTKEEPER_ENTITY_ARCHETYPE_HPP
#include <entt/entt.hpp>
#include "entity/registry.hpp"
#include "entity/id.hpp"
#include <list>
#include <functional>
namespace entity { namespace entity {
typedef entt::prototype archetype;
/**
* Entity type template.
*/
struct archetype
{
/// List of stamp functions which construct instances of the archetype's components.
std::list<std::function<void(entt::handle&)>> stamps;
/**
* Creates an instance of this archetype.
*
* @param registry Registry in which to create an entity.
*
* @return Entity ID of the created instance.
*/
entity::id create(entity::registry& registry) const;
void stamp(entt::handle& handle) const;
};
} // namespace entity } // namespace entity
#endif // ANTKEEPER_ENTITY_ARCHETYPE_HPP #endif // ANTKEEPER_ENTITY_ARCHETYPE_HPP

src/game/tools.hpp → src/entity/clone.cpp View File

@ -17,17 +17,19 @@
* 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_TOOLS_HPP
#define ANTKEEPER_GAME_TOOLS_HPP
#include "entity/clone.hpp"
#include "game/context.hpp"
#include "entity/id.hpp"
namespace entity {
namespace game {
void clone(entity::registry& registry, entity::id source, entity::id destination)
{
for (auto&& it: registry.storage())
{
if (auto& storage = it.second; storage.contains(source))
{
storage.emplace(destination, storage.get(source));
}
}
}
entity::id build_camera_tool(game::context& ctx);
entity::id build_time_tool(game::context& ctx);
} // namespace game
#endif // ANTKEEPER_GAME_TOOLS_HPP
} // namespace entity

+ 39
- 0
src/entity/clone.hpp View File

@ -0,0 +1,39 @@
/*
* Copyright (C) 2021 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_ENTITY_CLONE_HPP
#define ANTKEEPER_ENTITY_CLONE_HPP
#include "entity/registry.hpp"
#include "entity/id.hpp"
namespace entity {
/**
* Clones all the components of an entity.
*
* @param registry Entity registry.
* @param source Source entity ID.
* @param destination Destination entity ID.
*/
void clone(entity::registry& registry, entity::id source, entity::id destination);
} // namespace entity
#endif // ANTKEEPER_ENTITY_CLONE_HPP

+ 80
- 45
src/entity/commands.cpp View File

@ -31,62 +31,105 @@ namespace command {
void translate(entity::registry& registry, entity::id eid, const float3& translation) void translate(entity::registry& registry, entity::id eid, const float3& translation)
{ {
if (registry.has<game::component::transform>(eid))
const game::component::transform* transform = registry.try_get<game::component::transform>(eid);
if (transform)
{ {
game::component::transform& transform = registry.get<game::component::transform>(eid);
transform.local.translation += translation;
registry.patch<game::component::transform>
(
eid,
[&translation](auto& transform)
{
transform.local.translation += translation;
}
);
} }
} }
void rotate(entity::registry& registry, entity::id eid, float angle, const float3& axis) void rotate(entity::registry& registry, entity::id eid, float angle, const float3& axis)
{ {
if (registry.has<game::component::transform>(eid))
const game::component::transform* transform = registry.try_get<game::component::transform>(eid);
if (transform)
{ {
game::component::transform& transform = registry.get<game::component::transform>(eid);
transform.local.rotation = math::angle_axis(angle, axis) * transform.local.rotation;
registry.patch<game::component::transform>
(
eid,
[angle, &axis](auto& transform)
{
transform.local.rotation = math::normalize(math::angle_axis(angle, axis) * transform.local.rotation);
}
);
} }
} }
void move_to(entity::registry& registry, entity::id eid, const float3& position) void move_to(entity::registry& registry, entity::id eid, const float3& position)
{ {
if (registry.has<game::component::transform>(eid))
const game::component::transform* transform = registry.try_get<game::component::transform>(eid);
if (transform)
{ {
game::component::transform& transform = registry.get<game::component::transform>(eid);
transform.local.translation = position;
registry.patch<game::component::transform>
(
eid,
[&position](auto& transform)
{
transform.local.translation = position;
}
);
} }
} }
void warp_to(entity::registry& registry, entity::id eid, const float3& position) void warp_to(entity::registry& registry, entity::id eid, const float3& position)
{ {
if (registry.has<game::component::transform>(eid))
const game::component::transform* transform = registry.try_get<game::component::transform>(eid);
if (transform)
{ {
game::component::transform& transform = registry.get<game::component::transform>(eid);
transform.local.translation = position;
transform.warp = true;
registry.patch<game::component::transform>
(
eid,
[&position](auto& transform)
{
transform.local.translation = position;
transform.warp = true;
}
);
} }
} }
void set_scale(entity::registry& registry, entity::id eid, const float3& scale) void set_scale(entity::registry& registry, entity::id eid, const float3& scale)
{ {
if (registry.has<game::component::transform>(eid))
const game::component::transform* transform = registry.try_get<game::component::transform>(eid);
if (transform)
{ {
game::component::transform& transform = registry.get<game::component::transform>(eid);
transform.local.scale = scale;
registry.patch<game::component::transform>
(
eid,
[&scale](auto& transform)
{
transform.local.scale = scale;
}
);
} }
} }
void set_transform(entity::registry& registry, entity::id eid, const math::transform<float>& transform, bool warp) void set_transform(entity::registry& registry, entity::id eid, const math::transform<float>& transform, bool warp)
{ {
if (registry.has<game::component::transform>(eid))
const game::component::transform* transform_component = registry.try_get<game::component::transform>(eid);
if (transform_component)
{ {
game::component::transform& component = registry.get<game::component::transform>(eid);
component.local = transform;
component.warp = warp;
registry.patch<game::component::transform>
(
eid,
[&other_transform = transform, warp](auto& transform)
{
transform.local = other_transform;
transform.warp = warp;
}
);
} }
} }
void place(entity::registry& registry, entity::id eid, entity::id celestial_body_id, double altitude, double latitude, double longitude) void place(entity::registry& registry, entity::id eid, entity::id celestial_body_id, double altitude, double latitude, double longitude)
{ {
/*
if (registry.has<game::component::transform>(eid)) if (registry.has<game::component::transform>(eid))
{ {
double x = 0.0; double x = 0.0;
@ -115,32 +158,31 @@ void place(entity::registry& registry, entity::id eid, entity::id celestial_body
transform.local.translation = math::type_cast<float>(double3{x, y, z}); transform.local.translation = math::type_cast<float>(double3{x, y, z});
transform.warp = true; transform.warp = true;
} }
*/
} }
void assign_render_layers(entity::registry& registry, entity::id eid, unsigned int layers) void assign_render_layers(entity::registry& registry, entity::id eid, unsigned int layers)
{ {
if (registry.has<game::component::model>(eid))
const game::component::model* model = registry.try_get<game::component::model>(eid);
if (model)
{ {
game::component::model model = registry.get<game::component::model>(eid);
model.layers = layers;
registry.replace<game::component::model>(eid, model);
// Apply to child layers
registry.view<game::component::parent>().each(
[&](entity::id eid, auto& component)
{
if (component.parent == eid)
assign_render_layers(registry, eid, layers);
});
registry.patch<game::component::model>
(
eid,
[layers](auto& model)
{
model.layers = layers;
}
);
} }
} }
math::transform<float> get_local_transform(entity::registry& registry, entity::id eid) math::transform<float> get_local_transform(entity::registry& registry, entity::id eid)
{ {
if (registry.has<game::component::transform>(eid))
const game::component::transform* transform = registry.try_get<game::component::transform>(eid);
if (transform)
{ {
const game::component::transform& component = registry.get<game::component::transform>(eid);
return component.local;
return transform->local;
} }
return math::transform<float>::identity; return math::transform<float>::identity;
@ -148,21 +190,14 @@ math::transform get_local_transform(entity::registry& registry, entity::i
math::transform<float> get_world_transform(entity::registry& registry, entity::id eid) math::transform<float> get_world_transform(entity::registry& registry, entity::id eid)
{ {
if (registry.has<game::component::transform>(eid))
const game::component::transform* transform = registry.try_get<game::component::transform>(eid);
if (transform)
{ {
const game::component::transform& component = registry.get<game::component::transform>(eid);
return component.world;
return transform->world;
} }
return math::transform<float>::identity; return math::transform<float>::identity;
} }
void parent(entity::registry& registry, entity::id child, entity::id parent)
{
game::component::parent component;
component.parent = parent;
registry.assign_or_replace<game::component::parent>(child, component);
}
} // namespace command } // namespace command
} // namespace entity } // namespace entity

+ 0
- 1
src/entity/commands.hpp View File

@ -40,7 +40,6 @@ 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, unsigned int layers);
math::transform<float> get_local_transform(entity::registry& registry, entity::id eid); math::transform<float> get_local_transform(entity::registry& registry, entity::id eid);
math::transform<float> get_world_transform(entity::registry& registry, entity::id eid); math::transform<float> get_world_transform(entity::registry& registry, entity::id eid);
void parent(entity::registry& registry, entity::id child, entity::id parent);
} // namespace command } // namespace command
} // namespace entity } // namespace entity

+ 11
- 2
src/entity/ebt.cpp View File

@ -39,8 +39,17 @@ status print_eid(context& context)
status warp_to(context& context, float x, float y, float z) status warp_to(context& context, float x, float y, float z)
{ {
auto& transform = context.registry->get<game::component::transform>(context.entity_id); auto& transform = context.registry->get<game::component::transform>(context.entity_id);
transform.local.translation = {x, y, z};
transform.warp = true;
context.registry->patch<game::component::transform>
(
context.entity_id,
[x, y, z](auto& component)
{
component.local.translation = {x, y, z};
component.warp = true;
}
);
return status::success; return status::success;
} }

+ 27
- 14
src/game/ant/swarm.cpp View File

@ -50,7 +50,7 @@ static math::vector3 sphere_random(Generator& rng)
return {x * scale, y * scale, z * scale}; return {x * scale, y * scale, z * scale};
} }
void create_swarm(game::context& ctx)
entity::id create_swarm(game::context& ctx)
{ {
// Determine swarm properties // Determine swarm properties
const float3 swarm_center = {0, 100, 0}; const float3 swarm_center = {0, 100, 0};
@ -62,6 +62,19 @@ void create_swarm(game::context& ctx)
const float3 male_scale = {0.5, 0.5, 0.5}; const float3 male_scale = {0.5, 0.5, 0.5};
const float3 queen_scale = {1, 1, 1}; const float3 queen_scale = {1, 1, 1};
// Init transform component
game::component::transform transform;
transform.local = math::transform<float>::identity;
transform.world = transform.local;
transform.warp = true;
// Create swarm entity
entity::id swarm_eid = ctx.entity_registry->create();
transform.local.translation = swarm_center;
transform.world = transform.local;
transform.warp = true;
ctx.entity_registry->emplace<game::component::transform>(swarm_eid, transform);
// Init male model component // Init male model component
game::component::model male_model; game::component::model male_model;
male_model.render_model = ctx.resource_manager->load<render::model>("male-boid.mdl"); male_model.render_model = ctx.resource_manager->load<render::model>("male-boid.mdl");
@ -74,12 +87,6 @@ void create_swarm(game::context& ctx)
queen_model.instance_count = 0; queen_model.instance_count = 0;
queen_model.layers = 1; queen_model.layers = 1;
// Init transform component
game::component::transform transform;
transform.local = math::transform<float>::identity;
transform.world = transform.local;
transform.warp = true;
// Init steering component // Init steering component
game::component::steering steering; game::component::steering steering;
steering.agent.mass = 1.0f; steering.agent.mass = 1.0f;
@ -98,7 +105,7 @@ void create_swarm(game::context& ctx)
steering.wander_angle = 0.0f; steering.wander_angle = 0.0f;
steering.wander_angle2 = 0.0f; steering.wander_angle2 = 0.0f;
steering.seek_weight = 0.2f; steering.seek_weight = 0.2f;
steering.seek_target = {0, 100, 0};
steering.seek_target = swarm_center;
steering.flee_weight = 0.0f; steering.flee_weight = 0.0f;
steering.sum_weights = steering.wander_weight + steering.seek_weight + steering.flee_weight; steering.sum_weights = steering.wander_weight + steering.seek_weight + steering.flee_weight;
@ -114,33 +121,39 @@ void create_swarm(game::context& ctx)
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();
ctx.entity_registry->assign<game::component::steering>(alate_eid, steering);
ctx.entity_registry->emplace<game::component::steering>(alate_eid, steering);
if (i < male_count) if (i < male_count)
{ {
// Create male // Create male
ctx.entity_registry->assign<game::component::model>(alate_eid, male_model);
ctx.entity_registry->emplace<game::component::model>(alate_eid, male_model);
transform.local.scale = male_scale; transform.local.scale = male_scale;
transform.world = transform.local; transform.world = transform.local;
ctx.entity_registry->assign<game::component::transform>(alate_eid, transform);
ctx.entity_registry->emplace<game::component::transform>(alate_eid, transform);
} }
else else
{ {
// Create queen // Create queen
ctx.entity_registry->assign<game::component::model>(alate_eid, queen_model);
ctx.entity_registry->emplace<game::component::model>(alate_eid, queen_model);
transform.local.scale = queen_scale; transform.local.scale = queen_scale;
transform.world = transform.local; transform.world = transform.local;
ctx.entity_registry->assign<game::component::transform>(alate_eid, transform);
ctx.entity_registry->emplace<game::component::transform>(alate_eid, transform);
} }
} }
return swarm_eid;
} }
void destroy_swarm(game::context& ctx)
void destroy_swarm(game::context& ctx, entity::id swarm_eid)
{ {
// Destroy alates
auto view = ctx.entity_registry->view<game::component::steering>(); auto view = ctx.entity_registry->view<game::component::steering>();
ctx.entity_registry->destroy(view.begin(), view.end()); ctx.entity_registry->destroy(view.begin(), view.end());
// Destroy swarm
ctx.entity_registry->destroy(swarm_eid);
} }
} // namespace ant } // namespace ant

+ 3
- 2
src/game/ant/swarm.hpp View File

@ -21,12 +21,13 @@
#define ANTKEEPER_GAME_ANT_SWARM_HPP #define ANTKEEPER_GAME_ANT_SWARM_HPP
#include "game/context.hpp" #include "game/context.hpp"
#include "entity/id.hpp"
namespace game { namespace game {
namespace ant { namespace ant {
void create_swarm(game::context& ctx);
void destroy_swarm(game::context& ctx);
entity::id create_swarm(game::context& ctx);
void destroy_swarm(game::context& ctx, entity::id swarm_eid);
} // namespace ant } // namespace ant
} // namespace game } // namespace game

+ 3
- 0
src/game/component/constraint-stack.hpp View File

@ -33,6 +33,9 @@ namespace component {
*/ */
struct constraint_stack struct constraint_stack
{ {
/// Priority number, with lower priorities evaluated first.
int priority;
/// ID of the entity containing the first constraint stack node. /// ID of the entity containing the first constraint stack node.
entity::id head; entity::id head;
}; };

+ 43
- 0
src/game/component/constraint/child-of.hpp View File

@ -0,0 +1,43 @@
/*
* Copyright (C) 2021 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_COMPONENT_CONSTRAINT_CHILD_OF_HPP
#define ANTKEEPER_GAME_COMPONENT_CONSTRAINT_CHILD_OF_HPP
#include "entity/id.hpp"
#include "utility/fundamental-types.hpp"
namespace game {
namespace component {
namespace constraint {
/**
* Makes the entity a child of the target entity.
*/
struct child_of
{
/// Target entity ID.
entity::id target;
};
} // namespace constraint
} // namespace component
} // namespace game
#endif // ANTKEEPER_GAME_COMPONENT_CONSTRAINT_CHILD_OF_HPP

+ 44
- 0
src/game/component/constraint/constraint.hpp View File

@ -0,0 +1,44 @@
/*
* Copyright (C) 2021 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_COMPONENT_CONSTRAINT_HPP
#define ANTKEEPER_GAME_COMPONENT_CONSTRAINT_HPP
namespace game {
namespace component {
/// Transform constraint components.
namespace constraint {}
} // namespace component
} // namespace game
#include "game/component/constraint/child-of.hpp"
#include "game/component/constraint/copy-rotation.hpp"
#include "game/component/constraint/copy-scale.hpp"
#include "game/component/constraint/copy-transform.hpp"
#include "game/component/constraint/copy-translation.hpp"
#include "game/component/constraint/pivot.hpp"
#include "game/component/constraint/spring-rotation.hpp"
#include "game/component/constraint/spring-to.hpp"
#include "game/component/constraint/spring-translation.hpp"
#include "game/component/constraint/three-dof.hpp"
#include "game/component/constraint/track-to.hpp"
#endif // ANTKEEPER_GAME_COMPONENT_CONSTRAINT_HPP

src/game/component/constraints/copy-rotation.hpp → src/game/component/constraint/copy-rotation.hpp View File


src/game/component/constraints/copy-scale.hpp → src/game/component/constraint/copy-scale.hpp View File


src/game/component/constraints/copy-transform.hpp → src/game/component/constraint/copy-transform.hpp View File


src/game/component/constraints/copy-translation.hpp → src/game/component/constraint/copy-translation.hpp View File


+ 46
- 0
src/game/component/constraint/pivot.hpp View File

@ -0,0 +1,46 @@
/*
* Copyright (C) 2021 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_COMPONENT_CONSTRAINT_PIVOT_HPP
#define ANTKEEPER_GAME_COMPONENT_CONSTRAINT_PIVOT_HPP
#include "entity/id.hpp"
#include "utility/fundamental-types.hpp"
namespace game {
namespace component {
namespace constraint {
/**
* Pivots around a target entity.
*/
struct pivot
{
/// Target entity ID.
entity::id target;
/// Pivot point offset.
float3 offset;
};
} // namespace constraint
} // namespace component
} // namespace game
#endif // ANTKEEPER_GAME_COMPONENT_CONSTRAINT_PIVOT_HPP

+ 43
- 0
src/game/component/constraint/spring-rotation.hpp View File

@ -0,0 +1,43 @@
/*
* Copyright (C) 2021 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_COMPONENT_CONSTRAINT_SPRING_ROTATION_HPP
#define ANTKEEPER_GAME_COMPONENT_CONSTRAINT_SPRING_ROTATION_HPP
#include "animation/spring.hpp"
#include "utility/fundamental-types.hpp"
namespace game {
namespace component {
namespace constraint {
/**
*
*/
struct spring_rotation
{
/// Yaw, pitch, and roll angle spring.
numeric_spring<float3, float> spring;
};
} // namespace constraint
} // namespace component
} // namespace game
#endif // ANTKEEPER_GAME_COMPONENT_CONSTRAINT_SPRING_ROTATION_HPP

src/game/component/constraints/spring-to.hpp → src/game/component/constraint/spring-to.hpp View File


+ 43
- 0
src/game/component/constraint/spring-translation.hpp View File

@ -0,0 +1,43 @@
/*
* Copyright (C) 2021 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_COMPONENT_CONSTRAINT_SPRING_TRANSLATION_HPP
#define ANTKEEPER_GAME_COMPONENT_CONSTRAINT_SPRING_TRANSLATION_HPP
#include "animation/spring.hpp"
#include "utility/fundamental-types.hpp"
namespace game {
namespace component {
namespace constraint {
/**
*
*/
struct spring_translation
{
/// Translation spring.
numeric_spring<float3, float> spring;
};
} // namespace constraint
} // namespace component
} // namespace game
#endif // ANTKEEPER_GAME_COMPONENT_CONSTRAINT_SPRING_TRANSLATION_HPP

src/game/component/constraints/three-dof.hpp → src/game/component/constraint/three-dof.hpp View File

@ -25,7 +25,7 @@ namespace component {
namespace constraint { namespace constraint {
/** /**
* Springs to a target entity.
* Builds rotation from 3DoF angles.
*/ */
struct three_dof struct three_dof
{ {

src/game/component/constraints/track-to.hpp → src/game/component/constraint/track-to.hpp View File


+ 2
- 3
src/game/context.hpp View File

@ -216,7 +216,6 @@ struct context
render::compositor* surface_compositor; render::compositor* surface_compositor;
render::ground_pass* ground_pass; render::ground_pass* ground_pass;
// Scene utilities // Scene utilities
scene::collection* active_scene; scene::collection* active_scene;
@ -269,11 +268,11 @@ struct context
bool mono_audio; bool mono_audio;
bool captions; bool captions;
float captions_size; float captions_size;
// Entities // Entities
entity::registry* entity_registry; entity::registry* entity_registry;
std::unordered_map<std::string, entity::id> entities; std::unordered_map<std::string, entity::id> entities;
// Systems // Systems
game::system::behavior* behavior_system; game::system::behavior* behavior_system;
game::system::camera* camera_system; game::system::camera* camera_system;

+ 4
- 11
src/game/controls.cpp View File

@ -17,10 +17,13 @@
* 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 "controls.hpp"
#include "game/controls.hpp"
#include "resources/resource-manager.hpp" #include "resources/resource-manager.hpp"
#include "resources/json.hpp" #include "resources/json.hpp"
#include "application.hpp" #include "application.hpp"
#include "game/component/transform.hpp"
#include "game/component/constraint/constraint.hpp"
#include "game/component/constraint-stack.hpp"
#include <fstream> #include <fstream>
namespace game { namespace game {
@ -660,14 +663,4 @@ void apply_gamepad_calibration(input::gamepad& gamepad, const json& calibration)
} }
} }
void enable_orbit_controls(game::context& ctx)
{
}
void disable_orbit_controls(game::context& ctx)
{
}
} // namespace game } // namespace game

+ 0
- 3
src/game/controls.hpp View File

@ -87,9 +87,6 @@ bool save_gamepad_calibration(const game::context& ctx, const input::gamepad& ga
*/ */
void apply_gamepad_calibration(input::gamepad& gamepad, const json& calibration); void apply_gamepad_calibration(input::gamepad& gamepad, const json& calibration);
void enable_orbit_controls(game::context& ctx);
void disable_orbit_controls(game::context& ctx);
} // namespace game } // namespace game
#endif // ANTKEEPER_GAME_CONTROLS_HPP #endif // ANTKEEPER_GAME_CONTROLS_HPP

+ 11
- 11
src/game/loop.cpp View File

@ -25,9 +25,9 @@ namespace game {
loop::loop(): loop::loop():
update_callback([](double, double){}), update_callback([](double, double){}),
render_callback([](double){}), render_callback([](double){}),
update_rate(60.0),
update_timestep(1.0 / update_rate),
max_frame_duration(update_timestep)
update_frequency(60.0),
update_period(1.0 / update_frequency),
max_frame_duration(update_period)
{ {
reset(); reset();
} }
@ -42,10 +42,10 @@ void loop::set_render_callback(std::function callback)
render_callback = callback; render_callback = callback;
} }
void loop::set_update_rate(double frequency)
void loop::set_update_frequency(double frequency)
{ {
update_rate = frequency;
update_timestep = 1.0 / frequency;
update_frequency = frequency;
update_period = 1.0 / update_frequency;
} }
void loop::set_max_frame_duration(double duration) void loop::set_max_frame_duration(double duration)
@ -75,14 +75,14 @@ void loop::tick()
accumulator += std::min<double>(max_frame_duration, frame_duration); accumulator += std::min<double>(max_frame_duration, frame_duration);
while (accumulator >= update_timestep)
while (accumulator >= update_period)
{ {
update_callback(elapsed_time, update_timestep);
elapsed_time += update_timestep;
accumulator -= update_timestep;
update_callback(elapsed_time, update_period);
elapsed_time += update_period;
accumulator -= update_period;
} }
render_callback(accumulator * update_rate);
render_callback(accumulator * update_frequency);
} }
} // namespace game } // namespace game

+ 20
- 4
src/game/loop.hpp View File

@ -50,11 +50,11 @@ public:
void set_render_callback(std::function<void(double)> callback); void set_render_callback(std::function<void(double)> callback);
/** /**
* Sets the update rate.
* Sets the update frequency.
* *
* @param frequency Frequency, in hertz, at which the update callback should be called. * @param frequency Frequency, in hertz, at which the update callback should be called.
*/ */
void set_update_rate(double frequency);
void set_update_frequency(double frequency);
/** /**
* Sets the maximum duration of a frame. This limits the number of times the update callback is called per frame, thereby preventing a "spiral of death", in which the update callback is called too many times per frame while trying to catch up to the target update rate. * Sets the maximum duration of a frame. This limits the number of times the update callback is called per frame, thereby preventing a "spiral of death", in which the update callback is called too many times per frame while trying to catch up to the target update rate.
@ -67,6 +67,12 @@ public:
* Returns the duration of the last frame, in seconds. * Returns the duration of the last frame, in seconds.
*/ */
double get_frame_duration() const; double get_frame_duration() const;
/// Returns the frequency, in hertz, at which the update callback should be called.
double get_update_frequency() const;
/// Returns the period, in seconds, between update callback calls.
double get_update_period() const;
/** /**
* Resets the total elapsed time, frame duration, and internal timers. * Resets the total elapsed time, frame duration, and internal timers.
@ -81,8 +87,8 @@ public:
private: private:
std::function<void(double, double)> update_callback; std::function<void(double, double)> update_callback;
std::function<void(double)> render_callback; std::function<void(double)> render_callback;
double update_rate;
double update_timestep;
double update_frequency;
double update_period;
double max_frame_duration; double max_frame_duration;
double elapsed_time; double elapsed_time;
double accumulator; double accumulator;
@ -91,6 +97,16 @@ private:
double frame_duration; double frame_duration;
}; };
inline double loop::get_update_frequency() const
{
return update_frequency;
}
inline double loop::get_update_period() const
{
return update_period;
}
} // namespace game } // namespace game
#endif // ANTKEEPER_GAME_LOOP_HPP #endif // ANTKEEPER_GAME_LOOP_HPP

+ 3
- 3
src/game/state/boot.cpp View File

@ -1077,10 +1077,10 @@ void boot::setup_debugging()
void boot::setup_loop() void boot::setup_loop()
{ {
// Set update rate
if (ctx.config->contains("update_rate"))
// Set update frequency
if (ctx.config->contains("update_frequency"))
{ {
ctx.loop.set_update_rate((*ctx.config)["update_rate"].get<double>());
ctx.loop.set_update_frequency((*ctx.config)["update_frequency"].get<double>());
} }
// Set update callback // Set update callback

+ 2
- 2
src/game/state/main-menu.cpp View File

@ -270,7 +270,7 @@ main_menu::main_menu(game::context& ctx, bool fade_in):
ctx.ui_clear_pass->set_cleared_buffers(false, true, false); ctx.ui_clear_pass->set_cleared_buffers(false, true, false);
// Create mating swarm // Create mating swarm
game::ant::create_swarm(ctx);
swarm_eid = game::ant::create_swarm(ctx);
if (!ctx.menu_bg_billboard->is_active()) if (!ctx.menu_bg_billboard->is_active())
game::menu::fade_in_bg(ctx); game::menu::fade_in_bg(ctx);
@ -297,7 +297,7 @@ main_menu::~main_menu()
ctx.ui_scene->remove_object(&title_text); ctx.ui_scene->remove_object(&title_text);
// Destroy swarm // Destroy swarm
game::ant::destroy_swarm(ctx);
game::ant::destroy_swarm(ctx, swarm_eid);
ctx.logger->pop_task(EXIT_SUCCESS); ctx.logger->pop_task(EXIT_SUCCESS);
} }

+ 3
- 0
src/game/state/main-menu.hpp View File

@ -23,6 +23,7 @@
#include "game/state/base.hpp" #include "game/state/base.hpp"
#include "scene/text.hpp" #include "scene/text.hpp"
#include "animation/animation.hpp" #include "animation/animation.hpp"
#include "entity/id.hpp"
namespace game { namespace game {
namespace state { namespace state {
@ -39,6 +40,8 @@ private:
scene::text title_text; scene::text title_text;
animation<float> title_fade_animation; animation<float> title_fade_animation;
entity::id swarm_eid;
}; };
} // namespace state } // namespace state

+ 1028
- 0
src/game/state/nest-selection.cpp
File diff suppressed because it is too large
View File


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

@ -0,0 +1,49 @@
/*
* Copyright (C) 2021 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_STATE_NEST_SELECTION_HPP
#define ANTKEEPER_GAME_STATE_NEST_SELECTION_HPP
#include "game/state/base.hpp"
namespace game {
namespace state {
class nest_selection: public game::state::base
{
public:
nest_selection(game::context& ctx);
virtual ~nest_selection();
private:
void setup_camera();
bool is_keeper;
void enable_keeper_controls();
void disable_keeper_controls();
void enable_ant_controls();
void disable_ant_controls();
void enable_controls();
void disable_controls();
};
} // namespace state
} // namespace game
#endif // ANTKEEPER_GAME_STATE_NEST_SELECTION_HPP

+ 296
- 668
src/game/state/nuptial-flight.cpp
File diff suppressed because it is too large
View File


+ 13
- 6
src/game/state/nuptial-flight.hpp View File

@ -21,6 +21,7 @@
#define ANTKEEPER_GAME_STATE_NUPTIAL_FLIGHT_HPP #define ANTKEEPER_GAME_STATE_NUPTIAL_FLIGHT_HPP
#include "game/state/base.hpp" #include "game/state/base.hpp"
#include "entity/id.hpp"
namespace game { namespace game {
namespace state { namespace state {
@ -32,15 +33,21 @@ public:
virtual ~nuptial_flight(); virtual ~nuptial_flight();
private: private:
void setup_camera();
void create_camera_rig();
void destroy_camera_rig();
bool is_keeper;
void enable_keeper_controls();
void disable_keeper_controls();
void enable_ant_controls();
void disable_ant_controls();
void enable_controls(); void enable_controls();
void disable_controls(); void disable_controls();
entity::id camera_rig_eid;
entity::id camera_rig_spring_translation_eid;
entity::id camera_rig_spring_rotation_eid;
entity::id camera_rig_copy_translation_eid;
entity::id camera_rig_pivot_eid;
entity::id swarm_eid;
bool mouse_look;
}; };
} // namespace state } // namespace state

+ 12
- 12
src/game/system/astronomy.cpp View File

@ -65,32 +65,32 @@ astronomy::astronomy(entity::registry& registry):
}; };
registry.on_construct<game::component::observer>().connect<&astronomy::on_observer_modified>(this); registry.on_construct<game::component::observer>().connect<&astronomy::on_observer_modified>(this);
registry.on_replace<game::component::observer>().connect<&astronomy::on_observer_modified>(this);
registry.on_update<game::component::observer>().connect<&astronomy::on_observer_modified>(this);
registry.on_destroy<game::component::observer>().connect<&astronomy::on_observer_destroyed>(this); registry.on_destroy<game::component::observer>().connect<&astronomy::on_observer_destroyed>(this);
registry.on_construct<game::component::celestial_body>().connect<&astronomy::on_celestial_body_modified>(this); registry.on_construct<game::component::celestial_body>().connect<&astronomy::on_celestial_body_modified>(this);
registry.on_replace<game::component::celestial_body>().connect<&astronomy::on_celestial_body_modified>(this);
registry.on_update<game::component::celestial_body>().connect<&astronomy::on_celestial_body_modified>(this);
registry.on_destroy<game::component::celestial_body>().connect<&astronomy::on_celestial_body_destroyed>(this); registry.on_destroy<game::component::celestial_body>().connect<&astronomy::on_celestial_body_destroyed>(this);
registry.on_construct<game::component::orbit>().connect<&astronomy::on_orbit_modified>(this); registry.on_construct<game::component::orbit>().connect<&astronomy::on_orbit_modified>(this);
registry.on_replace<game::component::orbit>().connect<&astronomy::on_orbit_modified>(this);
registry.on_update<game::component::orbit>().connect<&astronomy::on_orbit_modified>(this);
registry.on_destroy<game::component::orbit>().connect<&astronomy::on_orbit_destroyed>(this); registry.on_destroy<game::component::orbit>().connect<&astronomy::on_orbit_destroyed>(this);
registry.on_construct<game::component::atmosphere>().connect<&astronomy::on_atmosphere_modified>(this); registry.on_construct<game::component::atmosphere>().connect<&astronomy::on_atmosphere_modified>(this);
registry.on_replace<game::component::atmosphere>().connect<&astronomy::on_atmosphere_modified>(this);
registry.on_update<game::component::atmosphere>().connect<&astronomy::on_atmosphere_modified>(this);
registry.on_destroy<game::component::atmosphere>().connect<&astronomy::on_atmosphere_destroyed>(this); registry.on_destroy<game::component::atmosphere>().connect<&astronomy::on_atmosphere_destroyed>(this);
} }
astronomy::~astronomy() astronomy::~astronomy()
{ {
registry.on_construct<game::component::observer>().disconnect<&astronomy::on_observer_modified>(this); registry.on_construct<game::component::observer>().disconnect<&astronomy::on_observer_modified>(this);
registry.on_replace<game::component::observer>().disconnect<&astronomy::on_observer_modified>(this);
registry.on_update<game::component::observer>().disconnect<&astronomy::on_observer_modified>(this);
registry.on_destroy<game::component::observer>().disconnect<&astronomy::on_observer_destroyed>(this); registry.on_destroy<game::component::observer>().disconnect<&astronomy::on_observer_destroyed>(this);
registry.on_construct<game::component::celestial_body>().disconnect<&astronomy::on_celestial_body_modified>(this); registry.on_construct<game::component::celestial_body>().disconnect<&astronomy::on_celestial_body_modified>(this);
registry.on_replace<game::component::celestial_body>().disconnect<&astronomy::on_celestial_body_modified>(this);
registry.on_update<game::component::celestial_body>().disconnect<&astronomy::on_celestial_body_modified>(this);
registry.on_destroy<game::component::celestial_body>().disconnect<&astronomy::on_celestial_body_destroyed>(this); registry.on_destroy<game::component::celestial_body>().disconnect<&astronomy::on_celestial_body_destroyed>(this);
registry.on_construct<game::component::orbit>().disconnect<&astronomy::on_orbit_modified>(this); registry.on_construct<game::component::orbit>().disconnect<&astronomy::on_orbit_modified>(this);
registry.on_replace<game::component::orbit>().disconnect<&astronomy::on_orbit_modified>(this);
registry.on_update<game::component::orbit>().disconnect<&astronomy::on_orbit_modified>(this);
registry.on_destroy<game::component::orbit>().disconnect<&astronomy::on_orbit_destroyed>(this); registry.on_destroy<game::component::orbit>().disconnect<&astronomy::on_orbit_destroyed>(this);
registry.on_construct<game::component::atmosphere>().disconnect<&astronomy::on_atmosphere_modified>(this); registry.on_construct<game::component::atmosphere>().disconnect<&astronomy::on_atmosphere_modified>(this);
registry.on_replace<game::component::atmosphere>().disconnect<&astronomy::on_atmosphere_modified>(this);
registry.on_update<game::component::atmosphere>().disconnect<&astronomy::on_atmosphere_modified>(this);
registry.on_destroy<game::component::atmosphere>().disconnect<&astronomy::on_atmosphere_destroyed>(this); registry.on_destroy<game::component::atmosphere>().disconnect<&astronomy::on_atmosphere_destroyed>(this);
} }
@ -427,7 +427,7 @@ void astronomy::set_sky_pass(::render::sky_pass* pass)
} }
} }
void astronomy::on_observer_modified(entity::registry& registry, entity::id entity_id, game::component::observer& component)
void astronomy::on_observer_modified(entity::registry& registry, entity::id entity_id)
{ {
if (entity_id == observer_eid) if (entity_id == observer_eid)
observer_modified(); observer_modified();
@ -439,7 +439,7 @@ void astronomy::on_observer_destroyed(entity::registry& registry, entity::id ent
observer_modified(); observer_modified();
} }
void astronomy::on_celestial_body_modified(entity::registry& registry, entity::id entity_id, game::component::celestial_body& component)
void astronomy::on_celestial_body_modified(entity::registry& registry, entity::id entity_id)
{ {
if (entity_id == reference_body_eid) if (entity_id == reference_body_eid)
reference_body_modified(); reference_body_modified();
@ -451,7 +451,7 @@ void astronomy::on_celestial_body_destroyed(entity::registry& registry, entity::
reference_body_modified(); reference_body_modified();
} }
void astronomy::on_orbit_modified(entity::registry& registry, entity::id entity_id, game::component::orbit& component)
void astronomy::on_orbit_modified(entity::registry& registry, entity::id entity_id)
{ {
if (entity_id == reference_body_eid) if (entity_id == reference_body_eid)
reference_orbit_modified(); reference_orbit_modified();
@ -463,7 +463,7 @@ void astronomy::on_orbit_destroyed(entity::registry& registry, entity::id entity
reference_orbit_modified(); reference_orbit_modified();
} }
void astronomy::on_atmosphere_modified(entity::registry& registry, entity::id entity_id, game::component::atmosphere& component)
void astronomy::on_atmosphere_modified(entity::registry& registry, entity::id entity_id)
{ {
if (entity_id == reference_body_eid) if (entity_id == reference_body_eid)
reference_atmosphere_modified(); reference_atmosphere_modified();

+ 4
- 4
src/game/system/astronomy.hpp View File

@ -91,13 +91,13 @@ public:
void set_sky_pass(::render::sky_pass* pass); void set_sky_pass(::render::sky_pass* pass);
private: private:
void on_observer_modified(entity::registry& registry, entity::id entity_id, game::component::observer& component);
void on_observer_modified(entity::registry& registry, entity::id entity_id);
void on_observer_destroyed(entity::registry& registry, entity::id entity_id); void on_observer_destroyed(entity::registry& registry, entity::id entity_id);
void on_celestial_body_modified(entity::registry& registry, entity::id entity_id, game::component::celestial_body& component);
void on_celestial_body_modified(entity::registry& registry, entity::id entity_id);
void on_celestial_body_destroyed(entity::registry& registry, entity::id entity_id); void on_celestial_body_destroyed(entity::registry& registry, entity::id entity_id);
void on_orbit_modified(entity::registry& registry, entity::id entity_id, game::component::orbit& component);
void on_orbit_modified(entity::registry& registry, entity::id entity_id);
void on_orbit_destroyed(entity::registry& registry, entity::id entity_id); void on_orbit_destroyed(entity::registry& registry, entity::id entity_id);
void on_atmosphere_modified(entity::registry& registry, entity::id entity_id, game::component::atmosphere& component);
void on_atmosphere_modified(entity::registry& registry, entity::id entity_id);
void on_atmosphere_destroyed(entity::registry& registry, entity::id entity_id); void on_atmosphere_destroyed(entity::registry& registry, entity::id entity_id);
/// Called each time the observer is modified. /// Called each time the observer is modified.

+ 69
- 33
src/game/system/atmosphere.cpp View File

@ -29,14 +29,21 @@ atmosphere::atmosphere(entity::registry& registry):
updatable(registry), updatable(registry),
rgb_wavelengths{0, 0, 0}, rgb_wavelengths{0, 0, 0},
rgb_ozone_cross_sections{0, 0, 0}, rgb_ozone_cross_sections{0, 0, 0},
atmosphere_component(nullptr),
active_atmosphere_eid(entt::null),
sky_pass(nullptr) sky_pass(nullptr)
{ {
registry.on_construct<game::component::atmosphere>().connect<&atmosphere::on_atmosphere_construct>(this); registry.on_construct<game::component::atmosphere>().connect<&atmosphere::on_atmosphere_construct>(this);
registry.on_replace<game::component::atmosphere>().connect<&atmosphere::on_atmosphere_replace>(this);
registry.on_update<game::component::atmosphere>().connect<&atmosphere::on_atmosphere_update>(this);
registry.on_destroy<game::component::atmosphere>().connect<&atmosphere::on_atmosphere_destroy>(this); registry.on_destroy<game::component::atmosphere>().connect<&atmosphere::on_atmosphere_destroy>(this);
} }
atmosphere::~atmosphere()
{
registry.on_construct<game::component::atmosphere>().disconnect<&atmosphere::on_atmosphere_construct>(this);
registry.on_update<game::component::atmosphere>().disconnect<&atmosphere::on_atmosphere_update>(this);
registry.on_destroy<game::component::atmosphere>().disconnect<&atmosphere::on_atmosphere_destroy>(this);
}
void atmosphere::update(double t, double dt) void atmosphere::update(double t, double dt)
{} {}
@ -52,7 +59,14 @@ void atmosphere::set_rgb_wavelengths(const double3& wavelengths)
physics::gas::ozone::cross_section_293k<double>(wavelengths.z * 1e9) physics::gas::ozone::cross_section_293k<double>(wavelengths.z * 1e9)
}; };
atmosphere_modified();
// Update atmosphere components
registry.view<component::atmosphere>().each
(
[&](entity::id entity_id, auto& component)
{
update_atmosphere(entity_id);
}
);
} }
void atmosphere::set_sky_pass(::render::sky_pass* pass) void atmosphere::set_sky_pass(::render::sky_pass* pass)
@ -61,18 +75,28 @@ void atmosphere::set_sky_pass(::render::sky_pass* pass)
update_sky_pass(); update_sky_pass();
} }
void atmosphere::atmosphere_modified()
void atmosphere::set_active_atmosphere(entity::id entity_id)
{
if (entity_id != active_atmosphere_eid)
{
active_atmosphere_eid = entity_id;
update_sky_pass();
}
}
void atmosphere::update_atmosphere(entity::id entity_id)
{ {
if (!atmosphere_component)
return;
// Get atmosphere component of the entity // Get atmosphere component of the entity
game::component::atmosphere& component = *atmosphere_component;
game::component::atmosphere* component = registry.try_get<game::component::atmosphere>(entity_id);
// Abort if entity has no atmosphere component
if (!component)
return;
// Calculate Rayleigh scattering coefficients // Calculate Rayleigh scattering coefficients
const double rayleigh_density = physics::number_density(component.rayleigh_concentration);
const double rayleigh_polarization = physics::gas::atmosphere::polarization(component.index_of_refraction, rayleigh_density);
component.rayleigh_scattering =
const double rayleigh_density = physics::number_density(component->rayleigh_concentration);
const double rayleigh_polarization = physics::gas::atmosphere::polarization(component->index_of_refraction, rayleigh_density);
component->rayleigh_scattering =
{ {
physics::gas::atmosphere::scattering(rayleigh_density, rayleigh_polarization, rgb_wavelengths.x), physics::gas::atmosphere::scattering(rayleigh_density, rayleigh_polarization, rgb_wavelengths.x),
physics::gas::atmosphere::scattering(rayleigh_density, rayleigh_polarization, rgb_wavelengths.y), physics::gas::atmosphere::scattering(rayleigh_density, rayleigh_polarization, rgb_wavelengths.y),
@ -80,53 +104,65 @@ void atmosphere::atmosphere_modified()
}; };
// Calculate Mie scattering and extinction coefficients // Calculate Mie scattering and extinction coefficients
const double mie_density = physics::number_density(component.mie_concentration);
const double mie_polarization = physics::gas::atmosphere::polarization(component.index_of_refraction, mie_density);
component.mie_scattering = physics::gas::atmosphere::scattering(mie_density, mie_polarization);
component.mie_extinction = physics::gas::atmosphere::extinction(component.mie_scattering, component.mie_albedo);
const double mie_density = physics::number_density(component->mie_concentration);
const double mie_polarization = physics::gas::atmosphere::polarization(component->index_of_refraction, mie_density);
component->mie_scattering = physics::gas::atmosphere::scattering(mie_density, mie_polarization);
component->mie_extinction = physics::gas::atmosphere::extinction(component->mie_scattering, component->mie_albedo);
// Calculate ozone absorption coefficients // Calculate ozone absorption coefficients
const double ozone_density = physics::number_density(component.ozone_concentration);
component.ozone_absorption =
const double ozone_density = physics::number_density(component->ozone_concentration);
component->ozone_absorption =
{ {
physics::gas::ozone::absorption(rgb_ozone_cross_sections.x, ozone_density), physics::gas::ozone::absorption(rgb_ozone_cross_sections.x, ozone_density),
physics::gas::ozone::absorption(rgb_ozone_cross_sections.y, ozone_density), physics::gas::ozone::absorption(rgb_ozone_cross_sections.y, ozone_density),
physics::gas::ozone::absorption(rgb_ozone_cross_sections.z, ozone_density) physics::gas::ozone::absorption(rgb_ozone_cross_sections.z, ozone_density)
}; };
// Pass atmosphere parameters to sky pass
update_sky_pass();
// Update sky pass parameters
if (entity_id == active_atmosphere_eid)
{
update_sky_pass();
}
} }
void atmosphere::update_sky_pass() void atmosphere::update_sky_pass()
{ {
if (!sky_pass || !atmosphere_component)
// Abort if no sky pass set
if (!sky_pass)
return; return;
const game::component::atmosphere& component = *atmosphere_component;
// Abort if active atmosphere entity is not valid
if (!registry.valid(active_atmosphere_eid))
return;
// Get atmosphere component of the entity
game::component::atmosphere* component = registry.try_get<game::component::atmosphere>(active_atmosphere_eid);
// Abort if entity has no atmosphere component
if (!component)
return;
sky_pass->set_atmosphere_upper_limit(static_cast<float>(component.upper_limit));
sky_pass->set_rayleigh_parameters(static_cast<float>(component.rayleigh_scale_height), math::type_cast<float>(component.rayleigh_scattering));
sky_pass->set_mie_parameters(static_cast<float>(component.mie_scale_height), static_cast<float>(component.mie_scattering), static_cast<float>(component.mie_extinction), static_cast<float>(component.mie_anisotropy));
sky_pass->set_ozone_parameters(static_cast<float>(component.ozone_lower_limit), static_cast<float>(component.ozone_upper_limit), static_cast<float>(component.ozone_mode), math::type_cast<float>(component.ozone_absorption));
sky_pass->set_airglow_illuminance(math::type_cast<float>(component.airglow_illuminance));
sky_pass->set_atmosphere_upper_limit(static_cast<float>(component->upper_limit));
sky_pass->set_rayleigh_parameters(static_cast<float>(component->rayleigh_scale_height), math::type_cast<float>(component->rayleigh_scattering));
sky_pass->set_mie_parameters(static_cast<float>(component->mie_scale_height), static_cast<float>(component->mie_scattering), static_cast<float>(component->mie_extinction), static_cast<float>(component->mie_anisotropy));
sky_pass->set_ozone_parameters(static_cast<float>(component->ozone_lower_limit), static_cast<float>(component->ozone_upper_limit), static_cast<float>(component->ozone_mode), math::type_cast<float>(component->ozone_absorption));
sky_pass->set_airglow_illuminance(math::type_cast<float>(component->airglow_illuminance));
} }
void atmosphere::on_atmosphere_construct(entity::registry& registry, entity::id entity_id, game::component::atmosphere& component)
void atmosphere::on_atmosphere_construct(entity::registry& registry, entity::id entity_id)
{ {
atmosphere_component = &component;
atmosphere_modified();
update_atmosphere(entity_id);
} }
void atmosphere::on_atmosphere_replace(entity::registry& registry, entity::id entity_id, game::component::atmosphere& component)
void atmosphere::on_atmosphere_update(entity::registry& registry, entity::id entity_id)
{ {
atmosphere_component = &component;
atmosphere_modified();
update_atmosphere(entity_id);
} }
void atmosphere::on_atmosphere_destroy(entity::registry& registry, entity::id entity_id) void atmosphere::on_atmosphere_destroy(entity::registry& registry, entity::id entity_id)
{ {
atmosphere_component = nullptr;
if (entity_id == active_atmosphere_eid)
active_atmosphere_eid = entt::null;
} }
} // namespace system } // namespace system

+ 12
- 3
src/game/system/atmosphere.hpp View File

@ -37,6 +37,7 @@ class atmosphere:
{ {
public: public:
atmosphere(entity::registry& registry); atmosphere(entity::registry& registry);
~atmosphere();
virtual void update(double t, double dt); virtual void update(double t, double dt);
@ -49,14 +50,22 @@ public:
void set_sky_pass(::render::sky_pass* pass); void set_sky_pass(::render::sky_pass* pass);
/**
* Sets the entity ID of the active atmosphere.
*
* @param entity_id Entity ID of the active atmosphere.
*/
void set_active_atmosphere(entity::id entity_id);
private: private:
void atmosphere_modified();
void update_atmosphere(entity::id entity_id);
void update_sky_pass(); void update_sky_pass();
void on_atmosphere_construct(entity::registry& registry, entity::id entity_id, game::component::atmosphere& component);
void on_atmosphere_replace(entity::registry& registry, entity::id entity_id, game::component::atmosphere& component);
void on_atmosphere_construct(entity::registry& registry, entity::id entity_id);
void on_atmosphere_update(entity::registry& registry, entity::id entity_id);
void on_atmosphere_destroy(entity::registry& registry, entity::id entity_id); void on_atmosphere_destroy(entity::registry& registry, entity::id entity_id);
entity::id active_atmosphere_eid;
double3 rgb_wavelengths; double3 rgb_wavelengths;
double3 rgb_ozone_cross_sections; double3 rgb_ozone_cross_sections;
game::component::atmosphere* atmosphere_component; game::component::atmosphere* atmosphere_component;

+ 22
- 24
src/game/system/blackbody.cpp View File

@ -22,6 +22,7 @@
#include "physics/light/blackbody.hpp" #include "physics/light/blackbody.hpp"
#include "physics/light/photometry.hpp" #include "physics/light/photometry.hpp"
#include "math/quadrature.hpp" #include "math/quadrature.hpp"
#include <numeric>
namespace game { namespace game {
namespace system { namespace system {
@ -35,10 +36,17 @@ blackbody::blackbody(entity::registry& registry):
std::iota(visible_wavelengths_nm.begin(), visible_wavelengths_nm.end(), 280); std::iota(visible_wavelengths_nm.begin(), visible_wavelengths_nm.end(), 280);
registry.on_construct<game::component::blackbody>().connect<&blackbody::on_blackbody_construct>(this); registry.on_construct<game::component::blackbody>().connect<&blackbody::on_blackbody_construct>(this);
registry.on_replace<game::component::blackbody>().connect<&blackbody::on_blackbody_replace>(this);
registry.on_update<game::component::blackbody>().connect<&blackbody::on_blackbody_update>(this);
registry.on_construct<game::component::celestial_body>().connect<&blackbody::on_celestial_body_construct>(this); registry.on_construct<game::component::celestial_body>().connect<&blackbody::on_celestial_body_construct>(this);
registry.on_replace<game::component::celestial_body>().connect<&blackbody::on_celestial_body_replace>(this);
registry.on_update<game::component::celestial_body>().connect<&blackbody::on_celestial_body_update>(this);
}
blackbody::~blackbody()
{
registry.on_construct<game::component::blackbody>().disconnect<&blackbody::on_blackbody_construct>(this);
registry.on_update<game::component::blackbody>().disconnect<&blackbody::on_blackbody_update>(this);
registry.on_construct<game::component::celestial_body>().disconnect<&blackbody::on_celestial_body_construct>(this);
registry.on_update<game::component::celestial_body>().disconnect<&blackbody::on_celestial_body_update>(this);
} }
void blackbody::update(double t, double dt) void blackbody::update(double t, double dt)
@ -51,34 +59,24 @@ void blackbody::set_illuminant(const math::vector2& illuminant)
void blackbody::update_luminance(entity::id entity_id) void blackbody::update_luminance(entity::id entity_id)
{ {
// Abort if entity has no blackbody component
if (!registry.has<component::blackbody>(entity_id))
return;
// Get blackbody component of the entity
component::blackbody& blackbody = registry.get<component::blackbody>(entity_id);
// Get blackbody and celestial body components of the entity
auto [blackbody, celestial_body] = registry.try_get<component::blackbody, component::celestial_body>(entity_id);
// Clear luminance
blackbody.luminance = {0, 0, 0};
// Abort if entity has no celestial body component
if (!registry.has<component::celestial_body>(entity_id))
// Abort if entity is missing a blackbody or celestial body component
if (!blackbody || !celestial_body)
return; return;
// Get celestial body component of the entity
const component::celestial_body& celestial_body = registry.get<component::celestial_body>(entity_id);
// Construct chromatic adaptation transform // Construct chromatic adaptation transform
const double3x3 cat = color::cat::matrix(illuminant, color::aces::white_point<double>); const double3x3 cat = color::cat::matrix(illuminant, color::aces::white_point<double>);
// Construct a lambda function which calculates the blackbody's RGB luminance of a given wavelength // Construct a lambda function which calculates the blackbody's RGB luminance of a given wavelength
auto rgb_luminance = [blackbody, cat](double wavelength_nm) -> double3
auto rgb_luminance = [temperature = blackbody->temperature, cat](double wavelength_nm) -> double3
{ {
// Convert wavelength from nanometers to meters // Convert wavelength from nanometers to meters
const double wavelength_m = wavelength_nm * 1e-9; const double wavelength_m = wavelength_nm * 1e-9;
// Calculate the spectral intensity of the wavelength // Calculate the spectral intensity of the wavelength
const double spectral_radiance = physics::light::blackbody::spectral_radiance<double>(blackbody.temperature, wavelength_m);
const double spectral_radiance = physics::light::blackbody::spectral_radiance<double>(temperature, wavelength_m);
// Calculate the ACEScg color of the wavelength using CIE color matching functions // Calculate the ACEScg color of the wavelength using CIE color matching functions
@ -89,25 +87,25 @@ void blackbody::update_luminance(entity::id entity_id)
}; };
// Integrate the blackbody RGB luminance over wavelengths in the visible spectrum // Integrate the blackbody RGB luminance over wavelengths in the visible spectrum
blackbody.luminance = math::quadrature::simpson(rgb_luminance, visible_wavelengths_nm.begin(), visible_wavelengths_nm.end());
blackbody->luminance = math::quadrature::simpson(rgb_luminance, visible_wavelengths_nm.begin(), visible_wavelengths_nm.end());
} }
void blackbody::on_blackbody_construct(entity::registry& registry, entity::id entity_id, game::component::blackbody& blackbody)
void blackbody::on_blackbody_construct(entity::registry& registry, entity::id entity_id)
{ {
update_luminance(entity_id); update_luminance(entity_id);
} }
void blackbody::on_blackbody_replace(entity::registry& registry, entity::id entity_id, game::component::blackbody& blackbody)
void blackbody::on_blackbody_update(entity::registry& registry, entity::id entity_id)
{ {
update_luminance(entity_id); update_luminance(entity_id);
} }
void blackbody::on_celestial_body_construct(entity::registry& registry, entity::id entity_id, game::component::celestial_body& celestial_body)
void blackbody::on_celestial_body_construct(entity::registry& registry, entity::id entity_id)
{ {
update_luminance(entity_id); update_luminance(entity_id);
} }
void blackbody::on_celestial_body_replace(entity::registry& registry, entity::id entity_id, game::component::celestial_body& celestial_body)
void blackbody::on_celestial_body_update(entity::registry& registry, entity::id entity_id)
{ {
update_luminance(entity_id); update_luminance(entity_id);
} }

+ 5
- 4
src/game/system/blackbody.hpp View File

@ -38,6 +38,7 @@ class blackbody:
{ {
public: public:
blackbody(entity::registry& registry); blackbody(entity::registry& registry);
~blackbody();
virtual void update(double t, double dt); virtual void update(double t, double dt);
@ -51,11 +52,11 @@ public:
private: private:
void update_luminance(entity::id entity_id); void update_luminance(entity::id entity_id);
void on_blackbody_construct(entity::registry& registry, entity::id entity_id, game::component::blackbody& blackbody);
void on_blackbody_replace(entity::registry& registry, entity::id entity_id, game::component::blackbody& blackbody);
void on_blackbody_construct(entity::registry& registry, entity::id entity_id);
void on_blackbody_update(entity::registry& registry, entity::id entity_id);
void on_celestial_body_construct(entity::registry& registry, entity::id entity_id, game::component::celestial_body& celestial_body);
void on_celestial_body_replace(entity::registry& registry, entity::id entity_id, game::component::celestial_body& celestial_body);
void on_celestial_body_construct(entity::registry& registry, entity::id entity_id);
void on_celestial_body_update(entity::registry& registry, entity::id entity_id);
math::vector2<double> illuminant; math::vector2<double> illuminant;
std::vector<double> visible_wavelengths_nm; std::vector<double> visible_wavelengths_nm;

+ 8
- 4
src/game/system/collision.cpp View File

@ -27,17 +27,21 @@ collision::collision(entity::registry& registry):
updatable(registry) updatable(registry)
{ {
registry.on_construct<component::collision>().connect<&collision::on_collision_construct>(this); registry.on_construct<component::collision>().connect<&collision::on_collision_construct>(this);
registry.on_replace<component::collision>().connect<&collision::on_collision_replace>(this);
registry.on_update<component::collision>().connect<&collision::on_collision_update>(this);
registry.on_destroy<component::collision>().connect<&collision::on_collision_destroy>(this); registry.on_destroy<component::collision>().connect<&collision::on_collision_destroy>(this);
} }
void collision::update(double t, double dt) void collision::update(double t, double dt)
{}
{
registry.on_construct<component::collision>().disconnect<&collision::on_collision_construct>(this);
registry.on_update<component::collision>().disconnect<&collision::on_collision_update>(this);
registry.on_destroy<component::collision>().disconnect<&collision::on_collision_destroy>(this);
}
void collision::on_collision_construct(entity::registry& registry, entity::id entity_id, component::collision& collision)
void collision::on_collision_construct(entity::registry& registry, entity::id entity_id)
{} {}
void collision::on_collision_replace(entity::registry& registry, entity::id entity_id, component::collision& collision)
void collision::on_collision_update(entity::registry& registry, entity::id entity_id)
{} {}
void collision::on_collision_destroy(entity::registry& registry, entity::id entity_id) void collision::on_collision_destroy(entity::registry& registry, entity::id entity_id)

+ 2
- 2
src/game/system/collision.hpp View File

@ -37,8 +37,8 @@ public:
virtual void update(double t, double dt); virtual void update(double t, double dt);
private: private:
void on_collision_construct(entity::registry& registry, entity::id entity_id, game::component::collision& collision);
void on_collision_replace(entity::registry& registry, entity::id entity_id, game::component::collision& collision);
void on_collision_construct(entity::registry& registry, entity::id entity_id);
void on_collision_update(entity::registry& registry, entity::id entity_id);
void on_collision_destroy(entity::registry& registry, entity::id entity_id); void on_collision_destroy(entity::registry& registry, entity::id entity_id);
}; };

+ 208
- 115
src/game/system/constraint.cpp View File

@ -19,186 +19,279 @@
#include "constraint.hpp" #include "constraint.hpp"
#include "game/component/constraint-stack.hpp" #include "game/component/constraint-stack.hpp"
#include "game/component/constraints/constraints.hpp"
#include "game/component/transform.hpp"
namespace game { namespace game {
namespace system { namespace system {
constraint::constraint(entity::registry& registry): constraint::constraint(entity::registry& registry):
updatable(registry) updatable(registry)
{}
{
registry.on_construct<component::constraint_stack>().connect<&constraint::on_constraint_stack_update>(this);
registry.on_update<component::constraint_stack>().connect<&constraint::on_constraint_stack_update>(this);
registry.on_destroy<component::constraint_stack>().connect<&constraint::on_constraint_stack_update>(this);
}
constraint::~constraint()
{
registry.on_construct<component::constraint_stack>().disconnect<&constraint::on_constraint_stack_update>(this);
registry.on_update<component::constraint_stack>().disconnect<&constraint::on_constraint_stack_update>(this);
registry.on_destroy<component::constraint_stack>().disconnect<&constraint::on_constraint_stack_update>(this);
}
void constraint::update(double t, double dt) void constraint::update(double t, double dt)
{ {
// For each entity with transform and constraint stack components // For each entity with transform and constraint stack components
registry.view<component::transform, component::constraint_stack>().each(
registry.view<component::transform, component::constraint_stack>().each
(
[&](entity::id transform_eid, auto& transform, auto& stack) [&](entity::id transform_eid, auto& transform, auto& stack)
{ {
// Init world-space transform
transform.world = transform.local;
// Get entity ID of first constraint // Get entity ID of first constraint
entity::id constraint_eid = stack.head; entity::id constraint_eid = stack.head;
// Consecutively apply constraints // Consecutively apply constraints
while (constraint_eid != entt::null)
while (registry.valid(constraint_eid))
{ {
// Invalid constraint
if (!registry.has<component::constraint_stack_node>(constraint_eid))
break;
// Get constraint stack node of the constraint
const component::constraint_stack_node* node = registry.try_get<component::constraint_stack_node>(constraint_eid);
// Get constraint stack node of this constraint
const auto& node = registry.get<component::constraint_stack_node>(constraint_eid);
// Abort if constraint is missing a constraint stack node
if (!node)
break;
// Apply constraint if enabled // Apply constraint if enabled
if (node.active)
if (node->active)
handle_constraint(transform, constraint_eid, static_cast<float>(dt)); handle_constraint(transform, constraint_eid, static_cast<float>(dt));
// Get entity ID of next constraint
constraint_eid = node.next;
// Get entity ID of next constraint in the stack
constraint_eid = node->next;
} }
});
}
);
}
void constraint::on_constraint_stack_update(entity::registry& registry, entity::id constraint_stack_eid)
{
registry.sort<component::constraint_stack>
(
[](const auto& lhs, const auto& rhs)
{
return lhs.priority < rhs.priority;
}
);
} }
void constraint::handle_constraint(component::transform& transform, entity::id constraint_eid, float dt) void constraint::handle_constraint(component::transform& transform, entity::id constraint_eid, float dt)
{ {
if (registry.has<component::constraint::copy_translation>(constraint_eid))
handle_copy_translation_constraint(transform, constraint_eid);
else if (registry.has<component::constraint::copy_rotation>(constraint_eid))
handle_copy_rotation_constraint(transform, constraint_eid);
else if (registry.has<component::constraint::copy_scale>(constraint_eid))
handle_copy_scale_constraint(transform, constraint_eid);
else if (registry.has<component::constraint::copy_transform>(constraint_eid))
handle_copy_transform_constraint(transform, constraint_eid);
else if (registry.has<component::constraint::track_to>(constraint_eid))
handle_track_to_constraint(transform, constraint_eid);
else if (registry.has<component::constraint::three_dof>(constraint_eid))
handle_three_dof_constraint(transform, constraint_eid);
else if (registry.has<component::constraint::spring_to>(constraint_eid))
handle_spring_to_constraint(transform, constraint_eid, dt);
}
void constraint::handle_copy_translation_constraint(component::transform& transform, entity::id constraint_eid)
{
const auto& params = registry.get<component::constraint::copy_translation>(constraint_eid);
if (this->registry.has<component::transform>(params.target))
if (auto constraint = registry.try_get<component::constraint::copy_translation>(constraint_eid); constraint)
handle_copy_translation_constraint(transform, *constraint);
else if (auto constraint = registry.try_get<component::constraint::copy_rotation>(constraint_eid); constraint)
handle_copy_rotation_constraint(transform, *constraint);
else if (auto constraint = registry.try_get<component::constraint::copy_scale>(constraint_eid); constraint)
handle_copy_scale_constraint(transform, *constraint);
else if (auto constraint = registry.try_get<component::constraint::copy_transform>(constraint_eid); constraint)
handle_copy_transform_constraint(transform, *constraint);
else if (auto constraint = registry.try_get<component::constraint::track_to>(constraint_eid); constraint)
handle_track_to_constraint(transform, *constraint);
else if (auto constraint = registry.try_get<component::constraint::three_dof>(constraint_eid); constraint)
handle_three_dof_constraint(transform, *constraint);
else if (auto constraint = registry.try_get<component::constraint::pivot>(constraint_eid); constraint)
handle_pivot_constraint(transform, *constraint);
else if (auto constraint = registry.try_get<component::constraint::child_of>(constraint_eid); constraint)
handle_child_of_constraint(transform, *constraint);
else if (auto constraint = registry.try_get<component::constraint::spring_to>(constraint_eid); constraint)
handle_spring_to_constraint(transform, *constraint, dt);
else if (auto constraint = registry.try_get<component::constraint::spring_translation>(constraint_eid); constraint)
handle_spring_translation_constraint(transform, *constraint, dt);
else if (auto constraint = registry.try_get<component::constraint::spring_rotation>(constraint_eid); constraint)
handle_spring_rotation_constraint(transform, *constraint, dt);
}
void constraint::handle_child_of_constraint(component::transform& transform, const component::constraint::child_of& constraint)
{
if (registry.valid(constraint.target))
{ {
const auto& target_translation = registry.get<component::transform>(params.target).world.translation;
if (params.offset)
const component::transform* target_transform = registry.try_get<component::transform>(constraint.target);
if (target_transform)
{ {
if (params.copy_x)
transform.world.translation.x += (params.invert_x) ? -target_translation.x : target_translation.x;
if (params.copy_y)
transform.world.translation.y += (params.invert_y) ? -target_translation.y : target_translation.y;
if (params.copy_z)
transform.world.translation.z += (params.invert_z) ? -target_translation.z : target_translation.z;
transform.world = target_transform->world * transform.world;
} }
else
}
}
void constraint::handle_copy_rotation_constraint(component::transform& transform, const component::constraint::copy_rotation& constraint)
{
if (registry.valid(constraint.target))
{
const component::transform* target_transform = registry.try_get<component::transform>(constraint.target);
if (target_transform)
{ {
if (params.copy_x)
transform.world.translation.x = (params.invert_x) ? -target_translation.x : target_translation.x;
if (params.copy_y)
transform.world.translation.y = (params.invert_y) ? -target_translation.y : target_translation.y;
if (params.copy_z)
transform.world.translation.z = (params.invert_z) ? -target_translation.z : target_translation.z;
transform.world.rotation = target_transform->world.rotation;
} }
} }
} }
void constraint::handle_copy_rotation_constraint(component::transform& transform, entity::id constraint_eid)
void constraint::handle_copy_scale_constraint(component::transform& transform, const component::constraint::copy_scale& constraint)
{ {
const auto& params = registry.get<component::constraint::copy_rotation>(constraint_eid);
if (this->registry.has<component::transform>(params.target))
if (registry.valid(constraint.target))
{ {
const auto& target_rotation = registry.get<component::transform>(params.target).world.rotation;
transform.world.rotation = target_rotation;
const component::transform* target_transform = registry.try_get<component::transform>(constraint.target);
if (target_transform)
{
const auto& target_scale = target_transform->world.scale;
if (constraint.copy_x)
transform.world.scale.x = target_scale.x;
if (constraint.copy_y)
transform.world.scale.y = target_scale.y;
if (constraint.copy_z)
transform.world.scale.z = target_scale.z;
}
} }
} }
void constraint::handle_copy_scale_constraint(component::transform& transform, entity::id constraint_eid)
void constraint::handle_copy_transform_constraint(component::transform& transform, const component::constraint::copy_transform& constraint)
{ {
const auto& params = registry.get<component::constraint::copy_scale>(constraint_eid);
if (this->registry.has<component::transform>(params.target))
if (registry.valid(constraint.target))
{ {
const auto& target_scale = registry.get<component::transform>(params.target).world.scale;
if (params.copy_x)
transform.world.scale.x = target_scale.x;
if (params.copy_y)
transform.world.scale.y = target_scale.y;
if (params.copy_z)
transform.world.scale.z = target_scale.z;
const component::transform* target_transform = registry.try_get<component::transform>(constraint.target);
if (target_transform)
{
transform.world = target_transform->world;
}
} }
} }
void constraint::handle_copy_transform_constraint(component::transform& transform, entity::id constraint_eid)
void constraint::handle_copy_translation_constraint(component::transform& transform, const component::constraint::copy_translation& constraint)
{ {
const auto& params = registry.get<component::constraint::copy_transform>(constraint_eid);
if (this->registry.has<component::transform>(params.target))
if (registry.valid(constraint.target))
{ {
const auto& target_transform = registry.get<component::transform>(params.target).world;
transform.world = target_transform;
const component::transform* target_transform = registry.try_get<component::transform>(constraint.target);
if (target_transform)
{
const auto& target_translation = target_transform->world.translation;
if (constraint.offset)
{
if (constraint.copy_x)
transform.world.translation.x += (constraint.invert_x) ? -target_translation.x : target_translation.x;
if (constraint.copy_y)
transform.world.translation.y += (constraint.invert_y) ? -target_translation.y : target_translation.y;
if (constraint.copy_z)
transform.world.translation.z += (constraint.invert_z) ? -target_translation.z : target_translation.z;
}
else
{
if (constraint.copy_x)
transform.world.translation.x = (constraint.invert_x) ? -target_translation.x : target_translation.x;
if (constraint.copy_y)
transform.world.translation.y = (constraint.invert_y) ? -target_translation.y : target_translation.y;
if (constraint.copy_z)
transform.world.translation.z = (constraint.invert_z) ? -target_translation.z : target_translation.z;
}
}
} }
} }
void constraint::handle_track_to_constraint(component::transform& transform, entity::id constraint_eid)
void constraint::handle_pivot_constraint(component::transform& transform, const component::constraint::pivot& constraint)
{ {
const auto& params = registry.get<component::constraint::track_to>(constraint_eid);
if (this->registry.has<component::transform>(params.target))
if (registry.valid(constraint.target))
{ {
const auto& target_transform = registry.get<component::transform>(params.target).world;
transform.world.rotation = math::look_rotation(math::normalize(math::sub(target_transform.translation, transform.world.translation)), params.up);
const component::transform* target_transform = registry.try_get<component::transform>(constraint.target);
if (target_transform)
{
// Get pivot center point
const float3 pivot_center = target_transform->world.translation + constraint.offset;
// Pivot translation
transform.world.translation = pivot_center + transform.world.rotation * (transform.world.translation - pivot_center);
}
} }
} }
void constraint::handle_three_dof_constraint(component::transform& transform, entity::id constraint_eid)
void constraint::handle_spring_rotation_constraint(component::transform& transform, component::constraint::spring_rotation& constraint, float dt)
{ {
const auto& params = registry.get<component::constraint::three_dof>(constraint_eid);
// Solve yaw, pitch, and roll angle spring
solve_numeric_spring<float3, float>(constraint.spring, dt);
// Build yaw, pitch, and roll quaternions
const math::quaternion<float> yaw = math::angle_axis(constraint.spring.x0[0], {0.0f, 1.0f, 0.0f});
const math::quaternion<float> pitch = math::angle_axis(constraint.spring.x0[1], {-1.0f, 0.0f, 0.0f});
const math::quaternion<float> roll = math::angle_axis(constraint.spring.x0[2], {0.0f, 0.0f, -1.0f});
const math::quaternion<float> yaw = math::angle_axis(params.yaw, {0.0f, 1.0f, 0.0f});
const math::quaternion<float> pitch = math::angle_axis(params.pitch, {-1.0f, 0.0f, 0.0f});
const math::quaternion<float> roll = math::angle_axis(params.roll, {0.0f, 0.0f, -1.0f});
transform.local.rotation = math::normalize(yaw * pitch * roll);
// Update transform rotation
transform.world.rotation = math::normalize(yaw * pitch * roll);
} }
void constraint::handle_spring_to_constraint(component::transform& transform, entity::id constraint_eid, float dt)
void constraint::handle_spring_to_constraint(component::transform& transform, component::constraint::spring_to& constraint, float dt)
{ {
auto& params = registry.get<component::constraint::spring_to>(constraint_eid);
if (this->registry.has<component::transform>(params.target))
if (registry.valid(constraint.target))
{ {
// Get transform of target entity
const auto& target_transform = registry.get<component::transform>(params.target).world;
// Spring translation
if (params.spring_translation)
const component::transform* target_transform = registry.try_get<component::transform>(constraint.target);
if (target_transform)
{ {
// Update translation spring target
params.translation.x1 = target_transform.translation;
// Solve translation spring
solve_numeric_spring<float3, float>(params.translation, dt);
// Spring translation
if (constraint.spring_translation)
{
// Update translation spring target
constraint.translation.x1 = target_transform->world.translation;
// Solve translation spring
solve_numeric_spring<float3, float>(constraint.translation, dt);
// Update transform translation
transform.world.translation = constraint.translation.x0;
}
// Update transform translation
transform.world.translation = params.translation.x0;
// Spring rotation
if (constraint.spring_rotation)
{
// Update rotation spring target
constraint.rotation.x1 =
{
target_transform->world.rotation.w,
target_transform->world.rotation.x,
target_transform->world.rotation.y,
target_transform->world.rotation.z
};
// Solve rotation spring
solve_numeric_spring<float4, float>(constraint.rotation, dt);
// Update transform rotation
transform.world.rotation = math::normalize(math::quaternion<float>{constraint.rotation.x0[0], constraint.rotation.x0[1], constraint.rotation.x0[2], constraint.rotation.x0[3]});
}
} }
// Spring rotation
if (params.spring_rotation)
}
}
void constraint::handle_spring_translation_constraint(component::transform& transform, component::constraint::spring_translation& constraint, float dt)
{
// Solve translation spring
solve_numeric_spring<float3, float>(constraint.spring, dt);
// Update transform translation
transform.world.translation = constraint.spring.x0;
}
void constraint::handle_three_dof_constraint(component::transform& transform, const component::constraint::three_dof& constraint)
{
const math::quaternion<float> yaw = math::angle_axis(constraint.yaw, {0.0f, 1.0f, 0.0f});
const math::quaternion<float> pitch = math::angle_axis(constraint.pitch, {-1.0f, 0.0f, 0.0f});
const math::quaternion<float> roll = math::angle_axis(constraint.roll, {0.0f, 0.0f, -1.0f});
transform.world.rotation = math::normalize(yaw * pitch * roll);
}
void constraint::handle_track_to_constraint(component::transform& transform, const component::constraint::track_to& constraint)
{
if (registry.valid(constraint.target))
{
const component::transform* target_transform = registry.try_get<component::transform>(constraint.target);
if (target_transform)
{ {
// Update rotation spring target
params.rotation.x1 = {target_transform.rotation.w, target_transform.rotation.x, target_transform.rotation.y, target_transform.rotation.z};
// Solve rotation spring
solve_numeric_spring<float4, float>(params.rotation, dt);
// Update transform rotation
transform.world.rotation = math::normalize(math::quaternion<float>{params.rotation.x0[0], params.rotation.x0[1], params.rotation.x0[2], params.rotation.x0[3]});
transform.world.rotation = math::look_rotation(math::normalize(math::sub(target_transform->world.translation, transform.world.translation)), constraint.up);
} }
} }
} }

+ 16
- 8
src/game/system/constraint.hpp View File

@ -22,6 +22,7 @@
#include "game/system/updatable.hpp" #include "game/system/updatable.hpp"
#include "game/component/transform.hpp" #include "game/component/transform.hpp"
#include "game/component/constraint/constraint.hpp"
#include "entity/id.hpp" #include "entity/id.hpp"
namespace game { namespace game {
@ -39,18 +40,25 @@ class constraint:
{ {
public: public:
constraint(entity::registry& registry); constraint(entity::registry& registry);
~constraint();
virtual void update(double t, double dt); virtual void update(double t, double dt);
private: private:
void handle_constraint(component::transform& transform, entity::id constraint_eid, float dt);
void on_constraint_stack_update(entity::registry& registry, entity::id constraint_stack_eid);
void handle_copy_translation_constraint(component::transform& transform, entity::id constraint_eid);
void handle_copy_rotation_constraint(component::transform& transform, entity::id constraint_eid);
void handle_copy_scale_constraint(component::transform& transform, entity::id constraint_eid);
void handle_copy_transform_constraint(component::transform& transform, entity::id constraint_eid);
void handle_track_to_constraint(component::transform& transform, entity::id constraint_eid);
void handle_three_dof_constraint(component::transform& transform, entity::id constraint_eid);
void handle_spring_to_constraint(component::transform& transform, entity::id constraint_eid, float dt);
void handle_constraint(component::transform& transform, entity::id constraint_eid, float dt);
void handle_child_of_constraint(component::transform& transform, const component::constraint::child_of& constraint);
void handle_copy_rotation_constraint(component::transform& transform, const component::constraint::copy_rotation& constraint);
void handle_copy_scale_constraint(component::transform& transform, const component::constraint::copy_scale& constraint);
void handle_copy_transform_constraint(component::transform& transform, const component::constraint::copy_transform& constraint);
void handle_copy_translation_constraint(component::transform& transform, const component::constraint::copy_translation& constraint);
void handle_pivot_constraint(component::transform& transform, const component::constraint::pivot& constraint);
void handle_spring_rotation_constraint(component::transform& transform, component::constraint::spring_rotation& constraint, float dt);
void handle_spring_to_constraint(component::transform& transform, component::constraint::spring_to& constraint, float dt);
void handle_spring_translation_constraint(component::transform& transform, component::constraint::spring_translation& constraint, float dt);
void handle_three_dof_constraint(component::transform& transform, const component::constraint::three_dof& constraint);
void handle_track_to_constraint(component::transform& transform, const component::constraint::track_to& constraint);
}; };
} // namespace system } // namespace system

+ 6
- 4
src/game/system/orbit.cpp View File

@ -31,13 +31,13 @@ orbit::orbit(entity::registry& registry):
time_scale(1.0) time_scale(1.0)
{ {
registry.on_construct<game::component::orbit>().connect<&orbit::on_orbit_construct>(this); registry.on_construct<game::component::orbit>().connect<&orbit::on_orbit_construct>(this);
registry.on_replace<game::component::orbit>().connect<&orbit::on_orbit_replace>(this);
registry.on_update<game::component::orbit>().connect<&orbit::on_orbit_update>(this);
} }
orbit::~orbit() orbit::~orbit()
{ {
registry.on_construct<game::component::orbit>().disconnect<&orbit::on_orbit_construct>(this); registry.on_construct<game::component::orbit>().disconnect<&orbit::on_orbit_construct>(this);
registry.on_replace<game::component::orbit>().disconnect<&orbit::on_orbit_replace>(this);
registry.on_update<game::component::orbit>().disconnect<&orbit::on_orbit_update>(this);
} }
void orbit::update(double t, double dt) void orbit::update(double t, double dt)
@ -84,13 +84,15 @@ void orbit::set_time_scale(double scale)
time_scale = scale; time_scale = scale;
} }
void orbit::on_orbit_construct(entity::registry& registry, entity::id entity_id, game::component::orbit& component)
void orbit::on_orbit_construct(entity::registry& registry, entity::id entity_id)
{ {
const game::component::orbit& component = registry.get<game::component::orbit>(entity_id);
ephemeris_indices.insert(component.ephemeris_index); ephemeris_indices.insert(component.ephemeris_index);
} }
void orbit::on_orbit_replace(entity::registry& registry, entity::id entity_id, game::component::orbit& component)
void orbit::on_orbit_update(entity::registry& registry, entity::id entity_id)
{ {
const game::component::orbit& component = registry.get<game::component::orbit>(entity_id);
ephemeris_indices.insert(component.ephemeris_index); ephemeris_indices.insert(component.ephemeris_index);
} }

+ 2
- 2
src/game/system/orbit.hpp View File

@ -70,8 +70,8 @@ public:
void set_ephemeris(const physics::orbit::ephemeris<double>* ephemeris); void set_ephemeris(const physics::orbit::ephemeris<double>* ephemeris);
private: private:
void on_orbit_construct(entity::registry& registry, entity::id entity_id, game::component::orbit& component);
void on_orbit_replace(entity::registry& registry, entity::id entity_id, game::component::orbit& component);
void on_orbit_construct(entity::registry& registry, entity::id entity_id);
void on_orbit_update(entity::registry& registry, entity::id entity_id);
const physics::orbit::ephemeris<double>* ephemeris; const physics::orbit::ephemeris<double>* ephemeris;
double time; double time;

+ 13
- 5
src/game/system/proteome.cpp View File

@ -29,19 +29,27 @@ proteome::proteome(entity::registry& registry):
updatable(registry) updatable(registry)
{ {
registry.on_construct<game::component::genome>().connect<&proteome::on_genome_construct>(this); registry.on_construct<game::component::genome>().connect<&proteome::on_genome_construct>(this);
registry.on_replace<game::component::genome>().connect<&proteome::on_genome_replace>(this);
registry.on_update<game::component::genome>().connect<&proteome::on_genome_update>(this);
}
proteome::~proteome()
{
registry.on_construct<game::component::genome>().disconnect<&proteome::on_genome_construct>(this);
registry.on_update<game::component::genome>().disconnect<&proteome::on_genome_update>(this);
} }
void proteome::update(double t, double dt) void proteome::update(double t, double dt)
{} {}
void proteome::on_genome_construct(entity::registry& registry, entity::id entity_id, game::component::genome& genome)
void proteome::on_genome_construct(entity::registry& registry, entity::id entity_id)
{ {
on_genome_replace(registry, entity_id, genome);
on_genome_update(registry, entity_id);
} }
void proteome::on_genome_replace(entity::registry& registry, entity::id entity_id, game::component::genome& genome)
void proteome::on_genome_update(entity::registry& registry, entity::id entity_id)
{ {
game::component::genome& genome = registry.get<game::component::genome>(entity_id);
// Allocate a proteome component // Allocate a proteome component
game::component::proteome proteome_component; game::component::proteome proteome_component;
@ -67,7 +75,7 @@ void proteome::on_genome_replace(entity::registry& registry, entity::id entity_i
} }
// Assign or replace the entity's proteome component // Assign or replace the entity's proteome component
registry.assign_or_replace<game::component::proteome>(entity_id, proteome_component);
registry.emplace_or_replace<game::component::proteome>(entity_id, proteome_component);
} }
} // namespace system } // namespace system

+ 3
- 2
src/game/system/proteome.hpp View File

@ -35,6 +35,7 @@ class proteome:
{ {
public: public:
proteome(entity::registry& registry); proteome(entity::registry& registry);
~proteome();
/** /**
* Scales then adds the timestep `dt` to the current time, then recalculates the positions of proteomeing bodies. * Scales then adds the timestep `dt` to the current time, then recalculates the positions of proteomeing bodies.
@ -45,8 +46,8 @@ public:
virtual void update(double t, double dt); virtual void update(double t, double dt);
private: private:
void on_genome_construct(entity::registry& registry, entity::id entity_id, game::component::genome& genome);
void on_genome_replace(entity::registry& registry, entity::id entity_id, game::component::genome& genome);
void on_genome_construct(entity::registry& registry, entity::id entity_id);
void on_genome_update(entity::registry& registry, entity::id entity_id);
}; };
} // namespace system } // namespace system

+ 25
- 10
src/game/system/render.cpp View File

@ -36,14 +36,23 @@ render::render(entity::registry& registry):
renderer(nullptr) renderer(nullptr)
{ {
registry.on_construct<component::model>().connect<&render::on_model_construct>(this); registry.on_construct<component::model>().connect<&render::on_model_construct>(this);
registry.on_replace<component::model>().connect<&render::on_model_replace>(this);
registry.on_update<component::model>().connect<&render::on_model_update>(this);
registry.on_destroy<component::model>().connect<&render::on_model_destroy>(this); registry.on_destroy<component::model>().connect<&render::on_model_destroy>(this);
registry.on_construct<component::light>().connect<&render::on_light_construct>(this); registry.on_construct<component::light>().connect<&render::on_light_construct>(this);
registry.on_replace<component::light>().connect<&render::on_light_replace>(this);
registry.on_update<component::light>().connect<&render::on_light_update>(this);
registry.on_destroy<component::light>().connect<&render::on_light_destroy>(this); registry.on_destroy<component::light>().connect<&render::on_light_destroy>(this);
} }
render::~render()
{
registry.on_construct<component::model>().disconnect<&render::on_model_construct>(this);
registry.on_update<component::model>().disconnect<&render::on_model_update>(this);
registry.on_destroy<component::model>().disconnect<&render::on_model_destroy>(this);
registry.on_construct<component::light>().disconnect<&render::on_light_construct>(this);
registry.on_update<component::light>().disconnect<&render::on_light_update>(this);
registry.on_destroy<component::light>().disconnect<&render::on_light_destroy>(this);
}
void render::update(double t, double dt) void render::update(double t, double dt)
{ {
this->t = t; this->t = t;
@ -196,16 +205,19 @@ void render::update_light(entity::id entity_id, game::component::light& componen
} }
} }
void render::on_model_construct(entity::registry& registry, entity::id entity_id, component::model& model)
void render::on_model_construct(entity::registry& registry, entity::id entity_id)
{ {
game::component::model& component = registry.get<game::component::model>(entity_id);
scene::model_instance* model_instance = new scene::model_instance(); scene::model_instance* model_instance = new scene::model_instance();
model_instances[entity_id] = model_instance; model_instances[entity_id] = model_instance;
update_model_and_materials(entity_id, model);
update_model_and_materials(entity_id, component);
} }
void render::on_model_replace(entity::registry& registry, entity::id entity_id, component::model& model)
void render::on_model_update(entity::registry& registry, entity::id entity_id)
{ {
update_model_and_materials(entity_id, model);
game::component::model& component = registry.get<game::component::model>(entity_id);
update_model_and_materials(entity_id, component);
} }
void render::on_model_destroy(entity::registry& registry, entity::id entity_id) void render::on_model_destroy(entity::registry& registry, entity::id entity_id)
@ -223,8 +235,10 @@ void render::on_model_destroy(entity::registry& registry, entity::id entity_id)
} }
} }
void render::on_light_construct(entity::registry& registry, entity::id entity_id, component::light& component)
void render::on_light_construct(entity::registry& registry, entity::id entity_id)
{ {
game::component::light& component = registry.get<game::component::light>(entity_id);
scene::light* light = nullptr; scene::light* light = nullptr;
switch (component.type) switch (component.type)
@ -259,9 +273,10 @@ void render::on_light_construct(entity::registry& registry, entity::id entity_id
} }
} }
void render::on_light_replace(entity::registry& registry, entity::id entity_id, component::light& light)
void render::on_light_update(entity::registry& registry, entity::id entity_id)
{ {
update_light(entity_id, light);
game::component::light& component = registry.get<game::component::light>(entity_id);
update_light(entity_id, component);
} }
void render::on_light_destroy(entity::registry& registry, entity::id entity_id) void render::on_light_destroy(entity::registry& registry, entity::id entity_id)

+ 6
- 5
src/game/system/render.hpp View File

@ -38,6 +38,8 @@ class render: public updatable
{ {
public: public:
render(entity::registry& registry); render(entity::registry& registry);
~render();
virtual void update(double t, double dt); virtual void update(double t, double dt);
void draw(double alpha); void draw(double alpha);
@ -55,12 +57,11 @@ private:
void update_model_and_materials(entity::id entity_id, game::component::model& model); void update_model_and_materials(entity::id entity_id, game::component::model& model);
void update_light(entity::id entity_id, game::component::light& component); void update_light(entity::id entity_id, game::component::light& component);
void on_model_construct(entity::registry& registry, entity::id entity_id, game::component::model& model);
void on_model_replace(entity::registry& registry, entity::id entity_id, game::component::model& model);
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_model_destroy(entity::registry& registry, entity::id entity_id);
void on_light_construct(entity::registry& registry, entity::id entity_id, game::component::light& light);
void on_light_replace(entity::registry& registry, entity::id entity_id, game::component::light& light);
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); void on_light_destroy(entity::registry& registry, entity::id entity_id);
double t; double t;

+ 11
- 26
src/game/system/spatial.cpp View File

@ -18,42 +18,27 @@
*/ */
#include "spatial.hpp" #include "spatial.hpp"
#include "game/component/parent.hpp"
#include "game/component/transform.hpp" #include "game/component/transform.hpp"
#include "game/component/constraint-stack.hpp"
#include <iostream>
namespace game { namespace game {
namespace system { namespace system {
spatial::spatial(entity::registry& registry): spatial::spatial(entity::registry& registry):
updatable(registry)
updatable(registry),
updated_unconstrained_transforms(registry, entt::collector.update<component::transform>().where(entt::exclude<component::constraint_stack>))
{} {}
void spatial::update(double t, double dt) void spatial::update(double t, double dt)
{ {
/// @TODO: sort transforms by parent, for more multi-level hierarchies
// Process parent transforms first
registry.view<component::transform>().each(
[&](entity::id entity_id, auto& transform)
{
if (!this->registry.has<component::parent>(entity_id))
{
transform.world = transform.local;
}
});
// Process child transforms second
registry.view<component::transform>().each(
[&](entity::id entity_id, auto& transform)
{
if (this->registry.has<component::parent>(entity_id))
{
entity::id parent = this->registry.get<component::parent>(entity_id).parent;
const component::transform& parent_transform = this->registry.get<component::transform>(parent);
transform.world = parent_transform.world * transform.local;
transform.warp = parent_transform.warp;
}
});
// Update world-space transforms of all updated, unconstrained transforms
for (const auto transform_eid: updated_unconstrained_transforms)
{
auto& transform = registry.get<component::transform>(transform_eid);
transform.world = transform.local;
}
updated_unconstrained_transforms.clear();
} }
} // namespace system } // namespace system

+ 5
- 0
src/game/system/spatial.hpp View File

@ -21,6 +21,7 @@
#define ANTKEEPER_GAME_SYSTEM_SPATIAL_HPP #define ANTKEEPER_GAME_SYSTEM_SPATIAL_HPP
#include "game/system/updatable.hpp" #include "game/system/updatable.hpp"
#include <entt/entt.hpp>
namespace game { namespace game {
namespace system { namespace system {
@ -31,6 +32,10 @@ class spatial:
public: public:
spatial(entity::registry& registry); spatial(entity::registry& registry);
virtual void update(double t, double dt); virtual void update(double t, double dt);
private:
/// Observes entities with updated, unconstrained transforms.
entt::observer updated_unconstrained_transforms;
}; };
} // namespace system } // namespace system

+ 9
- 2
src/game/system/steering.cpp View File

@ -83,8 +83,15 @@ void steering::update(double t, double dt)
} }
// Update transform // Update transform
transform.local.translation = agent.position;
transform.local.rotation = agent.orientation;
registry.patch<game::component::transform>
(
entity_id,
[&agent](auto& component)
{
component.local.translation = agent.position;
component.local.rotation = agent.orientation;
}
);
} }
); );
} }

+ 5
- 2
src/game/system/terrain.cpp View File

@ -68,7 +68,10 @@ terrain::terrain(entity::registry& registry):
} }
terrain::~terrain() terrain::~terrain()
{}
{
registry.on_construct<component::terrain>().disconnect<&terrain::on_terrain_construct>(this);
registry.on_destroy<component::terrain>().disconnect<&terrain::on_terrain_destroy>(this);
}
void terrain::update(double t, double dt) void terrain::update(double t, double dt)
{ {
@ -294,7 +297,7 @@ void terrain::set_max_error(double error)
max_error = error; max_error = error;
} }
void terrain::on_terrain_construct(entity::registry& registry, entity::id entity_id, component::terrain& component)
void terrain::on_terrain_construct(entity::registry& registry, entity::id entity_id)
{ {
terrain_quadsphere* quadsphere = new terrain_quadsphere(); terrain_quadsphere* quadsphere = new terrain_quadsphere();
terrain_quadspheres[entity_id] = quadsphere; terrain_quadspheres[entity_id] = quadsphere;

+ 1
- 1
src/game/system/terrain.hpp View File

@ -101,7 +101,7 @@ private:
static double screen_space_error(double horizontal_fov, double horizontal_resolution, double distance, double geometric_error); static double screen_space_error(double horizontal_fov, double horizontal_resolution, double distance, double geometric_error);
void on_terrain_construct(entity::registry& registry, entity::id entity_id, game::component::terrain& component);
void on_terrain_construct(entity::registry& registry, entity::id entity_id);
void on_terrain_destroy(entity::registry& registry, entity::id entity_id); void on_terrain_destroy(entity::registry& registry, entity::id entity_id);
/** /**

+ 5
- 2
src/game/system/vegetation.cpp View File

@ -45,7 +45,10 @@ vegetation::vegetation(entity::registry& registry):
} }
vegetation::~vegetation() vegetation::~vegetation()
{}
{
registry.on_construct<component::terrain>().disconnect<&vegetation::on_terrain_construct>(this);
registry.on_destroy<component::terrain>().disconnect<&vegetation::on_terrain_destroy>(this);
}
void vegetation::update(double t, double dt) void vegetation::update(double t, double dt)
{} {}
@ -79,7 +82,7 @@ void vegetation::set_scene(scene::collection* collection)
this->scene_collection = collection; this->scene_collection = collection;
} }
void vegetation::on_terrain_construct(entity::registry& registry, entity::id entity_id, component::terrain& component)
void vegetation::on_terrain_construct(entity::registry& registry, entity::id entity_id)
{} {}
void vegetation::on_terrain_destroy(entity::registry& registry, entity::id entity_id) void vegetation::on_terrain_destroy(entity::registry& registry, entity::id entity_id)

+ 1
- 1
src/game/system/vegetation.hpp View File

@ -61,7 +61,7 @@ public:
void set_scene(scene::collection* collection); void set_scene(scene::collection* collection);
private: private:
void on_terrain_construct(entity::registry& registry, entity::id entity_id, game::component::terrain& component);
void on_terrain_construct(entity::registry& registry, entity::id entity_id);
void on_terrain_destroy(entity::registry& registry, entity::id entity_id); void on_terrain_destroy(entity::registry& registry, entity::id entity_id);
float terrain_patch_size; float terrain_patch_size;

+ 0
- 108
src/game/tools.cpp View File

@ -1,108 +0,0 @@
/*
* Copyright (C) 2021 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/tools.hpp"
#include "application.hpp"
#include "animation/animator.hpp"
#include "animation/animation.hpp"
#include "game/component/tool.hpp"
#include "game/component/celestial-body.hpp"
#include "utility/timestamp.hpp"
#include "render/material.hpp"
#include "game/graphics.hpp"
namespace game {
entity::id build_camera_tool(game::context& ctx)
{
// Create camera tool entity
entity::id tool_eid = ctx.entity_registry->create();
// Create tool component
game::component::tool tool;
// Setup tool activated callback
tool.activated = [&ctx]()
{
if (!ctx.camera_flash_animation->is_stopped())
return;
game::graphics::save_screenshot(ctx);
render::material_property<float4>* tint = static_cast<render::material_property<float4>*>(ctx.camera_flash_billboard->get_material()->get_property("tint"));
tint->set_value({1.0f, 1.0f, 1.0f, 1.0f});
ctx.camera_flash_billboard->get_material()->update_tweens();
ctx.ui_scene->add_object(ctx.camera_flash_billboard);
ctx.camera_flash_animation->set_end_callback
(
[&ctx]()
{
ctx.ui_scene->remove_object(ctx.camera_flash_billboard);
}
);
ctx.camera_flash_animation->set_frame_callback
(
[&ctx, tint](int channel, const float& opacity)
{
tint->set_value({1.0f, 1.0f, 1.0f, opacity});
}
);
ctx.animator->remove_animation(ctx.camera_flash_animation);
ctx.animator->add_animation(ctx.camera_flash_animation);
ctx.camera_flash_animation->rewind();
ctx.camera_flash_animation->play();
};
// Add tool component to camera tool entity
ctx.entity_registry->assign<game::component::tool>(tool_eid, tool);
return tool_eid;
}
entity::id build_time_tool(game::context& ctx)
{
// Create time tool entity
entity::id tool_eid = ctx.entity_registry->create();
// Create tool component
game::component::tool tool;
// Setup tool active calback
tool.active = [&ctx]()
{
//auto mouse = ctx.app->get_mouse()->get_current_position();
//auto window = ctx.app->get_viewport_dimensions();
entity::id planet_eid = ctx.entities["planet"];
game::component::celestial_body body = ctx.entity_registry->get<game::component::celestial_body>(planet_eid);
//body.axial_rotation = math::radians(360.0f) * ((float)mouse_x / (float)window_w);
ctx.entity_registry->replace<game::component::celestial_body>(planet_eid, body);
};
// Add tool component to time tool entity
ctx.entity_registry->assign<game::component::tool>(tool_eid, tool);
return tool_eid;
}
} // namespace game

+ 6
- 2
src/game/world.cpp View File

@ -30,6 +30,7 @@
#include "game/component/observer.hpp" #include "game/component/observer.hpp"
#include "game/system/astronomy.hpp" #include "game/system/astronomy.hpp"
#include "game/system/orbit.hpp" #include "game/system/orbit.hpp"
#include "game/system/atmosphere.hpp"
#include "entity/commands.hpp" #include "entity/commands.hpp"
#include "entity/archetype.hpp" #include "entity/archetype.hpp"
#include "geom/spherical.hpp" #include "geom/spherical.hpp"
@ -115,7 +116,10 @@ void create_observer(game::context& ctx)
observer.longitude = 0.0; observer.longitude = 0.0;
// Assign observer component to observer entity // Assign observer component to observer entity
ctx.entity_registry->assign<game::component::observer>(observer_eid, observer);
ctx.entity_registry->emplace<game::component::observer>(observer_eid, observer);
// Set atmosphere system active atmosphere
ctx.atmosphere_system->set_active_atmosphere(observer.reference_body_eid);
// Set astronomy system observer // Set astronomy system observer
ctx.astronomy_system->set_observer(observer_eid); ctx.astronomy_system->set_observer(observer_eid);
@ -496,7 +500,7 @@ void create_earth(game::context& ctx)
}; };
terrain.max_lod = 0; terrain.max_lod = 0;
terrain.patch_material = nullptr; terrain.patch_material = nullptr;
//ctx.entity_registry->assign<game::component::terrain>(earth_eid, terrain);
//ctx.entity_registry->emplace<game::component::terrain>(earth_eid, terrain);
} }
catch (const std::exception&) catch (const std::exception&)
{ {

+ 2
- 2
src/render/material-property.hpp View File

@ -249,7 +249,7 @@ bool material_property::upload(double a) const
{ {
for (std::size_t i = 0; i < element_count; ++i) for (std::size_t i = 0; i < element_count; ++i)
{ {
if (!input->upload(i, values[i].interpolate(a)))
if (!input->upload(i, values[i].interpolate(static_cast<float>(a))))
return false; return false;
} }
@ -257,7 +257,7 @@ bool material_property::upload(double a) const
} }
else else
{ {
return input->upload(values[0].interpolate(a));
return input->upload(values[0].interpolate(static_cast<float>(a)));
} }
} }

+ 69
- 14
src/resources/entity-archetype-loader.cpp View File

@ -76,7 +76,13 @@ static bool load_component_atmosphere(entity::archetype& archetype, const json&
component.airglow_illuminance.z = airglow_illuminance[2].get<double>(); component.airglow_illuminance.z = airglow_illuminance[2].get<double>();
} }
archetype.set<game::component::atmosphere>(component);
archetype.stamps.push_back
(
[component](entt::handle& handle)
{
handle.emplace_or_replace<decltype(component)>(component);
}
);
return true; return true;
} }
@ -91,7 +97,13 @@ static bool load_component_behavior(entity::archetype& archetype, resource_manag
component.behavior_tree = resource_manager.load<entity::ebt::node>(element["file"].get<std::string>()); component.behavior_tree = resource_manager.load<entity::ebt::node>(element["file"].get<std::string>());
} }
archetype.set<game::component::behavior>(component);
archetype.stamps.push_back
(
[component](entt::handle& handle)
{
handle.emplace_or_replace<decltype(component)>(component);
}
);
return (component.behavior_tree != nullptr); return (component.behavior_tree != nullptr);
} }
@ -104,7 +116,13 @@ static bool load_component_blackbody(entity::archetype& archetype, const json& e
if (element.contains("temperature")) if (element.contains("temperature"))
component.temperature = element["temperature"].get<double>(); component.temperature = element["temperature"].get<double>();
archetype.set<game::component::blackbody>(component);
archetype.stamps.push_back
(
[component](entt::handle& handle)
{
handle.emplace_or_replace<decltype(component)>(component);
}
);
return true; return true;
} }
@ -140,8 +158,14 @@ static bool load_component_celestial_body(entity::archetype& archetype, const js
} }
if (element.contains("albedo")) if (element.contains("albedo"))
component.albedo = element["albedo"].get<double>(); component.albedo = element["albedo"].get<double>();
archetype.set<game::component::celestial_body>(component);
archetype.stamps.push_back
(
[component](entt::handle& handle)
{
handle.emplace_or_replace<decltype(component)>(component);
}
);
return true; return true;
} }
@ -156,7 +180,13 @@ static bool load_component_collision(entity::archetype& archetype, resource_mana
component.mesh = resource_manager.load<geom::mesh>(element["file"].get<std::string>()); component.mesh = resource_manager.load<geom::mesh>(element["file"].get<std::string>());
} }
archetype.set<game::component::collision>(component);
archetype.stamps.push_back
(
[component](entt::handle& handle)
{
handle.emplace_or_replace<decltype(component)>(component);
}
);
return (component.mesh != nullptr); return (component.mesh != nullptr);
} }
@ -168,8 +198,14 @@ static bool load_component_diffuse_reflector(entity::archetype& archetype, const
if (element.contains("albedo")) if (element.contains("albedo"))
component.albedo = element["albedo"].get<double>(); component.albedo = element["albedo"].get<double>();
archetype.set<game::component::diffuse_reflector>(component);
archetype.stamps.push_back
(
[component](entt::handle& handle)
{
handle.emplace_or_replace<decltype(component)>(component);
}
);
return true; return true;
} }
@ -186,7 +222,13 @@ static bool load_component_model(entity::archetype& archetype, resource_manager&
component.render_model = resource_manager.load<render::model>(element["file"].get<std::string>()); component.render_model = resource_manager.load<render::model>(element["file"].get<std::string>());
} }
archetype.set<game::component::model>(component);
archetype.stamps.push_back
(
[component](entt::handle& handle)
{
handle.emplace_or_replace<decltype(component)>(component);
}
);
return true; return true;
} }
@ -204,8 +246,14 @@ static bool load_component_orbit(entity::archetype& archetype, const json& eleme
component.ephemeris_index = element["ephemeris_index"].get<int>(); component.ephemeris_index = element["ephemeris_index"].get<int>();
if (element.contains("scale")) if (element.contains("scale"))
component.scale = element["scale"].get<double>(); component.scale = element["scale"].get<double>();
archetype.set<game::component::orbit>(component);
archetype.stamps.push_back
(
[component](entt::handle& handle)
{
handle.emplace_or_replace<decltype(component)>(component);
}
);
return true; return true;
} }
@ -242,7 +290,14 @@ static bool load_component_transform(entity::archetype& archetype, const json& e
} }
component.world = component.local; component.world = component.local;
archetype.set<game::component::transform>(component);
archetype.stamps.push_back
(
[component](entt::handle& handle)
{
handle.emplace_or_replace<decltype(component)>(component);
}
);
return true; return true;
} }
@ -270,14 +325,14 @@ static bool load_component(entity::archetype& archetype, resource_manager& resou
//throw std::runtime_error("Unknown component type \"" + element.key() + "\""); //throw std::runtime_error("Unknown component type \"" + element.key() + "\"");
return true;
return false;
} }
template <> template <>
entity::archetype* resource_loader<entity::archetype>::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) entity::archetype* resource_loader<entity::archetype>::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path)
{ {
// Allocate archetype // Allocate archetype
entity::archetype* archetype = new entity::archetype(resource_manager->get_archetype_registry());
entity::archetype* archetype = new entity::archetype();
// Read file into buffer // Read file into buffer
std::size_t size = static_cast<int>(PHYSFS_fileLength(file)); std::size_t size = static_cast<int>(PHYSFS_fileLength(file));

Loading…
Cancel
Save