From ce5c8b67433f806c31739424f8f9e13373533c4b Mon Sep 17 00:00:00 2001 From: "C. J. Howard" Date: Sun, 9 Oct 2022 10:28:51 +0800 Subject: [PATCH] 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 --- src/animation/spring.hpp | 16 +- src/color/color.hpp | 6 +- src/color/index.hpp | 2 +- src/color/rgb.hpp | 2 +- src/color/ucs.hpp | 2 +- src/color/xyy.hpp | 2 +- src/color/xyz.hpp | 2 +- .../constraints.hpp => entity/archetype.cpp} | 35 +- src/entity/archetype.hpp | 26 +- src/{game/tools.hpp => entity/clone.cpp} | 24 +- src/entity/clone.hpp | 39 + src/entity/commands.cpp | 125 +- src/entity/commands.hpp | 1 - src/entity/ebt.cpp | 13 +- src/game/ant/swarm.cpp | 41 +- src/game/ant/swarm.hpp | 5 +- src/game/component/constraint-stack.hpp | 3 + src/game/component/constraint/child-of.hpp | 43 + src/game/component/constraint/constraint.hpp | 44 + .../copy-rotation.hpp | 0 .../copy-scale.hpp | 0 .../copy-transform.hpp | 0 .../copy-translation.hpp | 0 src/game/component/constraint/pivot.hpp | 46 + .../component/constraint/spring-rotation.hpp | 43 + .../{constraints => constraint}/spring-to.hpp | 0 .../constraint/spring-translation.hpp | 43 + .../{constraints => constraint}/three-dof.hpp | 2 +- .../{constraints => constraint}/track-to.hpp | 0 src/game/context.hpp | 5 +- src/game/controls.cpp | 15 +- src/game/controls.hpp | 3 - src/game/loop.cpp | 22 +- src/game/loop.hpp | 24 +- src/game/state/boot.cpp | 6 +- src/game/state/main-menu.cpp | 4 +- src/game/state/main-menu.hpp | 3 + src/game/state/nest-selection.cpp | 1028 +++++++++++++++++ src/game/state/nest-selection.hpp | 49 + src/game/state/nuptial-flight.cpp | 964 +++++----------- src/game/state/nuptial-flight.hpp | 19 +- src/game/system/astronomy.cpp | 24 +- src/game/system/astronomy.hpp | 8 +- src/game/system/atmosphere.cpp | 102 +- src/game/system/atmosphere.hpp | 15 +- src/game/system/blackbody.cpp | 46 +- src/game/system/blackbody.hpp | 9 +- src/game/system/collision.cpp | 12 +- src/game/system/collision.hpp | 4 +- src/game/system/constraint.cpp | 323 ++++-- src/game/system/constraint.hpp | 24 +- src/game/system/orbit.cpp | 10 +- src/game/system/orbit.hpp | 4 +- src/game/system/proteome.cpp | 18 +- src/game/system/proteome.hpp | 5 +- src/game/system/render.cpp | 35 +- src/game/system/render.hpp | 11 +- src/game/system/spatial.cpp | 37 +- src/game/system/spatial.hpp | 5 + src/game/system/steering.cpp | 11 +- src/game/system/terrain.cpp | 7 +- src/game/system/terrain.hpp | 2 +- src/game/system/vegetation.cpp | 7 +- src/game/system/vegetation.hpp | 2 +- src/game/tools.cpp | 108 -- src/game/world.cpp | 8 +- src/render/material-property.hpp | 4 +- src/resources/entity-archetype-loader.cpp | 83 +- 68 files changed, 2411 insertions(+), 1220 deletions(-) rename src/{game/component/constraints/constraints.hpp => entity/archetype.cpp} (61%) rename src/{game/tools.hpp => entity/clone.cpp} (71%) create mode 100644 src/entity/clone.hpp create mode 100644 src/game/component/constraint/child-of.hpp create mode 100644 src/game/component/constraint/constraint.hpp rename src/game/component/{constraints => constraint}/copy-rotation.hpp (100%) rename src/game/component/{constraints => constraint}/copy-scale.hpp (100%) rename src/game/component/{constraints => constraint}/copy-transform.hpp (100%) rename src/game/component/{constraints => constraint}/copy-translation.hpp (100%) create mode 100644 src/game/component/constraint/pivot.hpp create mode 100644 src/game/component/constraint/spring-rotation.hpp rename src/game/component/{constraints => constraint}/spring-to.hpp (100%) create mode 100644 src/game/component/constraint/spring-translation.hpp rename src/game/component/{constraints => constraint}/three-dof.hpp (97%) rename src/game/component/{constraints => constraint}/track-to.hpp (100%) create mode 100644 src/game/state/nest-selection.cpp create mode 100644 src/game/state/nest-selection.hpp delete mode 100644 src/game/tools.cpp diff --git a/src/animation/spring.hpp b/src/animation/spring.hpp index 497b35a..2c5d2dc 100644 --- a/src/animation/spring.hpp +++ b/src/animation/spring.hpp @@ -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 S Scalar type. @@ -56,7 +56,7 @@ template 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 dt Delta time, in seconds. @@ -87,12 +87,12 @@ T rads_to_hz(T rads); template 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; v = det_v * inv_det; diff --git a/src/color/color.hpp b/src/color/color.hpp index 8be5a20..958cde1 100644 --- a/src/color/color.hpp +++ b/src/color/color.hpp @@ -20,18 +20,18 @@ #ifndef ANTKEEPER_COLOR_HPP #define ANTKEEPER_COLOR_HPP -/// Color manipulation functions. +/// Color manipulation. namespace color {} #include "aces.hpp" +#include "cat.hpp" #include "cct.hpp" +#include "illuminant.hpp" #include "index.hpp" #include "rgb.hpp" #include "srgb.hpp" #include "ucs.hpp" #include "xyy.hpp" #include "xyz.hpp" -#include "cat.hpp" -#include "illuminant.hpp" #endif // ANTKEEPER_COLOR_HPP diff --git a/src/color/index.hpp b/src/color/index.hpp index ff7b552..4b99a70 100644 --- a/src/color/index.hpp +++ b/src/color/index.hpp @@ -22,7 +22,7 @@ namespace color { -/// Functions which operate on color indices. +/// Color indices. namespace index { /** diff --git a/src/color/rgb.hpp b/src/color/rgb.hpp index 4e83a7f..f1ed816 100644 --- a/src/color/rgb.hpp +++ b/src/color/rgb.hpp @@ -25,7 +25,7 @@ namespace color { -/// Functions which operate on various RGB colorspaces. +/// RGB color spaces. namespace rgb { /** diff --git a/src/color/ucs.hpp b/src/color/ucs.hpp index 7b6048b..98a5048 100644 --- a/src/color/ucs.hpp +++ b/src/color/ucs.hpp @@ -24,7 +24,7 @@ namespace color { -/// Functions which operate in the CIE 1960 UCS colorspace. +/// CIE 1960 UCS color space. namespace ucs { /** diff --git a/src/color/xyy.hpp b/src/color/xyy.hpp index 975ffb2..11b4379 100644 --- a/src/color/xyy.hpp +++ b/src/color/xyy.hpp @@ -24,7 +24,7 @@ namespace color { -/// Functions which operate in the CIE xyY colorspace. +/// CIE xyY color space. namespace xyy { /** diff --git a/src/color/xyz.hpp b/src/color/xyz.hpp index 279f55d..6c4dc6c 100644 --- a/src/color/xyz.hpp +++ b/src/color/xyz.hpp @@ -25,7 +25,7 @@ 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 */ diff --git a/src/game/component/constraints/constraints.hpp b/src/entity/archetype.cpp similarity index 61% rename from src/game/component/constraints/constraints.hpp rename to src/entity/archetype.cpp index 611fcff..961449c 100644 --- a/src/game/component/constraints/constraints.hpp +++ b/src/entity/archetype.cpp @@ -17,24 +17,25 @@ * along with Antkeeper source code. If not, see . */ -#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 diff --git a/src/entity/archetype.hpp b/src/entity/archetype.hpp index b0ceb1e..cf86b2e 100644 --- a/src/entity/archetype.hpp +++ b/src/entity/archetype.hpp @@ -20,13 +20,33 @@ #ifndef ANTKEEPER_ENTITY_ARCHETYPE_HPP #define ANTKEEPER_ENTITY_ARCHETYPE_HPP -#include +#include "entity/registry.hpp" +#include "entity/id.hpp" +#include +#include 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> 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 #endif // ANTKEEPER_ENTITY_ARCHETYPE_HPP - diff --git a/src/game/tools.hpp b/src/entity/clone.cpp similarity index 71% rename from src/game/tools.hpp rename to src/entity/clone.cpp index 006e918..e6c987e 100644 --- a/src/game/tools.hpp +++ b/src/entity/clone.cpp @@ -17,17 +17,19 @@ * along with Antkeeper source code. If not, see . */ -#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 diff --git a/src/entity/clone.hpp b/src/entity/clone.hpp new file mode 100644 index 0000000..d7fff71 --- /dev/null +++ b/src/entity/clone.hpp @@ -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 . + */ + +#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 diff --git a/src/entity/commands.cpp b/src/entity/commands.cpp index 0a299d6..6152fc8 100644 --- a/src/entity/commands.cpp +++ b/src/entity/commands.cpp @@ -31,62 +31,105 @@ namespace command { void translate(entity::registry& registry, entity::id eid, const float3& translation) { - if (registry.has(eid)) + const game::component::transform* transform = registry.try_get(eid); + if (transform) { - game::component::transform& transform = registry.get(eid); - transform.local.translation += translation; + registry.patch + ( + eid, + [&translation](auto& transform) + { + transform.local.translation += translation; + } + ); } } void rotate(entity::registry& registry, entity::id eid, float angle, const float3& axis) { - if (registry.has(eid)) + const game::component::transform* transform = registry.try_get(eid); + if (transform) { - game::component::transform& transform = registry.get(eid); - transform.local.rotation = math::angle_axis(angle, axis) * transform.local.rotation; + registry.patch + ( + 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) { - if (registry.has(eid)) + const game::component::transform* transform = registry.try_get(eid); + if (transform) { - game::component::transform& transform = registry.get(eid); - transform.local.translation = position; + registry.patch + ( + eid, + [&position](auto& transform) + { + transform.local.translation = position; + } + ); } } void warp_to(entity::registry& registry, entity::id eid, const float3& position) { - if (registry.has(eid)) + const game::component::transform* transform = registry.try_get(eid); + if (transform) { - game::component::transform& transform = registry.get(eid); - transform.local.translation = position; - transform.warp = true; + registry.patch + ( + eid, + [&position](auto& transform) + { + transform.local.translation = position; + transform.warp = true; + } + ); } } void set_scale(entity::registry& registry, entity::id eid, const float3& scale) { - if (registry.has(eid)) + const game::component::transform* transform = registry.try_get(eid); + if (transform) { - game::component::transform& transform = registry.get(eid); - transform.local.scale = scale; + registry.patch + ( + eid, + [&scale](auto& transform) + { + transform.local.scale = scale; + } + ); } } void set_transform(entity::registry& registry, entity::id eid, const math::transform& transform, bool warp) { - if (registry.has(eid)) + const game::component::transform* transform_component = registry.try_get(eid); + if (transform_component) { - game::component::transform& component = registry.get(eid); - component.local = transform; - component.warp = warp; + registry.patch + ( + 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) { + /* if (registry.has(eid)) { 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(double3{x, y, z}); transform.warp = true; } + */ } void assign_render_layers(entity::registry& registry, entity::id eid, unsigned int layers) { - if (registry.has(eid)) + const game::component::model* model = registry.try_get(eid); + if (model) { - game::component::model model = registry.get(eid); - model.layers = layers; - registry.replace(eid, model); - - // Apply to child layers - registry.view().each( - [&](entity::id eid, auto& component) - { - if (component.parent == eid) - assign_render_layers(registry, eid, layers); - }); + registry.patch + ( + eid, + [layers](auto& model) + { + model.layers = layers; + } + ); } } math::transform get_local_transform(entity::registry& registry, entity::id eid) { - if (registry.has(eid)) + const game::component::transform* transform = registry.try_get(eid); + if (transform) { - const game::component::transform& component = registry.get(eid); - return component.local; + return transform->local; } return math::transform::identity; @@ -148,21 +190,14 @@ math::transform get_local_transform(entity::registry& registry, entity::i math::transform get_world_transform(entity::registry& registry, entity::id eid) { - if (registry.has(eid)) + const game::component::transform* transform = registry.try_get(eid); + if (transform) { - const game::component::transform& component = registry.get(eid); - return component.world; + return transform->world; } return math::transform::identity; } -void parent(entity::registry& registry, entity::id child, entity::id parent) -{ - game::component::parent component; - component.parent = parent; - registry.assign_or_replace(child, component); -} - } // namespace command } // namespace entity diff --git a/src/entity/commands.hpp b/src/entity/commands.hpp index 6e2a223..d18e45f 100644 --- a/src/entity/commands.hpp +++ b/src/entity/commands.hpp @@ -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); math::transform get_local_transform(entity::registry& registry, entity::id eid); math::transform get_world_transform(entity::registry& registry, entity::id eid); -void parent(entity::registry& registry, entity::id child, entity::id parent); } // namespace command } // namespace entity diff --git a/src/entity/ebt.cpp b/src/entity/ebt.cpp index 84926b4..3d233da 100644 --- a/src/entity/ebt.cpp +++ b/src/entity/ebt.cpp @@ -39,8 +39,17 @@ status print_eid(context& context) status warp_to(context& context, float x, float y, float z) { auto& transform = context.registry->get(context.entity_id); - transform.local.translation = {x, y, z}; - transform.warp = true; + + context.registry->patch + ( + context.entity_id, + [x, y, z](auto& component) + { + component.local.translation = {x, y, z}; + component.warp = true; + } + ); + return status::success; } diff --git a/src/game/ant/swarm.cpp b/src/game/ant/swarm.cpp index 156b152..2bcdff4 100644 --- a/src/game/ant/swarm.cpp +++ b/src/game/ant/swarm.cpp @@ -50,7 +50,7 @@ static math::vector3 sphere_random(Generator& rng) return {x * scale, y * scale, z * scale}; } -void create_swarm(game::context& ctx) +entity::id create_swarm(game::context& ctx) { // Determine swarm properties 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 queen_scale = {1, 1, 1}; + // Init transform component + game::component::transform transform; + transform.local = math::transform::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(swarm_eid, transform); + // Init male model component game::component::model male_model; male_model.render_model = ctx.resource_manager->load("male-boid.mdl"); @@ -74,12 +87,6 @@ void create_swarm(game::context& ctx) queen_model.instance_count = 0; queen_model.layers = 1; - // Init transform component - game::component::transform transform; - transform.local = math::transform::identity; - transform.world = transform.local; - transform.warp = true; - // Init steering component game::component::steering steering; steering.agent.mass = 1.0f; @@ -98,7 +105,7 @@ void create_swarm(game::context& ctx) steering.wander_angle = 0.0f; steering.wander_angle2 = 0.0f; steering.seek_weight = 0.2f; - steering.seek_target = {0, 100, 0}; + steering.seek_target = swarm_center; steering.flee_weight = 0.0f; 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; entity::id alate_eid = ctx.entity_registry->create(); - ctx.entity_registry->assign(alate_eid, steering); + ctx.entity_registry->emplace(alate_eid, steering); if (i < male_count) { // Create male - ctx.entity_registry->assign(alate_eid, male_model); + ctx.entity_registry->emplace(alate_eid, male_model); transform.local.scale = male_scale; transform.world = transform.local; - ctx.entity_registry->assign(alate_eid, transform); + ctx.entity_registry->emplace(alate_eid, transform); } else { // Create queen - ctx.entity_registry->assign(alate_eid, queen_model); + ctx.entity_registry->emplace(alate_eid, queen_model); transform.local.scale = queen_scale; transform.world = transform.local; - ctx.entity_registry->assign(alate_eid, transform); + ctx.entity_registry->emplace(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(); ctx.entity_registry->destroy(view.begin(), view.end()); + + // Destroy swarm + ctx.entity_registry->destroy(swarm_eid); } } // namespace ant diff --git a/src/game/ant/swarm.hpp b/src/game/ant/swarm.hpp index 3b60400..dc1e1e3 100644 --- a/src/game/ant/swarm.hpp +++ b/src/game/ant/swarm.hpp @@ -21,12 +21,13 @@ #define ANTKEEPER_GAME_ANT_SWARM_HPP #include "game/context.hpp" +#include "entity/id.hpp" namespace game { 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 game diff --git a/src/game/component/constraint-stack.hpp b/src/game/component/constraint-stack.hpp index e2ed7c5..1f6b686 100644 --- a/src/game/component/constraint-stack.hpp +++ b/src/game/component/constraint-stack.hpp @@ -33,6 +33,9 @@ namespace component { */ struct constraint_stack { + /// Priority number, with lower priorities evaluated first. + int priority; + /// ID of the entity containing the first constraint stack node. entity::id head; }; diff --git a/src/game/component/constraint/child-of.hpp b/src/game/component/constraint/child-of.hpp new file mode 100644 index 0000000..1d33c1b --- /dev/null +++ b/src/game/component/constraint/child-of.hpp @@ -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 . + */ + +#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 diff --git a/src/game/component/constraint/constraint.hpp b/src/game/component/constraint/constraint.hpp new file mode 100644 index 0000000..eab0416 --- /dev/null +++ b/src/game/component/constraint/constraint.hpp @@ -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 . + */ + +#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 diff --git a/src/game/component/constraints/copy-rotation.hpp b/src/game/component/constraint/copy-rotation.hpp similarity index 100% rename from src/game/component/constraints/copy-rotation.hpp rename to src/game/component/constraint/copy-rotation.hpp diff --git a/src/game/component/constraints/copy-scale.hpp b/src/game/component/constraint/copy-scale.hpp similarity index 100% rename from src/game/component/constraints/copy-scale.hpp rename to src/game/component/constraint/copy-scale.hpp diff --git a/src/game/component/constraints/copy-transform.hpp b/src/game/component/constraint/copy-transform.hpp similarity index 100% rename from src/game/component/constraints/copy-transform.hpp rename to src/game/component/constraint/copy-transform.hpp diff --git a/src/game/component/constraints/copy-translation.hpp b/src/game/component/constraint/copy-translation.hpp similarity index 100% rename from src/game/component/constraints/copy-translation.hpp rename to src/game/component/constraint/copy-translation.hpp diff --git a/src/game/component/constraint/pivot.hpp b/src/game/component/constraint/pivot.hpp new file mode 100644 index 0000000..e7867d2 --- /dev/null +++ b/src/game/component/constraint/pivot.hpp @@ -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 . + */ + +#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 diff --git a/src/game/component/constraint/spring-rotation.hpp b/src/game/component/constraint/spring-rotation.hpp new file mode 100644 index 0000000..7bd4f14 --- /dev/null +++ b/src/game/component/constraint/spring-rotation.hpp @@ -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 . + */ + +#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 spring; +}; + +} // namespace constraint +} // namespace component +} // namespace game + +#endif // ANTKEEPER_GAME_COMPONENT_CONSTRAINT_SPRING_ROTATION_HPP diff --git a/src/game/component/constraints/spring-to.hpp b/src/game/component/constraint/spring-to.hpp similarity index 100% rename from src/game/component/constraints/spring-to.hpp rename to src/game/component/constraint/spring-to.hpp diff --git a/src/game/component/constraint/spring-translation.hpp b/src/game/component/constraint/spring-translation.hpp new file mode 100644 index 0000000..52327d4 --- /dev/null +++ b/src/game/component/constraint/spring-translation.hpp @@ -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 . + */ + +#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 spring; +}; + +} // namespace constraint +} // namespace component +} // namespace game + +#endif // ANTKEEPER_GAME_COMPONENT_CONSTRAINT_SPRING_TRANSLATION_HPP diff --git a/src/game/component/constraints/three-dof.hpp b/src/game/component/constraint/three-dof.hpp similarity index 97% rename from src/game/component/constraints/three-dof.hpp rename to src/game/component/constraint/three-dof.hpp index c88fa6e..e085bb2 100644 --- a/src/game/component/constraints/three-dof.hpp +++ b/src/game/component/constraint/three-dof.hpp @@ -25,7 +25,7 @@ namespace component { namespace constraint { /** - * Springs to a target entity. + * Builds rotation from 3DoF angles. */ struct three_dof { diff --git a/src/game/component/constraints/track-to.hpp b/src/game/component/constraint/track-to.hpp similarity index 100% rename from src/game/component/constraints/track-to.hpp rename to src/game/component/constraint/track-to.hpp diff --git a/src/game/context.hpp b/src/game/context.hpp index 62c44f3..bf4f9b0 100644 --- a/src/game/context.hpp +++ b/src/game/context.hpp @@ -216,7 +216,6 @@ struct context render::compositor* surface_compositor; render::ground_pass* ground_pass; - // Scene utilities scene::collection* active_scene; @@ -269,11 +268,11 @@ struct context bool mono_audio; bool captions; float captions_size; - + // Entities entity::registry* entity_registry; std::unordered_map entities; - + // Systems game::system::behavior* behavior_system; game::system::camera* camera_system; diff --git a/src/game/controls.cpp b/src/game/controls.cpp index bb127d2..b30a0cc 100644 --- a/src/game/controls.cpp +++ b/src/game/controls.cpp @@ -17,10 +17,13 @@ * along with Antkeeper source code. If not, see . */ -#include "controls.hpp" +#include "game/controls.hpp" #include "resources/resource-manager.hpp" #include "resources/json.hpp" #include "application.hpp" +#include "game/component/transform.hpp" +#include "game/component/constraint/constraint.hpp" +#include "game/component/constraint-stack.hpp" #include 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 diff --git a/src/game/controls.hpp b/src/game/controls.hpp index 6479df9..37f817c 100644 --- a/src/game/controls.hpp +++ b/src/game/controls.hpp @@ -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 enable_orbit_controls(game::context& ctx); -void disable_orbit_controls(game::context& ctx); - } // namespace game #endif // ANTKEEPER_GAME_CONTROLS_HPP diff --git a/src/game/loop.cpp b/src/game/loop.cpp index 0816a5f..77071e7 100644 --- a/src/game/loop.cpp +++ b/src/game/loop.cpp @@ -25,9 +25,9 @@ namespace game { loop::loop(): update_callback([](double, 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(); } @@ -42,10 +42,10 @@ void loop::set_render_callback(std::function 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) @@ -75,14 +75,14 @@ void loop::tick() accumulator += std::min(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 diff --git a/src/game/loop.hpp b/src/game/loop.hpp index 1d41dc8..6f8eccc 100644 --- a/src/game/loop.hpp +++ b/src/game/loop.hpp @@ -50,11 +50,11 @@ public: void set_render_callback(std::function callback); /** - * Sets the update rate. + * Sets the update frequency. * * @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. @@ -67,6 +67,12 @@ public: * Returns the duration of the last frame, in seconds. */ 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. @@ -81,8 +87,8 @@ public: private: std::function update_callback; std::function render_callback; - double update_rate; - double update_timestep; + double update_frequency; + double update_period; double max_frame_duration; double elapsed_time; double accumulator; @@ -91,6 +97,16 @@ private: 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 #endif // ANTKEEPER_GAME_LOOP_HPP diff --git a/src/game/state/boot.cpp b/src/game/state/boot.cpp index 2c23559..3ad0132 100644 --- a/src/game/state/boot.cpp +++ b/src/game/state/boot.cpp @@ -1077,10 +1077,10 @@ void boot::setup_debugging() 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()); + ctx.loop.set_update_frequency((*ctx.config)["update_frequency"].get()); } // Set update callback diff --git a/src/game/state/main-menu.cpp b/src/game/state/main-menu.cpp index e83ba33..a1c9888 100644 --- a/src/game/state/main-menu.cpp +++ b/src/game/state/main-menu.cpp @@ -270,7 +270,7 @@ main_menu::main_menu(game::context& ctx, bool fade_in): ctx.ui_clear_pass->set_cleared_buffers(false, true, false); // Create mating swarm - game::ant::create_swarm(ctx); + swarm_eid = game::ant::create_swarm(ctx); if (!ctx.menu_bg_billboard->is_active()) game::menu::fade_in_bg(ctx); @@ -297,7 +297,7 @@ main_menu::~main_menu() ctx.ui_scene->remove_object(&title_text); // Destroy swarm - game::ant::destroy_swarm(ctx); + game::ant::destroy_swarm(ctx, swarm_eid); ctx.logger->pop_task(EXIT_SUCCESS); } diff --git a/src/game/state/main-menu.hpp b/src/game/state/main-menu.hpp index 0dbee8e..96ea9c5 100644 --- a/src/game/state/main-menu.hpp +++ b/src/game/state/main-menu.hpp @@ -23,6 +23,7 @@ #include "game/state/base.hpp" #include "scene/text.hpp" #include "animation/animation.hpp" +#include "entity/id.hpp" namespace game { namespace state { @@ -39,6 +40,8 @@ private: scene::text title_text; animation title_fade_animation; + + entity::id swarm_eid; }; } // namespace state diff --git a/src/game/state/nest-selection.cpp b/src/game/state/nest-selection.cpp new file mode 100644 index 0000000..099a787 --- /dev/null +++ b/src/game/state/nest-selection.cpp @@ -0,0 +1,1028 @@ +/* + * 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 . + */ + +#include "game/state/nest-selection.hpp" +#include "game/state/pause-menu.hpp" +#include "game/ant/swarm.hpp" +#include "entity/archetype.hpp" +#include "game/system/camera.hpp" +#include "game/system/astronomy.hpp" +#include "game/system/atmosphere.hpp" +#include "game/component/locomotion.hpp" +#include "game/component/transform.hpp" +#include "game/component/terrain.hpp" +#include "game/component/camera.hpp" +#include "game/component/model.hpp" +#include "game/component/constraint/spring-to.hpp" +#include "game/component/constraint/three-dof.hpp" +#include "game/component/constraint-stack.hpp" +#include "game/component/steering.hpp" +#include "entity/commands.hpp" +#include "animation/screen-transition.hpp" +#include "animation/ease.hpp" +#include "resources/resource-manager.hpp" +#include "game/world.hpp" +#include "application.hpp" +#include "render/passes/clear-pass.hpp" +#include "render/passes/ground-pass.hpp" +#include "state-machine.hpp" +#include "config.hpp" +#include "game/load.hpp" +#include "game/ant/breed.hpp" +#include "game/ant/morphogenesis.hpp" +#include "math/interpolation.hpp" +#include "physics/light/exposure.hpp" +#include "color/color.hpp" +#include + +using namespace game::ant; + +namespace game { +namespace state { + +nest_selection::nest_selection(game::context& ctx): + game::state::base(ctx) +{ + ctx.logger->push_task("Entering nest selection state"); + + // Allocate ant breed + ant::breed breed; + + // Load morphological traits + breed.head = ctx.resource_manager->load("miller-head.dna"); + breed.mandibles = ctx.resource_manager->load("miller-mandibles.dna"); + breed.antennae = ctx.resource_manager->load("slender-antennae.dna"); + breed.eyes = ctx.resource_manager->load("oval-eyes.dna"); + breed.mesosoma = ctx.resource_manager->load("humpback-mesosoma.dna"); + breed.legs = ctx.resource_manager->load("trekking-legs.dna"); + breed.waist = ctx.resource_manager->load("harvester-waist.dna"); + breed.gaster = ctx.resource_manager->load("ovoid-gaster.dna"); + breed.ocelli = ctx.resource_manager->load("trinocular-fisheye-ocelli.dna"); + breed.sting = ctx.resource_manager->load("sting-absent.dna"); + breed.sculpturing = ctx.resource_manager->load("politus-sculpturing.dna"); + breed.pigmentation = ctx.resource_manager->load("rust-pigmentation.dna"); + breed.egg = ctx.resource_manager->load("ellipsoid-egg.dna"); + breed.larva = ctx.resource_manager->load("long-neck-larva.dna"); + breed.cocoon = ctx.resource_manager->load("cocoon-present.dna"); + breed.pilosity = ctx.resource_manager->load("hairless-pilosity.dna"); + breed.forewings = nullptr; + breed.hindwings = nullptr; + + // Load behavioral traits + breed.foraging_time = ctx.resource_manager->load("crepuscular-foraging-time.dna"); + breed.diet = nullptr; + breed.nest = ctx.resource_manager->load("hypogeic-nest.dna"); + + // Build caste models + render::model* worker_model = ant::morphogenesis(breed, ant::caste::worker); + + // Disable UI color clear + ctx.ui_clear_pass->set_cleared_buffers(false, true, false); + + // Create world if not yet created + if (ctx.entities.find("earth") == ctx.entities.end()) + { + // Create cosmos + game::world::cosmogenesis(ctx); + + // Create observer + game::world::create_observer(ctx); + } + + // Load biome + game::load::biome(ctx, "desert-scrub.bio"); + + // Set world time + game::world::set_time(ctx, 2022, 10, 9, 12, 0, 0.0); + + // Set world time scale + game::world::set_time_scale(ctx, 60.0); + + // Setup and enable sky and ground passes + ctx.sky_pass->set_enabled(true); + ctx.ground_pass->set_enabled(true); + + // Create color checker + { + entity::archetype* color_checker_archetype = ctx.resource_manager->load("color-checker.ent"); + auto color_checker_eid = color_checker_archetype->create(*ctx.entity_registry); + entity::command::warp_to(*ctx.entity_registry, color_checker_eid, {0, 0, -10}); + } + + // Create diffuse spheres + { + entity::archetype* diffuse_spheres_archetype = ctx.resource_manager->load("diffuse-spheres.ent"); + auto diffuse_spheres_eid = diffuse_spheres_archetype->create(*ctx.entity_registry); + entity::command::warp_to(*ctx.entity_registry, diffuse_spheres_eid, {0, 0, -20}); + } + + // Create ruler + { + entity::archetype* ruler_10cm_archetype = ctx.resource_manager->load("ruler-10cm.ent"); + auto ruler_10cm_eid = ruler_10cm_archetype->create(*ctx.entity_registry); + entity::command::warp_to(*ctx.entity_registry, ruler_10cm_eid, {0, 0, 10}); + } + + // Create keeper if not yet created + if (ctx.entities.find("keeper") == ctx.entities.end()) + { + entity::id keeper_eid = ctx.entity_registry->create(); + ctx.entities["keeper"] = keeper_eid; + } + + // Start as ant-keeper + is_keeper = true; + + // Setup camera + setup_camera(); + + // Queue control setup + ctx.function_queue.push(std::bind(&nest_selection::enable_keeper_controls, this)); + + ctx.logger->pop_task(EXIT_SUCCESS); +} + +nest_selection::~nest_selection() +{ + ctx.logger->push_task("Exiting nest selection state"); + + ctx.logger->pop_task(EXIT_SUCCESS); +} + +void nest_selection::setup_camera() +{ + // Switch to surface camera + ctx.underground_camera->set_active(false); + ctx.surface_camera->set_active(true); + + // Create surface camera entity + if (!ctx.entities.count("surface_cam")) + { + // Create camera target entity + entity::id target_eid = ctx.entity_registry->create(); + ctx.entities["surface_cam_target"] = target_eid; + { + // Transform + game::component::transform target_transform; + target_transform.local = math::transform::identity; + target_transform.world = target_transform.local; + target_transform.warp = true; + ctx.entity_registry->emplace(target_eid, target_transform); + } + + // Create camera entity + entity::id camera_eid = ctx.entity_registry->create(); + ctx.entities["surface_cam"] = camera_eid; + + // Create camera transform component + game::component::transform transform; + transform.local = math::transform::identity; + transform.world = transform.local; + transform.warp = true; + ctx.entity_registry->emplace(camera_eid, transform); + + // Create camera camera component + game::component::camera camera; + camera.object = ctx.surface_camera; + ctx.entity_registry->emplace(camera_eid, camera); + + // Create camera 3DOF constraint entity + entity::id three_dof_constraint_eid = ctx.entity_registry->create(); + ctx.entities["surface_cam_3dof"] = three_dof_constraint_eid; + { + // Create 3DOF to constraint + game::component::constraint::three_dof three_dof; + three_dof.yaw = 0.0f; + three_dof.pitch = 0.0f; + three_dof.roll = 0.0f; + ctx.entity_registry->emplace(three_dof_constraint_eid, three_dof); + + // Create constraint stack node component + game::component::constraint_stack_node node; + node.active = true; + node.weight = 1.0f; + node.next = entt::null; + ctx.entity_registry->emplace(three_dof_constraint_eid, node); + } + + // Create camera spring to constraint entity + entity::id spring_constraint_eid = ctx.entity_registry->create(); + { + // Create spring to constraint + game::component::constraint::spring_to spring; + spring.target = target_eid; + spring.translation = {{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}, 1.0f, math::two_pi}; + spring.translation.w = hz_to_rads(8.0f); + + spring.spring_translation = true; + spring.spring_rotation = false; + ctx.entity_registry->emplace(spring_constraint_eid, spring); + + // Create constraint stack node component + game::component::constraint_stack_node node; + node.active = true; + node.weight = 1.0f; + node.next = three_dof_constraint_eid; + ctx.entity_registry->emplace(spring_constraint_eid, node); + } + + // Create camera constraint stack component + game::component::constraint_stack constraint_stack; + constraint_stack.head = spring_constraint_eid; + ctx.entity_registry->emplace(camera_eid, constraint_stack); + } + + game::ant::create_swarm(ctx); + + const float ev100_sunny16 = physics::light::ev::from_settings(16.0f, 1.0f / 100.0f, 100.0f); + const float ev100_looney11 = physics::light::ev::from_settings(11.0f, 1.0f / 100.0f, 100.0f); + ctx.surface_camera->set_exposure(ev100_sunny16); +} + +void nest_selection::enable_keeper_controls() +{ + // Get camera entities + entity::id camera_eid = ctx.entities["surface_cam"]; + entity::id target_eid = ctx.entities["surface_cam_target"]; + entity::id three_dof_eid = ctx.entities["surface_cam_3dof"]; + + const float min_elevation = 0.1f; + const float max_elevation = 100.0f; + const float slow_modifier = 0.25f; + const float fast_modifier = 4.0f; + const float dolly_speed = 5.0f; + const float truck_speed = dolly_speed; + const float pedestal_speed = 5.0f; + float mouse_tilt_sensitivity = 1.0f; + float mouse_pan_sensitivity = 1.0f; + bool mouse_invert_tilt = false; + bool mouse_invert_pan = false; + float gamepad_tilt_sensitivity = 1.0f; + float gamepad_pan_sensitivity = 1.0f; + bool gamepad_invert_tilt = false; + bool gamepad_invert_pan = false; + bool mouse_look_toggle = false; + ctx.mouse_look = false; + const double time_scale = 60.0; + const double ff_time_scale = time_scale * 200; + + if (ctx.config->contains("mouse_tilt_sensitivity")) + mouse_tilt_sensitivity = math::radians((*ctx.config)["mouse_tilt_sensitivity"].get()); + if (ctx.config->contains("mouse_pan_sensitivity")) + mouse_pan_sensitivity = math::radians((*ctx.config)["mouse_pan_sensitivity"].get()); + if (ctx.config->contains("mouse_invert_tilt")) + mouse_invert_tilt = (*ctx.config)["mouse_invert_tilt"].get(); + if (ctx.config->contains("mouse_invert_pan")) + mouse_invert_pan = (*ctx.config)["mouse_invert_pan"].get(); + if (ctx.config->contains("mouse_look_toggle")) + mouse_look_toggle = (*ctx.config)["mouse_look_toggle"].get(); + + if (ctx.config->contains("gamepad_tilt_sensitivity")) + gamepad_tilt_sensitivity = math::radians((*ctx.config)["gamepad_tilt_sensitivity"].get()); + if (ctx.config->contains("gamepad_pan_sensitivity")) + gamepad_pan_sensitivity = math::radians((*ctx.config)["gamepad_pan_sensitivity"].get()); + if (ctx.config->contains("gamepad_invert_tilt")) + gamepad_invert_tilt = (*ctx.config)["gamepad_invert_tilt"].get(); + if (ctx.config->contains("gamepad_invert_pan")) + gamepad_invert_pan = (*ctx.config)["gamepad_invert_pan"].get(); + + const input::control* move_slow = ctx.controls["move_slow"]; + const input::control* move_fast = ctx.controls["move_fast"]; + const input::control* mouse_look = ctx.controls["mouse_look"]; + + float mouse_tilt_factor = mouse_tilt_sensitivity * (mouse_invert_tilt ? -1.0f : 1.0f); + float mouse_pan_factor = mouse_pan_sensitivity * (mouse_invert_pan ? -1.0f : 1.0f); + float gamepad_tilt_factor = gamepad_tilt_sensitivity * (gamepad_invert_tilt ? -1.0f : 1.0f); + float gamepad_pan_factor = gamepad_pan_sensitivity * (gamepad_invert_pan ? -1.0f : 1.0f); + + ctx.controls["move_forward"]->set_active_callback + ( + [&ctx = this->ctx, target_eid, three_dof_eid, dolly_speed, move_slow, move_fast, slow_modifier, fast_modifier](float value) + { + if (move_slow->is_active()) + value *= slow_modifier; + if (move_fast->is_active()) + value *= fast_modifier; + + auto& three_dof = ctx.entity_registry->get(three_dof_eid); + const math::quaternion yaw = math::angle_axis(three_dof.yaw, {0.0f, 1.0f, 0.0f}); + + const float3 movement = {0.0f, 0.0f, -dolly_speed * value * static_cast(ctx.loop.get_update_period())}; + entity::command::translate(*ctx.entity_registry, target_eid, yaw * movement); + } + ); + + // Dolly backward + ctx.controls["move_back"]->set_active_callback + ( + [&ctx = this->ctx, target_eid, three_dof_eid, dolly_speed, move_slow, move_fast, slow_modifier, fast_modifier](float value) + { + if (move_slow->is_active()) + value *= slow_modifier; + if (move_fast->is_active()) + value *= fast_modifier; + + auto& three_dof = ctx.entity_registry->get(three_dof_eid); + const math::quaternion yaw = math::angle_axis(three_dof.yaw, {0.0f, 1.0f, 0.0f}); + + const float3 movement = {0.0f, 0.0f, dolly_speed * value * static_cast(ctx.loop.get_update_period())}; + entity::command::translate(*ctx.entity_registry, target_eid, yaw * movement); + } + ); + + // Truck right + ctx.controls["move_right"]->set_active_callback + ( + [&ctx = this->ctx, target_eid, three_dof_eid, truck_speed, move_slow, move_fast, slow_modifier, fast_modifier](float value) + { + if (move_slow->is_active()) + value *= slow_modifier; + if (move_fast->is_active()) + value *= fast_modifier; + + auto& three_dof = ctx.entity_registry->get(three_dof_eid); + const math::quaternion yaw = math::angle_axis(three_dof.yaw, {0.0f, 1.0f, 0.0f}); + + const float3 movement = {truck_speed * value * static_cast(ctx.loop.get_update_period()), 0.0f, 0.0f}; + entity::command::translate(*ctx.entity_registry, target_eid, yaw * movement); + } + ); + + // Truck left + ctx.controls["move_left"]->set_active_callback + ( + [&ctx = this->ctx, target_eid, three_dof_eid, truck_speed, move_slow, move_fast, slow_modifier, fast_modifier](float value) + { + if (move_slow->is_active()) + value *= slow_modifier; + if (move_fast->is_active()) + value *= fast_modifier; + + auto& three_dof = ctx.entity_registry->get(three_dof_eid); + const math::quaternion yaw = math::angle_axis(three_dof.yaw, {0.0f, 1.0f, 0.0f}); + + const float3 movement = {-truck_speed * value * static_cast(ctx.loop.get_update_period()), 0.0f, 0.0f}; + entity::command::translate(*ctx.entity_registry, target_eid, yaw * movement); + } + ); + + // Pedestal up + ctx.controls["move_up"]->set_active_callback + ( + [&ctx = this->ctx, target_eid, pedestal_speed, move_slow, move_fast, slow_modifier, fast_modifier, max_elevation](float value) + { + if (move_slow->is_active()) + value *= slow_modifier; + if (move_fast->is_active()) + value *= fast_modifier; + + float3 movement = {0.0f, pedestal_speed * value * static_cast(ctx.loop.get_update_period()), 0.0f}; + auto transform = entity::command::get_world_transform(*ctx.entity_registry, target_eid); + if (transform.translation.y + movement.y > max_elevation) + movement.y = max_elevation - transform.translation.y; + entity::command::translate(*ctx.entity_registry, target_eid, movement); + } + ); + + // Pedestal down + ctx.controls["move_down"]->set_active_callback + ( + [&ctx = this->ctx, target_eid, pedestal_speed, move_slow, move_fast, slow_modifier, fast_modifier, min_elevation](float value) + { + if (move_slow->is_active()) + value *= slow_modifier; + if (move_fast->is_active()) + value *= fast_modifier; + + float3 movement = {0.0f, -pedestal_speed * value * static_cast(ctx.loop.get_update_period()), 0.0f}; + auto transform = entity::command::get_world_transform(*ctx.entity_registry, target_eid); + if (transform.translation.y + movement.y < min_elevation) + movement.y = min_elevation - transform.translation.y; + + entity::command::translate(*ctx.entity_registry, target_eid, movement); + } + ); + + // Mouse rotate + ctx.controls["mouse_look"]->set_activated_callback + ( + [&ctx = this->ctx, mouse_look_toggle]() + { + if (mouse_look_toggle) + ctx.mouse_look = !ctx.mouse_look; + else + ctx.mouse_look = true; + + ctx.app->set_relative_mouse_mode(ctx.mouse_look); + } + ); + ctx.controls["mouse_look"]->set_deactivated_callback + ( + [&ctx = this->ctx, mouse_look_toggle]() + { + if (!mouse_look_toggle) + { + ctx.mouse_look = false; + ctx.app->set_relative_mouse_mode(false); + } + } + ); + // Pan left + ctx.controls["look_left_gamepad"]->set_active_callback + ( + [&ctx = this->ctx, three_dof_eid, gamepad_pan_factor](float value) + { + auto& three_dof = ctx.entity_registry->get(three_dof_eid); + three_dof.yaw += gamepad_pan_factor * value * static_cast(ctx.loop.get_update_period()); + } + ); + ctx.controls["look_left_mouse"]->set_active_callback + ( + [&ctx = this->ctx, three_dof_eid, mouse_pan_factor](float value) + { + if (!ctx.mouse_look) + return; + + auto& three_dof = ctx.entity_registry->get(three_dof_eid); + three_dof.yaw += mouse_pan_factor * value * static_cast(ctx.loop.get_update_period()); + } + ); + + // Pan right + ctx.controls["look_right_gamepad"]->set_active_callback + ( + [&ctx = this->ctx, three_dof_eid, gamepad_pan_factor](float value) + { + auto& three_dof = ctx.entity_registry->get(three_dof_eid); + three_dof.yaw -= gamepad_pan_factor * value * static_cast(ctx.loop.get_update_period()); + } + ); + ctx.controls["look_right_mouse"]->set_active_callback + ( + [&ctx = this->ctx, three_dof_eid, mouse_pan_factor](float value) + { + if (!ctx.mouse_look) + return; + + auto& three_dof = ctx.entity_registry->get(three_dof_eid); + three_dof.yaw -= mouse_pan_factor * value * static_cast(ctx.loop.get_update_period()); + } + ); + // Tilt up + ctx.controls["look_up_gamepad"]->set_active_callback + ( + [&ctx = this->ctx, three_dof_eid, gamepad_tilt_factor](float value) + { + auto& three_dof = ctx.entity_registry->get(three_dof_eid); + three_dof.pitch -= gamepad_tilt_factor * value * static_cast(ctx.loop.get_update_period()); + three_dof.pitch = std::max(math::radians(-90.0f), three_dof.pitch); + } + ); + ctx.controls["look_up_mouse"]->set_active_callback + ( + [&ctx = this->ctx, three_dof_eid, mouse_tilt_factor](float value) + { + if (!ctx.mouse_look) + return; + + auto& three_dof = ctx.entity_registry->get(three_dof_eid); + three_dof.pitch -= mouse_tilt_factor * value * static_cast(ctx.loop.get_update_period()); + three_dof.pitch = std::max(math::radians(-90.0f), three_dof.pitch); + } + ); + // Tilt down + ctx.controls["look_down_gamepad"]->set_active_callback + ( + [&ctx = this->ctx, three_dof_eid, gamepad_tilt_factor](float value) + { + auto& three_dof = ctx.entity_registry->get(three_dof_eid); + three_dof.pitch += gamepad_tilt_factor * value * static_cast(ctx.loop.get_update_period()); + three_dof.pitch = std::min(math::radians(90.0f), three_dof.pitch); + } + ); + ctx.controls["look_down_mouse"]->set_active_callback + ( + [&ctx = this->ctx, three_dof_eid, mouse_tilt_factor](float value) + { + if (!ctx.mouse_look) + return; + + auto& three_dof = ctx.entity_registry->get(three_dof_eid); + three_dof.pitch += mouse_tilt_factor * value * static_cast(ctx.loop.get_update_period()); + three_dof.pitch = std::min(math::radians(90.0f), three_dof.pitch); + } + ); + + // Setup switch POV control + ctx.controls["switch_pov"]->set_activated_callback + ( + [this]() + { + // Disable keeper controls + this->disable_keeper_controls(); + + // Switch to ant + this->is_keeper = false; + + // Enable ant controls + this->enable_ant_controls(); + } + ); + + // Fast-forward + ctx.controls["fast_forward"]->set_activated_callback + ( + [&ctx = this->ctx, ff_time_scale]() + { + game::world::set_time_scale(ctx, ff_time_scale); + } + ); + ctx.controls["fast_forward"]->set_deactivated_callback + ( + [&ctx = this->ctx, time_scale]() + { + game::world::set_time_scale(ctx, time_scale); + } + ); + ctx.controls["rewind"]->set_activated_callback + ( + [&ctx = this->ctx, ff_time_scale]() + { + game::world::set_time_scale(ctx, -ff_time_scale); + } + ); + ctx.controls["rewind"]->set_deactivated_callback + ( + [&ctx = this->ctx, time_scale]() + { + game::world::set_time_scale(ctx, time_scale); + } + ); + + // Setup pause control + ctx.controls["pause"]->set_activated_callback + ( + [this, &ctx = this->ctx]() + { + // Disable controls + this->disable_controls(); + + // Set resume callback + ctx.resume_callback = [this, &ctx]() + { + this->enable_controls(); + ctx.resume_callback = nullptr; + }; + + // Push pause menu state + ctx.state_machine.emplace(new game::state::pause_menu(ctx)); + } + ); + + ctx.controls["increase_exposure"]->set_active_callback + ( + [&ctx = this->ctx](float) + { + //ctx.astronomy_system->set_exposure_offset(ctx.astronomy_system->get_exposure_offset() - 1.0f); + ctx.surface_camera->set_exposure(ctx.surface_camera->get_exposure() + 3.0f * static_cast(ctx.loop.get_update_period())); + ctx.logger->log("EV100: " + std::to_string(ctx.surface_camera->get_exposure())); + } + ); + + ctx.controls["decrease_exposure"]->set_active_callback + ( + [&ctx = this->ctx](float) + { + //ctx.astronomy_system->set_exposure_offset(ctx.astronomy_system->get_exposure_offset() + 1.0f); + ctx.surface_camera->set_exposure(ctx.surface_camera->get_exposure() - 3.0f * static_cast(ctx.loop.get_update_period())); + ctx.logger->log("EV100: " + std::to_string(ctx.surface_camera->get_exposure())); + } + ); + + const float wavelength_speed = 20.0; + ctx.controls["dec_red"]->set_active_callback + ( + [&ctx = this->ctx, wavelength_speed](float) + { + ctx.rgb_wavelengths.x -= wavelength_speed * ctx.loop.get_update_period(); + ctx.atmosphere_system->set_rgb_wavelengths(ctx.rgb_wavelengths * 1e-9); + std::stringstream stream; + stream << ctx.rgb_wavelengths; + ctx.logger->log("wavelengths: " + stream.str()); + } + ); + ctx.controls["inc_red"]->set_active_callback + ( + [&ctx = this->ctx, wavelength_speed](float) + { + ctx.rgb_wavelengths.x += wavelength_speed* ctx.loop.get_update_period(); + ctx.atmosphere_system->set_rgb_wavelengths(ctx.rgb_wavelengths * 1e-9); + std::stringstream stream; + stream << ctx.rgb_wavelengths; + ctx.logger->log("wavelengths: " + stream.str()); + } + ); + + ctx.controls["dec_green"]->set_active_callback + ( + [&ctx = this->ctx, wavelength_speed](float) + { + ctx.rgb_wavelengths.y -= wavelength_speed; + ctx.atmosphere_system->set_rgb_wavelengths(ctx.rgb_wavelengths * 1e-9); + std::stringstream stream; + stream << ctx.rgb_wavelengths; + ctx.logger->log("wavelengths: " + stream.str()); + } + ); + ctx.controls["inc_green"]->set_active_callback + ( + [&ctx = this->ctx, wavelength_speed](float) + { + ctx.rgb_wavelengths.y += wavelength_speed; + ctx.atmosphere_system->set_rgb_wavelengths(ctx.rgb_wavelengths * 1e-9); + std::stringstream stream; + stream << ctx.rgb_wavelengths; + ctx.logger->log("wavelengths: " + stream.str()); + } + ); + + ctx.controls["dec_blue"]->set_active_callback + ( + [&ctx = this->ctx, wavelength_speed](float) + { + ctx.rgb_wavelengths.z -= wavelength_speed; + ctx.atmosphere_system->set_rgb_wavelengths(ctx.rgb_wavelengths * 1e-9); + std::stringstream stream; + stream << ctx.rgb_wavelengths; + ctx.logger->log("wavelengths: " + stream.str()); + } + ); + ctx.controls["inc_blue"]->set_active_callback + ( + [&ctx = this->ctx, wavelength_speed](float) + { + ctx.rgb_wavelengths.z += wavelength_speed; + ctx.atmosphere_system->set_rgb_wavelengths(ctx.rgb_wavelengths * 1e-9); + std::stringstream stream; + stream << ctx.rgb_wavelengths; + ctx.logger->log("wavelengths: " + stream.str()); + } + ); +} + +void nest_selection::disable_keeper_controls() +{ + ctx.controls["move_forward"]->set_active_callback(nullptr); + ctx.controls["move_back"]->set_active_callback(nullptr); + ctx.controls["move_right"]->set_active_callback(nullptr); + ctx.controls["move_left"]->set_active_callback(nullptr); + ctx.controls["move_up"]->set_active_callback(nullptr); + ctx.controls["move_down"]->set_active_callback(nullptr); + ctx.controls["mouse_look"]->set_activated_callback(nullptr); + ctx.controls["mouse_look"]->set_deactivated_callback(nullptr); + ctx.controls["look_left_gamepad"]->set_active_callback(nullptr); + ctx.controls["look_left_mouse"]->set_active_callback(nullptr); + ctx.controls["look_right_gamepad"]->set_active_callback(nullptr); + ctx.controls["look_right_mouse"]->set_active_callback(nullptr); + ctx.controls["look_up_gamepad"]->set_active_callback(nullptr); + ctx.controls["look_up_mouse"]->set_active_callback(nullptr); + ctx.controls["look_down_gamepad"]->set_active_callback(nullptr); + ctx.controls["look_down_mouse"]->set_active_callback(nullptr); + ctx.controls["switch_pov"]->set_activated_callback(nullptr); + ctx.controls["fast_forward"]->set_activated_callback(nullptr); + ctx.controls["rewind"]->set_activated_callback(nullptr); + ctx.controls["pause"]->set_activated_callback(nullptr); + ctx.controls["increase_exposure"]->set_activated_callback(nullptr); + ctx.controls["decrease_exposure"]->set_activated_callback(nullptr); +} + +void nest_selection::enable_ant_controls() +{ + // Get ant controller entities + entity::id ant_eid = ctx.entities["ant"]; + + const float move_forward_speed = 5.0f; + const float move_back_speed = move_forward_speed * 0.5f; + const float strafe_speed = move_forward_speed * 0.5f; + const float turn_speed = math::radians(270.0f); + const float slow_modifier = 0.5f; + const float fast_modifier = 2.0f; + float mouse_tilt_sensitivity = 1.0f; + float mouse_pan_sensitivity = 1.0f; + bool mouse_invert_tilt = false; + bool mouse_invert_pan = false; + float gamepad_tilt_sensitivity = 1.0f; + float gamepad_pan_sensitivity = 1.0f; + bool gamepad_invert_tilt = false; + bool gamepad_invert_pan = false; + const double time_scale = 60.0; + const double ff_time_scale = time_scale * 200; + + if (ctx.config->contains("mouse_tilt_sensitivity")) + mouse_tilt_sensitivity = math::radians((*ctx.config)["mouse_tilt_sensitivity"].get()); + if (ctx.config->contains("mouse_pan_sensitivity")) + mouse_pan_sensitivity = math::radians((*ctx.config)["mouse_pan_sensitivity"].get()); + if (ctx.config->contains("mouse_invert_tilt")) + mouse_invert_tilt = (*ctx.config)["mouse_invert_tilt"].get(); + if (ctx.config->contains("mouse_invert_pan")) + mouse_invert_pan = (*ctx.config)["mouse_invert_pan"].get(); + + if (ctx.config->contains("gamepad_tilt_sensitivity")) + gamepad_tilt_sensitivity = math::radians((*ctx.config)["gamepad_tilt_sensitivity"].get()); + if (ctx.config->contains("gamepad_pan_sensitivity")) + gamepad_pan_sensitivity = math::radians((*ctx.config)["gamepad_pan_sensitivity"].get()); + if (ctx.config->contains("gamepad_invert_tilt")) + gamepad_invert_tilt = (*ctx.config)["gamepad_invert_tilt"].get(); + if (ctx.config->contains("gamepad_invert_pan")) + gamepad_invert_pan = (*ctx.config)["gamepad_invert_pan"].get(); + + const input::control* move_slow = ctx.controls["move_slow"]; + const input::control* move_fast = ctx.controls["move_fast"]; + const input::control* mouse_look = ctx.controls["mouse_look"]; + + float mouse_tilt_factor = mouse_tilt_sensitivity * (mouse_invert_tilt ? -1.0f : 1.0f); + float mouse_pan_factor = mouse_pan_sensitivity * (mouse_invert_pan ? -1.0f : 1.0f); + float gamepad_tilt_factor = gamepad_tilt_sensitivity * (gamepad_invert_tilt ? -1.0f : 1.0f); + float gamepad_pan_factor = gamepad_pan_sensitivity * (gamepad_invert_pan ? -1.0f : 1.0f); + + // Move forward + ctx.controls["move_forward"]->set_active_callback + ( + [&ctx = this->ctx, ant_eid, move_forward_speed, move_slow, move_fast, slow_modifier, fast_modifier](float value) + { + if (move_slow->is_active()) + value *= slow_modifier; + if (move_fast->is_active()) + value *= fast_modifier; + + auto& locomotion = ctx.entity_registry->get(ant_eid); + const math::quaternion yaw = math::angle_axis(locomotion.yaw, {0.0f, 1.0f, 0.0f}); + + const float3 movement = {0.0f, 0.0f, move_forward_speed * value * static_cast(ctx.loop.get_update_period())}; + entity::command::translate(*ctx.entity_registry, ant_eid, yaw * movement); + } + ); + + // Move back + ctx.controls["move_back"]->set_active_callback + ( + [&ctx = this->ctx, ant_eid, move_back_speed, move_slow, move_fast, slow_modifier, fast_modifier](float value) + { + if (move_slow->is_active()) + value *= slow_modifier; + if (move_fast->is_active()) + value *= fast_modifier; + + auto& locomotion = ctx.entity_registry->get(ant_eid); + const math::quaternion yaw = math::angle_axis(locomotion.yaw, {0.0f, 1.0f, 0.0f}); + + const float3 movement = {0.0f, 0.0f, -move_back_speed * value * static_cast(ctx.loop.get_update_period())}; + entity::command::translate(*ctx.entity_registry, ant_eid, yaw * movement); + } + ); + + // Turn right + ctx.controls["move_right"]->set_active_callback + ( + [&ctx = this->ctx, ant_eid, turn_speed, move_slow, move_fast, slow_modifier, fast_modifier](float value) + { + if (move_slow->is_active()) + value *= slow_modifier; + if (move_fast->is_active()) + value *= fast_modifier; + + auto& locomotion = ctx.entity_registry->get(ant_eid); + float delta_yaw = -turn_speed * value * static_cast(ctx.loop.get_update_period()); + locomotion.yaw += delta_yaw; + + entity::command::rotate(*ctx.entity_registry, ant_eid, delta_yaw, {0.0f, 1.0f, 0.0f}); + } + ); + + // Truck left + ctx.controls["move_left"]->set_active_callback + ( + [&ctx = this->ctx, ant_eid, turn_speed, move_slow, move_fast, slow_modifier, fast_modifier](float value) + { + if (move_slow->is_active()) + value *= slow_modifier; + if (move_fast->is_active()) + value *= fast_modifier; + + auto& locomotion = ctx.entity_registry->get(ant_eid); + float delta_yaw = turn_speed * value * static_cast(ctx.loop.get_update_period()); + locomotion.yaw += delta_yaw; + + entity::command::rotate(*ctx.entity_registry, ant_eid, delta_yaw, {0.0f, 1.0f, 0.0f}); + } + ); + + // Pan left + /* + ctx.controls["look_left_gamepad"]->set_active_callback + ( + [&ctx = this->ctx, three_dof_eid, gamepad_pan_factor](float value) + { + auto& three_dof = ctx.entity_registry->get(three_dof_eid); + three_dof.yaw += gamepad_pan_factor * value * static_cast(ctx.loop.get_update_period()); + } + ); + ctx.controls["look_left_mouse"]->set_active_callback + ( + [&ctx = this->ctx, three_dof_eid, mouse_pan_factor](float value) + { + if (!ctx.mouse_look) + return; + + auto& three_dof = ctx.entity_registry->get(three_dof_eid); + three_dof.yaw += mouse_pan_factor * value * static_cast(ctx.loop.get_update_period()); + } + ); + + // Pan right + ctx.controls["look_right_gamepad"]->set_active_callback + ( + [&ctx = this->ctx, three_dof_eid, gamepad_pan_factor](float value) + { + auto& three_dof = ctx.entity_registry->get(three_dof_eid); + three_dof.yaw -= gamepad_pan_factor * value * static_cast(ctx.loop.get_update_period()); + } + ); + ctx.controls["look_right_mouse"]->set_active_callback + ( + [&ctx = this->ctx, three_dof_eid, mouse_pan_factor](float value) + { + if (!ctx.mouse_look) + return; + + auto& three_dof = ctx.entity_registry->get(three_dof_eid); + three_dof.yaw -= mouse_pan_factor * value * static_cast(ctx.loop.get_update_period()); + } + ); + // Tilt up + ctx.controls["look_up_gamepad"]->set_active_callback + ( + [&ctx = this->ctx, three_dof_eid, gamepad_tilt_factor](float value) + { + auto& three_dof = ctx.entity_registry->get(three_dof_eid); + three_dof.pitch -= gamepad_tilt_factor * value * static_cast(ctx.loop.get_update_period()); + three_dof.pitch = std::max(math::radians(-90.0f), three_dof.pitch); + } + ); + ctx.controls["look_up_mouse"]->set_active_callback + ( + [&ctx = this->ctx, three_dof_eid, mouse_tilt_factor](float value) + { + if (!ctx.mouse_look) + return; + + auto& three_dof = ctx.entity_registry->get(three_dof_eid); + three_dof.pitch -= mouse_tilt_factor * value * static_cast(ctx.loop.get_update_period()); + three_dof.pitch = std::max(math::radians(-90.0f), three_dof.pitch); + } + ); + // Tilt down + ctx.controls["look_down_gamepad"]->set_active_callback + ( + [&ctx = this->ctx, three_dof_eid, gamepad_tilt_factor](float value) + { + auto& three_dof = ctx.entity_registry->get(three_dof_eid); + three_dof.pitch += gamepad_tilt_factor * value * static_cast(ctx.loop.get_update_period()); + three_dof.pitch = std::min(math::radians(90.0f), three_dof.pitch); + } + ); + ctx.controls["look_down_mouse"]->set_active_callback + ( + [&ctx = this->ctx, three_dof_eid, mouse_tilt_factor](float value) + { + if (!ctx.mouse_look) + return; + + auto& three_dof = ctx.entity_registry->get(three_dof_eid); + three_dof.pitch += mouse_tilt_factor * value * static_cast(ctx.loop.get_update_period()); + three_dof.pitch = std::min(math::radians(90.0f), three_dof.pitch); + } + ); + */ + + // Setup switch POV control + ctx.controls["switch_pov"]->set_activated_callback + ( + [this]() + { + // Disable ant controls + this->disable_ant_controls(); + + // Switch to keeper + this->is_keeper = true; + + // Enable keeper controls + this->enable_keeper_controls(); + } + ); + + // Fast-forward + ctx.controls["fast_forward"]->set_activated_callback + ( + [&ctx = this->ctx, ff_time_scale]() + { + game::world::set_time_scale(ctx, ff_time_scale); + } + ); + ctx.controls["fast_forward"]->set_deactivated_callback + ( + [&ctx = this->ctx, time_scale]() + { + game::world::set_time_scale(ctx, time_scale); + } + ); + ctx.controls["rewind"]->set_activated_callback + ( + [&ctx = this->ctx, ff_time_scale]() + { + game::world::set_time_scale(ctx, -ff_time_scale); + } + ); + ctx.controls["rewind"]->set_deactivated_callback + ( + [&ctx = this->ctx, time_scale]() + { + game::world::set_time_scale(ctx, time_scale); + } + ); + + // Setup pause control + ctx.controls["pause"]->set_activated_callback + ( + [this, &ctx = this->ctx]() + { + // Disable controls + this->disable_controls(); + + // Set resume callback + ctx.resume_callback = [this, &ctx]() + { + this->enable_controls(); + ctx.resume_callback = nullptr; + }; + + // Push pause menu state + ctx.state_machine.emplace(new game::state::pause_menu(ctx)); + } + ); +} + +void nest_selection::disable_ant_controls() +{ + ctx.controls["move_forward"]->set_active_callback(nullptr); + ctx.controls["move_back"]->set_active_callback(nullptr); + ctx.controls["move_right"]->set_active_callback(nullptr); + ctx.controls["move_left"]->set_active_callback(nullptr); + ctx.controls["look_left_gamepad"]->set_active_callback(nullptr); + ctx.controls["look_left_mouse"]->set_active_callback(nullptr); + ctx.controls["look_right_gamepad"]->set_active_callback(nullptr); + ctx.controls["look_right_mouse"]->set_active_callback(nullptr); + ctx.controls["look_up_gamepad"]->set_active_callback(nullptr); + ctx.controls["look_up_mouse"]->set_active_callback(nullptr); + ctx.controls["look_down_gamepad"]->set_active_callback(nullptr); + ctx.controls["look_down_mouse"]->set_active_callback(nullptr); + ctx.controls["switch_pov"]->set_activated_callback(nullptr); + ctx.controls["fast_forward"]->set_activated_callback(nullptr); + ctx.controls["rewind"]->set_activated_callback(nullptr); + ctx.controls["pause"]->set_activated_callback(nullptr); +} + +void nest_selection::enable_controls() +{ + if (is_keeper) + enable_keeper_controls(); + else + enable_ant_controls(); +} + +void nest_selection::disable_controls() +{ + if (is_keeper) + disable_keeper_controls(); + else + disable_ant_controls(); +} + +} // namespace state +} // namespace game diff --git a/src/game/state/nest-selection.hpp b/src/game/state/nest-selection.hpp new file mode 100644 index 0000000..1e52186 --- /dev/null +++ b/src/game/state/nest-selection.hpp @@ -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 . + */ + +#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 diff --git a/src/game/state/nuptial-flight.cpp b/src/game/state/nuptial-flight.cpp index dd7785a..b8eb312 100644 --- a/src/game/state/nuptial-flight.cpp +++ b/src/game/state/nuptial-flight.cpp @@ -29,10 +29,10 @@ #include "game/component/terrain.hpp" #include "game/component/camera.hpp" #include "game/component/model.hpp" -#include "game/component/constraints/spring-to.hpp" -#include "game/component/constraints/three-dof.hpp" +#include "game/component/constraint/constraint.hpp" #include "game/component/constraint-stack.hpp" #include "game/component/steering.hpp" +#include "game/controls.hpp" #include "entity/commands.hpp" #include "animation/screen-transition.hpp" #include "animation/ease.hpp" @@ -61,37 +61,6 @@ nuptial_flight::nuptial_flight(game::context& ctx): { ctx.logger->push_task("Entering nuptial flight state"); - // Allocate ant breed - ant::breed breed; - - // Load morphological traits - breed.head = ctx.resource_manager->load("miller-head.dna"); - breed.mandibles = ctx.resource_manager->load("miller-mandibles.dna"); - breed.antennae = ctx.resource_manager->load("slender-antennae.dna"); - breed.eyes = ctx.resource_manager->load("oval-eyes.dna"); - breed.mesosoma = ctx.resource_manager->load("humpback-mesosoma.dna"); - breed.legs = ctx.resource_manager->load("trekking-legs.dna"); - breed.waist = ctx.resource_manager->load("harvester-waist.dna"); - breed.gaster = ctx.resource_manager->load("ovoid-gaster.dna"); - breed.ocelli = ctx.resource_manager->load("trinocular-fisheye-ocelli.dna"); - breed.sting = ctx.resource_manager->load("sting-absent.dna"); - breed.sculpturing = ctx.resource_manager->load("politus-sculpturing.dna"); - breed.pigmentation = ctx.resource_manager->load("rust-pigmentation.dna"); - breed.egg = ctx.resource_manager->load("ellipsoid-egg.dna"); - breed.larva = ctx.resource_manager->load("long-neck-larva.dna"); - breed.cocoon = ctx.resource_manager->load("cocoon-present.dna"); - breed.pilosity = ctx.resource_manager->load("hairless-pilosity.dna"); - breed.forewings = nullptr; - breed.hindwings = nullptr; - - // Load behavioral traits - breed.foraging_time = ctx.resource_manager->load("crepuscular-foraging-time.dna"); - breed.diet = nullptr; - breed.nest = ctx.resource_manager->load("hypogeic-nest.dna"); - - // Build caste models - render::model* worker_model = ant::morphogenesis(breed, ant::caste::worker); - // Disable UI color clear ctx.ui_clear_pass->set_cleared_buffers(false, true, false); @@ -109,55 +78,59 @@ nuptial_flight::nuptial_flight(game::context& ctx): game::load::biome(ctx, "desert-scrub.bio"); // Set world time - game::world::set_time(ctx, 2022, 10, 9, 12, 0, 0.0); + game::world::set_time(ctx, 2022, 6, 21, 12, 0, 0.0); // Set world time scale - game::world::set_time_scale(ctx, 60.0); + game::world::set_time_scale(ctx, 0.0); // Setup and enable sky and ground passes ctx.sky_pass->set_enabled(true); ctx.ground_pass->set_enabled(true); - // Create color checker - { - entity::archetype* color_checker_archetype = ctx.resource_manager->load("color-checker.ent"); - auto color_checker_eid = color_checker_archetype->create(*ctx.entity_registry); - entity::command::warp_to(*ctx.entity_registry, color_checker_eid, {0, 0, -10}); - } + // Create mating swarm + swarm_eid = game::ant::create_swarm(ctx); - // Create diffuse spheres - { - entity::archetype* diffuse_spheres_archetype = ctx.resource_manager->load("diffuse-spheres.ent"); - auto diffuse_spheres_eid = diffuse_spheres_archetype->create(*ctx.entity_registry); - entity::command::warp_to(*ctx.entity_registry, diffuse_spheres_eid, {0, 0, -20}); - } - - // Create ruler - { - entity::archetype* ruler_10cm_archetype = ctx.resource_manager->load("ruler-10cm.ent"); - auto ruler_10cm_eid = ruler_10cm_archetype->create(*ctx.entity_registry); - entity::command::warp_to(*ctx.entity_registry, ruler_10cm_eid, {0, 0, 10}); - } + // Switch to surface camera + ctx.underground_camera->set_active(false); + ctx.surface_camera->set_active(true); - // Create keeper if not yet created - if (ctx.entities.find("keeper") == ctx.entities.end()) - { - entity::id keeper_eid = ctx.entity_registry->create(); - ctx.entities["keeper"] = keeper_eid; - } + // Set camera exposure + const float ev100_sunny16 = physics::light::ev::from_settings(16.0f, 1.0f / 100.0f, 100.0f); + ctx.surface_camera->set_exposure(ev100_sunny16); - // Start as ant-keeper - is_keeper = true; + // Create camera rig + create_camera_rig(); - // Setup camera - setup_camera(); + ctx.entity_registry->view().each + ( + [&](entity::id alate_eid, auto& transform, auto& steering) + { + ctx.entity_registry->patch + ( + camera_rig_copy_translation_eid, + [&](auto& component) + { + component.target = alate_eid; + } + ); + + ctx.entity_registry->patch + ( + camera_rig_pivot_eid, + [&](auto& component) + { + component.target = alate_eid; + } + ); + } + ); // Queue fade in - ctx.fade_transition_color->set_value({1, 1, 1}); + ctx.fade_transition_color->set_value({0, 0, 0}); ctx.function_queue.push(std::bind(&screen_transition::transition, ctx.fade_transition, config::nuptial_flight_fade_in_duration, true, ease::out_sine, true, nullptr)); // Queue control setup - ctx.function_queue.push(std::bind(&nuptial_flight::enable_keeper_controls, this)); + ctx.function_queue.push(std::bind(&nuptial_flight::enable_controls, this)); ctx.logger->pop_task(EXIT_SUCCESS); } @@ -166,113 +139,120 @@ nuptial_flight::~nuptial_flight() { ctx.logger->push_task("Exiting nuptial flight state"); + destroy_camera_rig(); + game::ant::destroy_swarm(ctx, swarm_eid); + ctx.logger->pop_task(EXIT_SUCCESS); } -void nuptial_flight::setup_camera() +void nuptial_flight::create_camera_rig() { - // Switch to surface camera - ctx.underground_camera->set_active(false); - ctx.surface_camera->set_active(true); - - // Create surface camera entity - if (!ctx.entities.count("surface_cam")) + // Construct camera rig pivot constraint + component::constraint::pivot camera_rig_pivot; + camera_rig_pivot.target = swarm_eid; + camera_rig_pivot.offset = {0, 0, 0}; + component::constraint_stack_node camera_rig_pivot_node; + camera_rig_pivot_node.active = true; + camera_rig_pivot_node.weight = 1.0f; + camera_rig_pivot_node.next = entt::null; + camera_rig_pivot_eid = ctx.entity_registry->create(); + ctx.entity_registry->emplace(camera_rig_pivot_eid, camera_rig_pivot); + ctx.entity_registry->emplace(camera_rig_pivot_eid, camera_rig_pivot_node); + + // Construct camera rig copy translation constraint + component::constraint::copy_translation camera_rig_copy_translation; + camera_rig_copy_translation.target = swarm_eid; + camera_rig_copy_translation.copy_x = true; + camera_rig_copy_translation.copy_y = true; + camera_rig_copy_translation.copy_z = true; + camera_rig_copy_translation.invert_x = false; + camera_rig_copy_translation.invert_y = false; + camera_rig_copy_translation.invert_z = false; + camera_rig_copy_translation.offset = true; + component::constraint_stack_node camera_rig_copy_translation_node; + camera_rig_copy_translation_node.active = true; + camera_rig_copy_translation_node.weight = 1.0f; + camera_rig_copy_translation_node.next = camera_rig_pivot_eid; + camera_rig_copy_translation_eid = ctx.entity_registry->create(); + ctx.entity_registry->emplace(camera_rig_copy_translation_eid, camera_rig_copy_translation); + ctx.entity_registry->emplace(camera_rig_copy_translation_eid, camera_rig_copy_translation_node); + + // Construct camera rig spring rotation constraint + component::constraint::spring_rotation camera_rig_spring_rotation; + camera_rig_spring_rotation.spring = { - // Create camera target entity - entity::id target_eid = ctx.entity_registry->create(); - ctx.entities["surface_cam_target"] = target_eid; - { - // Transform - game::component::transform target_transform; - target_transform.local = math::transform::identity; - target_transform.world = target_transform.local; - target_transform.warp = true; - ctx.entity_registry->assign(target_eid, target_transform); - } - - // Create camera entity - entity::id camera_eid = ctx.entity_registry->create(); - ctx.entities["surface_cam"] = camera_eid; - - // Create camera transform component - game::component::transform transform; - transform.local = math::transform::identity; - transform.world = transform.local; - transform.warp = true; - ctx.entity_registry->assign(camera_eid, transform); - - // Create camera camera component - game::component::camera camera; - camera.object = ctx.surface_camera; - ctx.entity_registry->assign(camera_eid, camera); - - // Create camera 3DOF constraint entity - entity::id three_dof_constraint_eid = ctx.entity_registry->create(); - ctx.entities["surface_cam_3dof"] = three_dof_constraint_eid; - { - // Create 3DOF to constraint - game::component::constraint::three_dof three_dof; - three_dof.yaw = 0.0f; - three_dof.pitch = 0.0f; - three_dof.roll = 0.0f; - ctx.entity_registry->assign(three_dof_constraint_eid, three_dof); - - // Create constraint stack node component - game::component::constraint_stack_node node; - node.active = true; - node.weight = 1.0f; - node.next = entt::null; - ctx.entity_registry->assign(three_dof_constraint_eid, node); - } - - // Create camera spring to constraint entity - entity::id spring_constraint_eid = ctx.entity_registry->create(); - { - // Create spring to constraint - game::component::constraint::spring_to spring; - spring.target = target_eid; - spring.translation = {{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}, 1.0f, math::two_pi}; - spring.translation.w = hz_to_rads(8.0f); - - spring.spring_translation = true; - spring.spring_rotation = false; - ctx.entity_registry->assign(spring_constraint_eid, spring); - - // Create constraint stack node component - game::component::constraint_stack_node node; - node.active = true; - node.weight = 1.0f; - node.next = three_dof_constraint_eid; - ctx.entity_registry->assign(spring_constraint_eid, node); - } - - // Create camera constraint stack component - game::component::constraint_stack constraint_stack; - constraint_stack.head = spring_constraint_eid; - ctx.entity_registry->assign(camera_eid, constraint_stack); - } - - game::ant::create_swarm(ctx); - - const float ev100_sunny16 = physics::light::ev::from_settings(16.0f, 1.0f / 100.0f, 100.0f); - const float ev100_looney11 = physics::light::ev::from_settings(11.0f, 1.0f / 100.0f, 100.0f); - ctx.surface_camera->set_exposure(ev100_sunny16); + {0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 0.0f}, + 1.0f, + hz_to_rads(8.0f) + }; + component::constraint_stack_node camera_rig_spring_rotation_node; + camera_rig_spring_rotation_node.active = true; + camera_rig_spring_rotation_node.weight = 1.0f; + camera_rig_spring_rotation_node.next = camera_rig_copy_translation_eid; + camera_rig_spring_rotation_eid = ctx.entity_registry->create(); + ctx.entity_registry->emplace(camera_rig_spring_rotation_eid, camera_rig_spring_rotation); + ctx.entity_registry->emplace(camera_rig_spring_rotation_eid, camera_rig_spring_rotation_node); + + // Construct camera rig spring translation constraint + component::constraint::spring_translation camera_rig_spring_translation; + camera_rig_spring_translation.spring = + { + {0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 0.0f}, + 1.0f, + hz_to_rads(8.0f) + }; + component::constraint_stack_node camera_rig_spring_translation_node; + camera_rig_spring_translation_node.active = true; + camera_rig_spring_translation_node.weight = 1.0f; + camera_rig_spring_translation_node.next = camera_rig_spring_rotation_eid; + camera_rig_spring_translation_eid = ctx.entity_registry->create(); + ctx.entity_registry->emplace(camera_rig_spring_translation_eid, camera_rig_spring_translation); + ctx.entity_registry->emplace(camera_rig_spring_translation_eid, camera_rig_spring_translation_node); + + // Construct camera constraint stack + component::constraint_stack camera_rig_constraint_stack; + camera_rig_constraint_stack.priority = 1; + camera_rig_constraint_stack.head = camera_rig_spring_translation_eid; + + // Construct camera transform component + component::transform camera_rig_transform; + camera_rig_transform.local = math::transform::identity; + camera_rig_transform.world = camera_rig_transform.local; + camera_rig_transform.warp = true; + + // Construct camera rig camera component + component::camera camera_rig_camera; + camera_rig_camera.object = ctx.surface_camera; + + // Construct camera rig entity + camera_rig_eid = ctx.entity_registry->create(); + ctx.entity_registry->emplace(camera_rig_eid, camera_rig_camera); + ctx.entity_registry->emplace(camera_rig_eid, camera_rig_transform); + ctx.entity_registry->emplace(camera_rig_eid, camera_rig_constraint_stack); +} + +void nuptial_flight::destroy_camera_rig() +{ + ctx.entity_registry->destroy(camera_rig_eid); + ctx.entity_registry->destroy(camera_rig_spring_translation_eid); + ctx.entity_registry->destroy(camera_rig_spring_rotation_eid); + ctx.entity_registry->destroy(camera_rig_copy_translation_eid); + ctx.entity_registry->destroy(camera_rig_pivot_eid); } -void nuptial_flight::enable_keeper_controls() +void nuptial_flight::enable_controls() { - // Get camera entities - entity::id camera_eid = ctx.entities["surface_cam"]; - entity::id target_eid = ctx.entities["surface_cam_target"]; - entity::id three_dof_eid = ctx.entities["surface_cam_3dof"]; + mouse_look = false; - const float min_elevation = 0.1f; - const float max_elevation = 100.0f; - const float slow_modifier = 0.25f; - const float fast_modifier = 4.0f; - const float dolly_speed = 5.0f; - const float truck_speed = dolly_speed; - const float pedestal_speed = 5.0f; + float slow_modifier = 0.25f; + float fast_modifier = 4.0f; + float dolly_speed = 5.0f; + float truck_speed = dolly_speed; + float pedestal_speed = 5.0f; float mouse_tilt_sensitivity = 1.0f; float mouse_pan_sensitivity = 1.0f; bool mouse_invert_tilt = false; @@ -282,9 +262,8 @@ void nuptial_flight::enable_keeper_controls() bool gamepad_invert_tilt = false; bool gamepad_invert_pan = false; bool mouse_look_toggle = false; - ctx.mouse_look = false; - const double time_scale = 60.0; - const double ff_time_scale = time_scale * 200; + double time_scale = 0.0; + double ff_time_scale = 60.0 * 200.0; if (ctx.config->contains("mouse_tilt_sensitivity")) mouse_tilt_sensitivity = math::radians((*ctx.config)["mouse_tilt_sensitivity"].get()); @@ -306,246 +285,201 @@ void nuptial_flight::enable_keeper_controls() if (ctx.config->contains("gamepad_invert_pan")) gamepad_invert_pan = (*ctx.config)["gamepad_invert_pan"].get(); - const input::control* move_slow = ctx.controls["move_slow"]; - const input::control* move_fast = ctx.controls["move_fast"]; - const input::control* mouse_look = ctx.controls["mouse_look"]; - - float mouse_tilt_factor = mouse_tilt_sensitivity * (mouse_invert_tilt ? -1.0f : 1.0f); - float mouse_pan_factor = mouse_pan_sensitivity * (mouse_invert_pan ? -1.0f : 1.0f); - float gamepad_tilt_factor = gamepad_tilt_sensitivity * (gamepad_invert_tilt ? -1.0f : 1.0f); - float gamepad_pan_factor = gamepad_pan_sensitivity * (gamepad_invert_pan ? -1.0f : 1.0f); - - ctx.controls["move_forward"]->set_active_callback - ( - [&ctx = this->ctx, target_eid, three_dof_eid, dolly_speed, move_slow, move_fast, slow_modifier, fast_modifier](float value) - { - if (move_slow->is_active()) - value *= slow_modifier; - if (move_fast->is_active()) - value *= fast_modifier; - - auto& three_dof = ctx.entity_registry->get(three_dof_eid); - const math::quaternion yaw = math::angle_axis(three_dof.yaw, {0.0f, 1.0f, 0.0f}); - - const float3 movement = {0.0f, 0.0f, -dolly_speed * value * (1.0f / 60.0f)}; - entity::command::translate(*ctx.entity_registry, target_eid, yaw * movement); - } - ); - - // Dolly backward - ctx.controls["move_back"]->set_active_callback - ( - [&ctx = this->ctx, target_eid, three_dof_eid, dolly_speed, move_slow, move_fast, slow_modifier, fast_modifier](float value) - { - if (move_slow->is_active()) - value *= slow_modifier; - if (move_fast->is_active()) - value *= fast_modifier; - - auto& three_dof = ctx.entity_registry->get(three_dof_eid); - const math::quaternion yaw = math::angle_axis(three_dof.yaw, {0.0f, 1.0f, 0.0f}); - - const float3 movement = {0.0f, 0.0f, dolly_speed * value * (1.0f / 60.0f)}; - entity::command::translate(*ctx.entity_registry, target_eid, yaw * movement); - } - ); - - // Truck right - ctx.controls["move_right"]->set_active_callback - ( - [&ctx = this->ctx, target_eid, three_dof_eid, truck_speed, move_slow, move_fast, slow_modifier, fast_modifier](float value) - { - if (move_slow->is_active()) - value *= slow_modifier; - if (move_fast->is_active()) - value *= fast_modifier; - - auto& three_dof = ctx.entity_registry->get(three_dof_eid); - const math::quaternion yaw = math::angle_axis(three_dof.yaw, {0.0f, 1.0f, 0.0f}); - - const float3 movement = {truck_speed * value * (1.0f / 60.0f), 0.0f, 0.0f}; - entity::command::translate(*ctx.entity_registry, target_eid, yaw * movement); - } - ); + const float mouse_tilt_factor = mouse_tilt_sensitivity * (mouse_invert_tilt ? -1.0f : 1.0f); + const float mouse_pan_factor = mouse_pan_sensitivity * (mouse_invert_pan ? -1.0f : 1.0f); + const float gamepad_tilt_factor = gamepad_tilt_sensitivity * (gamepad_invert_tilt ? -1.0f : 1.0f); + const float gamepad_pan_factor = gamepad_pan_sensitivity * (gamepad_invert_pan ? -1.0f : 1.0f); - // Truck left - ctx.controls["move_left"]->set_active_callback - ( - [&ctx = this->ctx, target_eid, three_dof_eid, truck_speed, move_slow, move_fast, slow_modifier, fast_modifier](float value) - { - if (move_slow->is_active()) - value *= slow_modifier; - if (move_fast->is_active()) - value *= fast_modifier; - - auto& three_dof = ctx.entity_registry->get(three_dof_eid); - const math::quaternion yaw = math::angle_axis(three_dof.yaw, {0.0f, 1.0f, 0.0f}); - - const float3 movement = {-truck_speed * value * (1.0f / 60.0f), 0.0f, 0.0f}; - entity::command::translate(*ctx.entity_registry, target_eid, yaw * movement); - } - ); - - // Pedestal up - ctx.controls["move_up"]->set_active_callback - ( - [&ctx = this->ctx, target_eid, pedestal_speed, move_slow, move_fast, slow_modifier, fast_modifier, max_elevation](float value) - { - if (move_slow->is_active()) - value *= slow_modifier; - if (move_fast->is_active()) - value *= fast_modifier; - - float3 movement = {0.0f, pedestal_speed * value * (1.0f / 60.0f), 0.0f}; - auto transform = entity::command::get_world_transform(*ctx.entity_registry, target_eid); - if (transform.translation.y + movement.y > max_elevation) - movement.y = max_elevation - transform.translation.y; - entity::command::translate(*ctx.entity_registry, target_eid, movement); - } - ); - - // Pedestal down - ctx.controls["move_down"]->set_active_callback - ( - [&ctx = this->ctx, target_eid, pedestal_speed, move_slow, move_fast, slow_modifier, fast_modifier, min_elevation](float value) - { - if (move_slow->is_active()) - value *= slow_modifier; - if (move_fast->is_active()) - value *= fast_modifier; - - float3 movement = {0.0f, -pedestal_speed * value * (1.0f / 60.0f), 0.0f}; - auto transform = entity::command::get_world_transform(*ctx.entity_registry, target_eid); - if (transform.translation.y + movement.y < min_elevation) - movement.y = min_elevation - transform.translation.y; - - entity::command::translate(*ctx.entity_registry, target_eid, movement); - } - ); - - // Mouse rotate + // Mouse look ctx.controls["mouse_look"]->set_activated_callback ( - [&ctx = this->ctx, mouse_look_toggle]() + [&, mouse_look_toggle]() { if (mouse_look_toggle) - ctx.mouse_look = !ctx.mouse_look; + mouse_look = !mouse_look; else - ctx.mouse_look = true; + mouse_look = true; - ctx.app->set_relative_mouse_mode(ctx.mouse_look); + ctx.app->set_relative_mouse_mode(mouse_look); } ); ctx.controls["mouse_look"]->set_deactivated_callback ( - [&ctx = this->ctx, mouse_look_toggle]() + [&, mouse_look_toggle]() { - if (!mouse_look_toggle) + if (!mouse_look_toggle && mouse_look) { - ctx.mouse_look = false; + mouse_look = false; ctx.app->set_relative_mouse_mode(false); } } ); - // Pan left + + // Arc left control + ctx.controls["look_left_mouse"]->set_active_callback + ( + [&, mouse_pan_factor](float value) + { + if (!mouse_look) + return; + + ctx.entity_registry->patch + ( + camera_rig_spring_rotation_eid, + [&](auto& component) + { + component.spring.x1[0] -= mouse_pan_factor * value; + } + ); + } + ); ctx.controls["look_left_gamepad"]->set_active_callback ( - [&ctx = this->ctx, three_dof_eid, gamepad_pan_factor](float value) + [&, gamepad_pan_factor](float value) { - auto& three_dof = ctx.entity_registry->get(three_dof_eid); - three_dof.yaw += gamepad_pan_factor * value * (1.0f / 60.0f); + ctx.entity_registry->patch + ( + camera_rig_spring_rotation_eid, + [&](auto& component) + { + component.spring.x1[0] -= gamepad_pan_factor * value * static_cast(ctx.loop.get_update_period()); + } + ); } ); - ctx.controls["look_left_mouse"]->set_active_callback + + // Arc right control + ctx.controls["look_right_mouse"]->set_active_callback ( - [&ctx = this->ctx, three_dof_eid, mouse_pan_factor](float value) + [&, mouse_pan_factor](float value) { - if (!ctx.mouse_look) + if (!mouse_look) return; - auto& three_dof = ctx.entity_registry->get(three_dof_eid); - three_dof.yaw += mouse_pan_factor * value * (1.0f / 60.0f); + ctx.entity_registry->patch + ( + camera_rig_spring_rotation_eid, + [&](auto& component) + { + component.spring.x1[0] += mouse_pan_factor * value; + } + ); } ); - - // Pan right ctx.controls["look_right_gamepad"]->set_active_callback ( - [&ctx = this->ctx, three_dof_eid, gamepad_pan_factor](float value) + [&, gamepad_pan_factor](float value) { - auto& three_dof = ctx.entity_registry->get(three_dof_eid); - three_dof.yaw -= gamepad_pan_factor * value * (1.0f / 60.0f); + ctx.entity_registry->patch + ( + camera_rig_spring_rotation_eid, + [&](auto& component) + { + component.spring.x1[0] += gamepad_pan_factor * value * static_cast(ctx.loop.get_update_period()); + } + ); } ); - ctx.controls["look_right_mouse"]->set_active_callback + + // Arc down control + ctx.controls["look_down_mouse"]->set_active_callback ( - [&ctx = this->ctx, three_dof_eid, mouse_pan_factor](float value) + [&, mouse_tilt_factor](float value) { - if (!ctx.mouse_look) + if (!mouse_look) return; - auto& three_dof = ctx.entity_registry->get(three_dof_eid); - three_dof.yaw -= mouse_pan_factor * value * (1.0f / 60.0f); + ctx.entity_registry->patch + ( + camera_rig_spring_rotation_eid, + [&](auto& component) + { + component.spring.x1[1] -= mouse_tilt_factor * value; + component.spring.x1[1] = std::max(-math::half_pi, component.spring.x1[1]); + } + ); } ); - // Tilt up - ctx.controls["look_up_gamepad"]->set_active_callback + ctx.controls["look_down_gamepad"]->set_active_callback ( - [&ctx = this->ctx, three_dof_eid, gamepad_tilt_factor](float value) + [&, gamepad_tilt_factor](float value) { - auto& three_dof = ctx.entity_registry->get(three_dof_eid); - three_dof.pitch -= gamepad_tilt_factor * value * (1.0f / 60.0f); - three_dof.pitch = std::max(math::radians(-90.0f), three_dof.pitch); + ctx.entity_registry->patch + ( + camera_rig_spring_rotation_eid, + [&](auto& component) + { + component.spring.x1[1] -= gamepad_tilt_factor * value * static_cast(ctx.loop.get_update_period()); + component.spring.x1[1] = std::max(-math::half_pi, component.spring.x1[1]); + } + ); } ); + + // Arc up control ctx.controls["look_up_mouse"]->set_active_callback ( - [&ctx = this->ctx, three_dof_eid, mouse_tilt_factor](float value) + [&, mouse_tilt_factor](float value) { - if (!ctx.mouse_look) + if (!mouse_look) return; - auto& three_dof = ctx.entity_registry->get(three_dof_eid); - three_dof.pitch -= mouse_tilt_factor * value * (1.0f / 60.0f); - three_dof.pitch = std::max(math::radians(-90.0f), three_dof.pitch); + ctx.entity_registry->patch + ( + camera_rig_spring_rotation_eid, + [&](auto& component) + { + component.spring.x1[1] += mouse_tilt_factor * value; + component.spring.x1[1] = std::min(math::half_pi, component.spring.x1[1]); + } + ); } ); - // Tilt down - ctx.controls["look_down_gamepad"]->set_active_callback + ctx.controls["look_up_gamepad"]->set_active_callback ( - [&ctx = this->ctx, three_dof_eid, gamepad_tilt_factor](float value) + [&, gamepad_tilt_factor](float value) { - auto& three_dof = ctx.entity_registry->get(three_dof_eid); - three_dof.pitch += gamepad_tilt_factor * value * (1.0f / 60.0f); - three_dof.pitch = std::min(math::radians(90.0f), three_dof.pitch); + ctx.entity_registry->patch + ( + camera_rig_spring_rotation_eid, + [&](auto& component) + { + component.spring.x1[1] += gamepad_tilt_factor * value * static_cast(ctx.loop.get_update_period()); + component.spring.x1[1] = std::min(math::half_pi, component.spring.x1[1]); + } + ); } ); - ctx.controls["look_down_mouse"]->set_active_callback + + // Dolly in control + ctx.controls["move_up"]->set_active_callback ( - [&ctx = this->ctx, three_dof_eid, mouse_tilt_factor](float value) + [&](float value) { - if (!ctx.mouse_look) - return; - - auto& three_dof = ctx.entity_registry->get(three_dof_eid); - three_dof.pitch += mouse_tilt_factor * value * (1.0f / 60.0f); - three_dof.pitch = std::min(math::radians(90.0f), three_dof.pitch); + ctx.entity_registry->patch + ( + camera_rig_spring_translation_eid, + [&](auto& component) + { + component.spring.x1[2] -= 100.0f * static_cast(ctx.loop.get_update_period()); + } + ); } ); - // Setup switch POV control - ctx.controls["switch_pov"]->set_activated_callback + // Dolly out control + ctx.controls["move_down"]->set_active_callback ( - [this]() + [&](float value) { - // Disable keeper controls - this->disable_keeper_controls(); - - // Switch to ant - this->is_keeper = false; - - // Enable ant controls - this->enable_ant_controls(); + ctx.entity_registry->patch + ( + camera_rig_spring_translation_eid, + [&](auto& component) + { + component.spring.x1[2] += 100.0f * static_cast(ctx.loop.get_update_period()); + } + ); } ); @@ -604,7 +538,7 @@ void nuptial_flight::enable_keeper_controls() [&ctx = this->ctx](float) { //ctx.astronomy_system->set_exposure_offset(ctx.astronomy_system->get_exposure_offset() - 1.0f); - ctx.surface_camera->set_exposure(ctx.surface_camera->get_exposure() + 3.0f * (1.0f / 60.0f)); + ctx.surface_camera->set_exposure(ctx.surface_camera->get_exposure() + 3.0f * static_cast(ctx.loop.get_update_period())); ctx.logger->log("EV100: " + std::to_string(ctx.surface_camera->get_exposure())); } ); @@ -614,17 +548,17 @@ void nuptial_flight::enable_keeper_controls() [&ctx = this->ctx](float) { //ctx.astronomy_system->set_exposure_offset(ctx.astronomy_system->get_exposure_offset() + 1.0f); - ctx.surface_camera->set_exposure(ctx.surface_camera->get_exposure() - 3.0f * (1.0f / 60.0f)); + ctx.surface_camera->set_exposure(ctx.surface_camera->get_exposure() - 3.0f * static_cast(ctx.loop.get_update_period())); ctx.logger->log("EV100: " + std::to_string(ctx.surface_camera->get_exposure())); } ); - const float wavelength_speed = 20.0 * (1.0 / 60.0); + const float wavelength_speed = 20.0; ctx.controls["dec_red"]->set_active_callback ( [&ctx = this->ctx, wavelength_speed](float) { - ctx.rgb_wavelengths.x -= wavelength_speed; + ctx.rgb_wavelengths.x -= wavelength_speed * ctx.loop.get_update_period(); ctx.atmosphere_system->set_rgb_wavelengths(ctx.rgb_wavelengths * 1e-9); std::stringstream stream; stream << ctx.rgb_wavelengths; @@ -635,7 +569,7 @@ void nuptial_flight::enable_keeper_controls() ( [&ctx = this->ctx, wavelength_speed](float) { - ctx.rgb_wavelengths.x += wavelength_speed; + ctx.rgb_wavelengths.x += wavelength_speed * ctx.loop.get_update_period(); ctx.atmosphere_system->set_rgb_wavelengths(ctx.rgb_wavelengths * 1e-9); std::stringstream stream; stream << ctx.rgb_wavelengths; @@ -647,7 +581,7 @@ void nuptial_flight::enable_keeper_controls() ( [&ctx = this->ctx, wavelength_speed](float) { - ctx.rgb_wavelengths.y -= wavelength_speed; + ctx.rgb_wavelengths.y -= wavelength_speed * ctx.loop.get_update_period(); ctx.atmosphere_system->set_rgb_wavelengths(ctx.rgb_wavelengths * 1e-9); std::stringstream stream; stream << ctx.rgb_wavelengths; @@ -658,7 +592,7 @@ void nuptial_flight::enable_keeper_controls() ( [&ctx = this->ctx, wavelength_speed](float) { - ctx.rgb_wavelengths.y += wavelength_speed; + ctx.rgb_wavelengths.y += wavelength_speed * ctx.loop.get_update_period(); ctx.atmosphere_system->set_rgb_wavelengths(ctx.rgb_wavelengths * 1e-9); std::stringstream stream; stream << ctx.rgb_wavelengths; @@ -670,7 +604,7 @@ void nuptial_flight::enable_keeper_controls() ( [&ctx = this->ctx, wavelength_speed](float) { - ctx.rgb_wavelengths.z -= wavelength_speed; + ctx.rgb_wavelengths.z -= wavelength_speed * ctx.loop.get_update_period(); ctx.atmosphere_system->set_rgb_wavelengths(ctx.rgb_wavelengths * 1e-9); std::stringstream stream; stream << ctx.rgb_wavelengths; @@ -681,7 +615,7 @@ void nuptial_flight::enable_keeper_controls() ( [&ctx = this->ctx, wavelength_speed](float) { - ctx.rgb_wavelengths.z += wavelength_speed; + ctx.rgb_wavelengths.z += wavelength_speed * ctx.loop.get_update_period(); ctx.atmosphere_system->set_rgb_wavelengths(ctx.rgb_wavelengths * 1e-9); std::stringstream stream; stream << ctx.rgb_wavelengths; @@ -690,8 +624,14 @@ void nuptial_flight::enable_keeper_controls() ); } -void nuptial_flight::disable_keeper_controls() +void nuptial_flight::disable_controls() { + if (mouse_look) + { + mouse_look = false; + ctx.app->set_relative_mouse_mode(false); + } + ctx.controls["move_forward"]->set_active_callback(nullptr); ctx.controls["move_back"]->set_active_callback(nullptr); ctx.controls["move_right"]->set_active_callback(nullptr); @@ -716,317 +656,5 @@ void nuptial_flight::disable_keeper_controls() ctx.controls["decrease_exposure"]->set_activated_callback(nullptr); } -void nuptial_flight::enable_ant_controls() -{ - // Get ant controller entities - entity::id ant_eid = ctx.entities["ant"]; - - const float move_forward_speed = 5.0f; - const float move_back_speed = move_forward_speed * 0.5f; - const float strafe_speed = move_forward_speed * 0.5f; - const float turn_speed = math::radians(270.0f); - const float slow_modifier = 0.5f; - const float fast_modifier = 2.0f; - float mouse_tilt_sensitivity = 1.0f; - float mouse_pan_sensitivity = 1.0f; - bool mouse_invert_tilt = false; - bool mouse_invert_pan = false; - float gamepad_tilt_sensitivity = 1.0f; - float gamepad_pan_sensitivity = 1.0f; - bool gamepad_invert_tilt = false; - bool gamepad_invert_pan = false; - const double time_scale = 60.0; - const double ff_time_scale = time_scale * 200; - - if (ctx.config->contains("mouse_tilt_sensitivity")) - mouse_tilt_sensitivity = math::radians((*ctx.config)["mouse_tilt_sensitivity"].get()); - if (ctx.config->contains("mouse_pan_sensitivity")) - mouse_pan_sensitivity = math::radians((*ctx.config)["mouse_pan_sensitivity"].get()); - if (ctx.config->contains("mouse_invert_tilt")) - mouse_invert_tilt = (*ctx.config)["mouse_invert_tilt"].get(); - if (ctx.config->contains("mouse_invert_pan")) - mouse_invert_pan = (*ctx.config)["mouse_invert_pan"].get(); - - if (ctx.config->contains("gamepad_tilt_sensitivity")) - gamepad_tilt_sensitivity = math::radians((*ctx.config)["gamepad_tilt_sensitivity"].get()); - if (ctx.config->contains("gamepad_pan_sensitivity")) - gamepad_pan_sensitivity = math::radians((*ctx.config)["gamepad_pan_sensitivity"].get()); - if (ctx.config->contains("gamepad_invert_tilt")) - gamepad_invert_tilt = (*ctx.config)["gamepad_invert_tilt"].get(); - if (ctx.config->contains("gamepad_invert_pan")) - gamepad_invert_pan = (*ctx.config)["gamepad_invert_pan"].get(); - - const input::control* move_slow = ctx.controls["move_slow"]; - const input::control* move_fast = ctx.controls["move_fast"]; - const input::control* mouse_look = ctx.controls["mouse_look"]; - - float mouse_tilt_factor = mouse_tilt_sensitivity * (mouse_invert_tilt ? -1.0f : 1.0f); - float mouse_pan_factor = mouse_pan_sensitivity * (mouse_invert_pan ? -1.0f : 1.0f); - float gamepad_tilt_factor = gamepad_tilt_sensitivity * (gamepad_invert_tilt ? -1.0f : 1.0f); - float gamepad_pan_factor = gamepad_pan_sensitivity * (gamepad_invert_pan ? -1.0f : 1.0f); - - // Move forward - ctx.controls["move_forward"]->set_active_callback - ( - [&ctx = this->ctx, ant_eid, move_forward_speed, move_slow, move_fast, slow_modifier, fast_modifier](float value) - { - if (move_slow->is_active()) - value *= slow_modifier; - if (move_fast->is_active()) - value *= fast_modifier; - - auto& locomotion = ctx.entity_registry->get(ant_eid); - const math::quaternion yaw = math::angle_axis(locomotion.yaw, {0.0f, 1.0f, 0.0f}); - - const float3 movement = {0.0f, 0.0f, move_forward_speed * value * (1.0f / 60.0f)}; - entity::command::translate(*ctx.entity_registry, ant_eid, yaw * movement); - } - ); - - // Move back - ctx.controls["move_back"]->set_active_callback - ( - [&ctx = this->ctx, ant_eid, move_back_speed, move_slow, move_fast, slow_modifier, fast_modifier](float value) - { - if (move_slow->is_active()) - value *= slow_modifier; - if (move_fast->is_active()) - value *= fast_modifier; - - auto& locomotion = ctx.entity_registry->get(ant_eid); - const math::quaternion yaw = math::angle_axis(locomotion.yaw, {0.0f, 1.0f, 0.0f}); - - const float3 movement = {0.0f, 0.0f, -move_back_speed * value * (1.0f / 60.0f)}; - entity::command::translate(*ctx.entity_registry, ant_eid, yaw * movement); - } - ); - - // Turn right - ctx.controls["move_right"]->set_active_callback - ( - [&ctx = this->ctx, ant_eid, turn_speed, move_slow, move_fast, slow_modifier, fast_modifier](float value) - { - if (move_slow->is_active()) - value *= slow_modifier; - if (move_fast->is_active()) - value *= fast_modifier; - - auto& locomotion = ctx.entity_registry->get(ant_eid); - float delta_yaw = -turn_speed * value * (1.0f / 60.0f); - locomotion.yaw += delta_yaw; - - entity::command::rotate(*ctx.entity_registry, ant_eid, delta_yaw, {0.0f, 1.0f, 0.0f}); - } - ); - - // Truck left - ctx.controls["move_left"]->set_active_callback - ( - [&ctx = this->ctx, ant_eid, turn_speed, move_slow, move_fast, slow_modifier, fast_modifier](float value) - { - if (move_slow->is_active()) - value *= slow_modifier; - if (move_fast->is_active()) - value *= fast_modifier; - - auto& locomotion = ctx.entity_registry->get(ant_eid); - float delta_yaw = turn_speed * value * (1.0f / 60.0f); - locomotion.yaw += delta_yaw; - - entity::command::rotate(*ctx.entity_registry, ant_eid, delta_yaw, {0.0f, 1.0f, 0.0f}); - } - ); - - // Pan left - /* - ctx.controls["look_left_gamepad"]->set_active_callback - ( - [&ctx = this->ctx, three_dof_eid, gamepad_pan_factor](float value) - { - auto& three_dof = ctx.entity_registry->get(three_dof_eid); - three_dof.yaw += gamepad_pan_factor * value * (1.0f / 60.0f); - } - ); - ctx.controls["look_left_mouse"]->set_active_callback - ( - [&ctx = this->ctx, three_dof_eid, mouse_pan_factor](float value) - { - if (!ctx.mouse_look) - return; - - auto& three_dof = ctx.entity_registry->get(three_dof_eid); - three_dof.yaw += mouse_pan_factor * value * (1.0f / 60.0f); - } - ); - - // Pan right - ctx.controls["look_right_gamepad"]->set_active_callback - ( - [&ctx = this->ctx, three_dof_eid, gamepad_pan_factor](float value) - { - auto& three_dof = ctx.entity_registry->get(three_dof_eid); - three_dof.yaw -= gamepad_pan_factor * value * (1.0f / 60.0f); - } - ); - ctx.controls["look_right_mouse"]->set_active_callback - ( - [&ctx = this->ctx, three_dof_eid, mouse_pan_factor](float value) - { - if (!ctx.mouse_look) - return; - - auto& three_dof = ctx.entity_registry->get(three_dof_eid); - three_dof.yaw -= mouse_pan_factor * value * (1.0f / 60.0f); - } - ); - // Tilt up - ctx.controls["look_up_gamepad"]->set_active_callback - ( - [&ctx = this->ctx, three_dof_eid, gamepad_tilt_factor](float value) - { - auto& three_dof = ctx.entity_registry->get(three_dof_eid); - three_dof.pitch -= gamepad_tilt_factor * value * (1.0f / 60.0f); - three_dof.pitch = std::max(math::radians(-90.0f), three_dof.pitch); - } - ); - ctx.controls["look_up_mouse"]->set_active_callback - ( - [&ctx = this->ctx, three_dof_eid, mouse_tilt_factor](float value) - { - if (!ctx.mouse_look) - return; - - auto& three_dof = ctx.entity_registry->get(three_dof_eid); - three_dof.pitch -= mouse_tilt_factor * value * (1.0f / 60.0f); - three_dof.pitch = std::max(math::radians(-90.0f), three_dof.pitch); - } - ); - // Tilt down - ctx.controls["look_down_gamepad"]->set_active_callback - ( - [&ctx = this->ctx, three_dof_eid, gamepad_tilt_factor](float value) - { - auto& three_dof = ctx.entity_registry->get(three_dof_eid); - three_dof.pitch += gamepad_tilt_factor * value * (1.0f / 60.0f); - three_dof.pitch = std::min(math::radians(90.0f), three_dof.pitch); - } - ); - ctx.controls["look_down_mouse"]->set_active_callback - ( - [&ctx = this->ctx, three_dof_eid, mouse_tilt_factor](float value) - { - if (!ctx.mouse_look) - return; - - auto& three_dof = ctx.entity_registry->get(three_dof_eid); - three_dof.pitch += mouse_tilt_factor * value * (1.0f / 60.0f); - three_dof.pitch = std::min(math::radians(90.0f), three_dof.pitch); - } - ); - */ - - // Setup switch POV control - ctx.controls["switch_pov"]->set_activated_callback - ( - [this]() - { - // Disable ant controls - this->disable_ant_controls(); - - // Switch to keeper - this->is_keeper = true; - - // Enable keeper controls - this->enable_keeper_controls(); - } - ); - - // Fast-forward - ctx.controls["fast_forward"]->set_activated_callback - ( - [&ctx = this->ctx, ff_time_scale]() - { - game::world::set_time_scale(ctx, ff_time_scale); - } - ); - ctx.controls["fast_forward"]->set_deactivated_callback - ( - [&ctx = this->ctx, time_scale]() - { - game::world::set_time_scale(ctx, time_scale); - } - ); - ctx.controls["rewind"]->set_activated_callback - ( - [&ctx = this->ctx, ff_time_scale]() - { - game::world::set_time_scale(ctx, -ff_time_scale); - } - ); - ctx.controls["rewind"]->set_deactivated_callback - ( - [&ctx = this->ctx, time_scale]() - { - game::world::set_time_scale(ctx, time_scale); - } - ); - - // Setup pause control - ctx.controls["pause"]->set_activated_callback - ( - [this, &ctx = this->ctx]() - { - // Disable controls - this->disable_controls(); - - // Set resume callback - ctx.resume_callback = [this, &ctx]() - { - this->enable_controls(); - ctx.resume_callback = nullptr; - }; - - // Push pause menu state - ctx.state_machine.emplace(new game::state::pause_menu(ctx)); - } - ); -} - -void nuptial_flight::disable_ant_controls() -{ - ctx.controls["move_forward"]->set_active_callback(nullptr); - ctx.controls["move_back"]->set_active_callback(nullptr); - ctx.controls["move_right"]->set_active_callback(nullptr); - ctx.controls["move_left"]->set_active_callback(nullptr); - ctx.controls["look_left_gamepad"]->set_active_callback(nullptr); - ctx.controls["look_left_mouse"]->set_active_callback(nullptr); - ctx.controls["look_right_gamepad"]->set_active_callback(nullptr); - ctx.controls["look_right_mouse"]->set_active_callback(nullptr); - ctx.controls["look_up_gamepad"]->set_active_callback(nullptr); - ctx.controls["look_up_mouse"]->set_active_callback(nullptr); - ctx.controls["look_down_gamepad"]->set_active_callback(nullptr); - ctx.controls["look_down_mouse"]->set_active_callback(nullptr); - ctx.controls["switch_pov"]->set_activated_callback(nullptr); - ctx.controls["fast_forward"]->set_activated_callback(nullptr); - ctx.controls["rewind"]->set_activated_callback(nullptr); - ctx.controls["pause"]->set_activated_callback(nullptr); -} - -void nuptial_flight::enable_controls() -{ - if (is_keeper) - enable_keeper_controls(); - else - enable_ant_controls(); -} - -void nuptial_flight::disable_controls() -{ - if (is_keeper) - disable_keeper_controls(); - else - disable_ant_controls(); -} - } // namespace state } // namespace game diff --git a/src/game/state/nuptial-flight.hpp b/src/game/state/nuptial-flight.hpp index 51f22cd..45fbf72 100644 --- a/src/game/state/nuptial-flight.hpp +++ b/src/game/state/nuptial-flight.hpp @@ -21,6 +21,7 @@ #define ANTKEEPER_GAME_STATE_NUPTIAL_FLIGHT_HPP #include "game/state/base.hpp" +#include "entity/id.hpp" namespace game { namespace state { @@ -32,15 +33,21 @@ public: virtual ~nuptial_flight(); 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 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 diff --git a/src/game/system/astronomy.cpp b/src/game/system/astronomy.cpp index feffb41..9de1c4d 100644 --- a/src/game/system/astronomy.cpp +++ b/src/game/system/astronomy.cpp @@ -65,32 +65,32 @@ astronomy::astronomy(entity::registry& registry): }; registry.on_construct().connect<&astronomy::on_observer_modified>(this); - registry.on_replace().connect<&astronomy::on_observer_modified>(this); + registry.on_update().connect<&astronomy::on_observer_modified>(this); registry.on_destroy().connect<&astronomy::on_observer_destroyed>(this); registry.on_construct().connect<&astronomy::on_celestial_body_modified>(this); - registry.on_replace().connect<&astronomy::on_celestial_body_modified>(this); + registry.on_update().connect<&astronomy::on_celestial_body_modified>(this); registry.on_destroy().connect<&astronomy::on_celestial_body_destroyed>(this); registry.on_construct().connect<&astronomy::on_orbit_modified>(this); - registry.on_replace().connect<&astronomy::on_orbit_modified>(this); + registry.on_update().connect<&astronomy::on_orbit_modified>(this); registry.on_destroy().connect<&astronomy::on_orbit_destroyed>(this); registry.on_construct().connect<&astronomy::on_atmosphere_modified>(this); - registry.on_replace().connect<&astronomy::on_atmosphere_modified>(this); + registry.on_update().connect<&astronomy::on_atmosphere_modified>(this); registry.on_destroy().connect<&astronomy::on_atmosphere_destroyed>(this); } astronomy::~astronomy() { registry.on_construct().disconnect<&astronomy::on_observer_modified>(this); - registry.on_replace().disconnect<&astronomy::on_observer_modified>(this); + registry.on_update().disconnect<&astronomy::on_observer_modified>(this); registry.on_destroy().disconnect<&astronomy::on_observer_destroyed>(this); registry.on_construct().disconnect<&astronomy::on_celestial_body_modified>(this); - registry.on_replace().disconnect<&astronomy::on_celestial_body_modified>(this); + registry.on_update().disconnect<&astronomy::on_celestial_body_modified>(this); registry.on_destroy().disconnect<&astronomy::on_celestial_body_destroyed>(this); registry.on_construct().disconnect<&astronomy::on_orbit_modified>(this); - registry.on_replace().disconnect<&astronomy::on_orbit_modified>(this); + registry.on_update().disconnect<&astronomy::on_orbit_modified>(this); registry.on_destroy().disconnect<&astronomy::on_orbit_destroyed>(this); registry.on_construct().disconnect<&astronomy::on_atmosphere_modified>(this); - registry.on_replace().disconnect<&astronomy::on_atmosphere_modified>(this); + registry.on_update().disconnect<&astronomy::on_atmosphere_modified>(this); registry.on_destroy().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) observer_modified(); @@ -439,7 +439,7 @@ void astronomy::on_observer_destroyed(entity::registry& registry, entity::id ent 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) reference_body_modified(); @@ -451,7 +451,7 @@ void astronomy::on_celestial_body_destroyed(entity::registry& registry, entity:: 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) reference_orbit_modified(); @@ -463,7 +463,7 @@ void astronomy::on_orbit_destroyed(entity::registry& registry, entity::id entity 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) reference_atmosphere_modified(); diff --git a/src/game/system/astronomy.hpp b/src/game/system/astronomy.hpp index d2ddb10..858cd03 100644 --- a/src/game/system/astronomy.hpp +++ b/src/game/system/astronomy.hpp @@ -91,13 +91,13 @@ public: void set_sky_pass(::render::sky_pass* pass); 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_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_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_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); /// Called each time the observer is modified. diff --git a/src/game/system/atmosphere.cpp b/src/game/system/atmosphere.cpp index f37deb4..13da655 100644 --- a/src/game/system/atmosphere.cpp +++ b/src/game/system/atmosphere.cpp @@ -29,14 +29,21 @@ atmosphere::atmosphere(entity::registry& registry): updatable(registry), rgb_wavelengths{0, 0, 0}, rgb_ozone_cross_sections{0, 0, 0}, - atmosphere_component(nullptr), + active_atmosphere_eid(entt::null), sky_pass(nullptr) { registry.on_construct().connect<&atmosphere::on_atmosphere_construct>(this); - registry.on_replace().connect<&atmosphere::on_atmosphere_replace>(this); + registry.on_update().connect<&atmosphere::on_atmosphere_update>(this); registry.on_destroy().connect<&atmosphere::on_atmosphere_destroy>(this); } +atmosphere::~atmosphere() +{ + registry.on_construct().disconnect<&atmosphere::on_atmosphere_construct>(this); + registry.on_update().disconnect<&atmosphere::on_atmosphere_update>(this); + registry.on_destroy().disconnect<&atmosphere::on_atmosphere_destroy>(this); +} + 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(wavelengths.z * 1e9) }; - atmosphere_modified(); + // Update atmosphere components + registry.view().each + ( + [&](entity::id entity_id, auto& component) + { + update_atmosphere(entity_id); + } + ); } 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(); } -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 - game::component::atmosphere& component = *atmosphere_component; + game::component::atmosphere* component = registry.try_get(entity_id); + + // Abort if entity has no atmosphere component + if (!component) + return; // 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.y), @@ -80,53 +104,65 @@ void atmosphere::atmosphere_modified() }; // 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 - 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.y, 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() { - if (!sky_pass || !atmosphere_component) + // Abort if no sky pass set + if (!sky_pass) 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(active_atmosphere_eid); + + // Abort if entity has no atmosphere component + if (!component) + return; - sky_pass->set_atmosphere_upper_limit(static_cast(component.upper_limit)); - sky_pass->set_rayleigh_parameters(static_cast(component.rayleigh_scale_height), math::type_cast(component.rayleigh_scattering)); - sky_pass->set_mie_parameters(static_cast(component.mie_scale_height), static_cast(component.mie_scattering), static_cast(component.mie_extinction), static_cast(component.mie_anisotropy)); - sky_pass->set_ozone_parameters(static_cast(component.ozone_lower_limit), static_cast(component.ozone_upper_limit), static_cast(component.ozone_mode), math::type_cast(component.ozone_absorption)); - sky_pass->set_airglow_illuminance(math::type_cast(component.airglow_illuminance)); + sky_pass->set_atmosphere_upper_limit(static_cast(component->upper_limit)); + sky_pass->set_rayleigh_parameters(static_cast(component->rayleigh_scale_height), math::type_cast(component->rayleigh_scattering)); + sky_pass->set_mie_parameters(static_cast(component->mie_scale_height), static_cast(component->mie_scattering), static_cast(component->mie_extinction), static_cast(component->mie_anisotropy)); + sky_pass->set_ozone_parameters(static_cast(component->ozone_lower_limit), static_cast(component->ozone_upper_limit), static_cast(component->ozone_mode), math::type_cast(component->ozone_absorption)); + sky_pass->set_airglow_illuminance(math::type_cast(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) { - atmosphere_component = nullptr; + if (entity_id == active_atmosphere_eid) + active_atmosphere_eid = entt::null; } } // namespace system diff --git a/src/game/system/atmosphere.hpp b/src/game/system/atmosphere.hpp index 63306e0..46c4254 100644 --- a/src/game/system/atmosphere.hpp +++ b/src/game/system/atmosphere.hpp @@ -37,6 +37,7 @@ class atmosphere: { public: atmosphere(entity::registry& registry); + ~atmosphere(); virtual void update(double t, double dt); @@ -49,14 +50,22 @@ public: 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: - void atmosphere_modified(); + void update_atmosphere(entity::id entity_id); 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); + entity::id active_atmosphere_eid; double3 rgb_wavelengths; double3 rgb_ozone_cross_sections; game::component::atmosphere* atmosphere_component; diff --git a/src/game/system/blackbody.cpp b/src/game/system/blackbody.cpp index 7cd8f6f..9983821 100644 --- a/src/game/system/blackbody.cpp +++ b/src/game/system/blackbody.cpp @@ -22,6 +22,7 @@ #include "physics/light/blackbody.hpp" #include "physics/light/photometry.hpp" #include "math/quadrature.hpp" +#include namespace game { namespace system { @@ -35,10 +36,17 @@ blackbody::blackbody(entity::registry& registry): std::iota(visible_wavelengths_nm.begin(), visible_wavelengths_nm.end(), 280); registry.on_construct().connect<&blackbody::on_blackbody_construct>(this); - registry.on_replace().connect<&blackbody::on_blackbody_replace>(this); - + registry.on_update().connect<&blackbody::on_blackbody_update>(this); registry.on_construct().connect<&blackbody::on_celestial_body_construct>(this); - registry.on_replace().connect<&blackbody::on_celestial_body_replace>(this); + registry.on_update().connect<&blackbody::on_celestial_body_update>(this); +} + +blackbody::~blackbody() +{ + registry.on_construct().disconnect<&blackbody::on_blackbody_construct>(this); + registry.on_update().disconnect<&blackbody::on_blackbody_update>(this); + registry.on_construct().disconnect<&blackbody::on_celestial_body_construct>(this); + registry.on_update().disconnect<&blackbody::on_celestial_body_update>(this); } 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) { - // Abort if entity has no blackbody component - if (!registry.has(entity_id)) - return; - - // Get blackbody component of the entity - component::blackbody& blackbody = registry.get(entity_id); + // Get blackbody and celestial body components of the entity + auto [blackbody, celestial_body] = registry.try_get(entity_id); - // Clear luminance - blackbody.luminance = {0, 0, 0}; - - // Abort if entity has no celestial body component - if (!registry.has(entity_id)) + // Abort if entity is missing a blackbody or celestial body component + if (!blackbody || !celestial_body) return; - // Get celestial body component of the entity - const component::celestial_body& celestial_body = registry.get(entity_id); - // Construct chromatic adaptation transform const double3x3 cat = color::cat::matrix(illuminant, color::aces::white_point); // 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 const double wavelength_m = wavelength_nm * 1e-9; // Calculate the spectral intensity of the wavelength - const double spectral_radiance = physics::light::blackbody::spectral_radiance(blackbody.temperature, wavelength_m); + const double spectral_radiance = physics::light::blackbody::spectral_radiance(temperature, wavelength_m); // 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 - 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); } -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); } -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); } -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); } diff --git a/src/game/system/blackbody.hpp b/src/game/system/blackbody.hpp index 5cb63c0..3606a80 100644 --- a/src/game/system/blackbody.hpp +++ b/src/game/system/blackbody.hpp @@ -38,6 +38,7 @@ class blackbody: { public: blackbody(entity::registry& registry); + ~blackbody(); virtual void update(double t, double dt); @@ -51,11 +52,11 @@ public: private: 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 illuminant; std::vector visible_wavelengths_nm; diff --git a/src/game/system/collision.cpp b/src/game/system/collision.cpp index de8559a..4edccdf 100644 --- a/src/game/system/collision.cpp +++ b/src/game/system/collision.cpp @@ -27,17 +27,21 @@ collision::collision(entity::registry& registry): updatable(registry) { registry.on_construct().connect<&collision::on_collision_construct>(this); - registry.on_replace().connect<&collision::on_collision_replace>(this); + registry.on_update().connect<&collision::on_collision_update>(this); registry.on_destroy().connect<&collision::on_collision_destroy>(this); } void collision::update(double t, double dt) -{} +{ + registry.on_construct().disconnect<&collision::on_collision_construct>(this); + registry.on_update().disconnect<&collision::on_collision_update>(this); + registry.on_destroy().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) diff --git a/src/game/system/collision.hpp b/src/game/system/collision.hpp index 42dc046..71c452a 100644 --- a/src/game/system/collision.hpp +++ b/src/game/system/collision.hpp @@ -37,8 +37,8 @@ public: virtual void update(double t, double dt); 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); }; diff --git a/src/game/system/constraint.cpp b/src/game/system/constraint.cpp index 8681490..2b7c207 100644 --- a/src/game/system/constraint.cpp +++ b/src/game/system/constraint.cpp @@ -19,186 +19,279 @@ #include "constraint.hpp" #include "game/component/constraint-stack.hpp" -#include "game/component/constraints/constraints.hpp" -#include "game/component/transform.hpp" namespace game { namespace system { constraint::constraint(entity::registry& registry): updatable(registry) -{} +{ + registry.on_construct().connect<&constraint::on_constraint_stack_update>(this); + registry.on_update().connect<&constraint::on_constraint_stack_update>(this); + registry.on_destroy().connect<&constraint::on_constraint_stack_update>(this); +} + +constraint::~constraint() +{ + registry.on_construct().disconnect<&constraint::on_constraint_stack_update>(this); + registry.on_update().disconnect<&constraint::on_constraint_stack_update>(this); + registry.on_destroy().disconnect<&constraint::on_constraint_stack_update>(this); +} void constraint::update(double t, double dt) { // For each entity with transform and constraint stack components - registry.view().each( + registry.view().each + ( [&](entity::id transform_eid, auto& transform, auto& stack) { + // Init world-space transform + transform.world = transform.local; + // Get entity ID of first constraint entity::id constraint_eid = stack.head; // Consecutively apply constraints - while (constraint_eid != entt::null) + while (registry.valid(constraint_eid)) { - // Invalid constraint - if (!registry.has(constraint_eid)) - break; + // Get constraint stack node of the constraint + const component::constraint_stack_node* node = registry.try_get(constraint_eid); - // Get constraint stack node of this constraint - const auto& node = registry.get(constraint_eid); + // Abort if constraint is missing a constraint stack node + if (!node) + break; // Apply constraint if enabled - if (node.active) + if (node->active) handle_constraint(transform, constraint_eid, static_cast(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 + ( + [](const auto& lhs, const auto& rhs) + { + return lhs.priority < rhs.priority; + } + ); } void constraint::handle_constraint(component::transform& transform, entity::id constraint_eid, float dt) { - if (registry.has(constraint_eid)) - handle_copy_translation_constraint(transform, constraint_eid); - else if (registry.has(constraint_eid)) - handle_copy_rotation_constraint(transform, constraint_eid); - else if (registry.has(constraint_eid)) - handle_copy_scale_constraint(transform, constraint_eid); - else if (registry.has(constraint_eid)) - handle_copy_transform_constraint(transform, constraint_eid); - else if (registry.has(constraint_eid)) - handle_track_to_constraint(transform, constraint_eid); - else if (registry.has(constraint_eid)) - handle_three_dof_constraint(transform, constraint_eid); - else if (registry.has(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(constraint_eid); - - if (this->registry.has(params.target)) + if (auto constraint = registry.try_get(constraint_eid); constraint) + handle_copy_translation_constraint(transform, *constraint); + else if (auto constraint = registry.try_get(constraint_eid); constraint) + handle_copy_rotation_constraint(transform, *constraint); + else if (auto constraint = registry.try_get(constraint_eid); constraint) + handle_copy_scale_constraint(transform, *constraint); + else if (auto constraint = registry.try_get(constraint_eid); constraint) + handle_copy_transform_constraint(transform, *constraint); + else if (auto constraint = registry.try_get(constraint_eid); constraint) + handle_track_to_constraint(transform, *constraint); + else if (auto constraint = registry.try_get(constraint_eid); constraint) + handle_three_dof_constraint(transform, *constraint); + else if (auto constraint = registry.try_get(constraint_eid); constraint) + handle_pivot_constraint(transform, *constraint); + else if (auto constraint = registry.try_get(constraint_eid); constraint) + handle_child_of_constraint(transform, *constraint); + else if (auto constraint = registry.try_get(constraint_eid); constraint) + handle_spring_to_constraint(transform, *constraint, dt); + else if (auto constraint = registry.try_get(constraint_eid); constraint) + handle_spring_translation_constraint(transform, *constraint, dt); + else if (auto constraint = registry.try_get(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(params.target).world.translation; - - if (params.offset) + const component::transform* target_transform = registry.try_get(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(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(constraint_eid); - - if (this->registry.has(params.target)) + if (registry.valid(constraint.target)) { - const auto& target_rotation = registry.get(params.target).world.rotation; - transform.world.rotation = target_rotation; + const component::transform* target_transform = registry.try_get(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(constraint_eid); - - if (this->registry.has(params.target)) + if (registry.valid(constraint.target)) { - const auto& target_scale = registry.get(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(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(constraint_eid); - - if (this->registry.has(params.target)) + if (registry.valid(constraint.target)) { - const auto& target_transform = registry.get(params.target).world; - transform.world = target_transform; + const component::transform* target_transform = registry.try_get(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(constraint_eid); - - if (this->registry.has(params.target)) + if (registry.valid(constraint.target)) { - const auto& target_transform = registry.get(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(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(constraint_eid); + // Solve yaw, pitch, and roll angle spring + solve_numeric_spring(constraint.spring, dt); + + // Build yaw, pitch, and roll quaternions + const math::quaternion yaw = math::angle_axis(constraint.spring.x0[0], {0.0f, 1.0f, 0.0f}); + const math::quaternion pitch = math::angle_axis(constraint.spring.x0[1], {-1.0f, 0.0f, 0.0f}); + const math::quaternion roll = math::angle_axis(constraint.spring.x0[2], {0.0f, 0.0f, -1.0f}); - const math::quaternion yaw = math::angle_axis(params.yaw, {0.0f, 1.0f, 0.0f}); - const math::quaternion pitch = math::angle_axis(params.pitch, {-1.0f, 0.0f, 0.0f}); - const math::quaternion 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(constraint_eid); - - if (this->registry.has(params.target)) + if (registry.valid(constraint.target)) { - // Get transform of target entity - const auto& target_transform = registry.get(params.target).world; - - // Spring translation - if (params.spring_translation) + const component::transform* target_transform = registry.try_get(constraint.target); + if (target_transform) { - // Update translation spring target - params.translation.x1 = target_transform.translation; - - // Solve translation spring - solve_numeric_spring(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(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(constraint.rotation, dt); + + // Update transform rotation + transform.world.rotation = math::normalize(math::quaternion{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(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 yaw = math::angle_axis(constraint.yaw, {0.0f, 1.0f, 0.0f}); + const math::quaternion pitch = math::angle_axis(constraint.pitch, {-1.0f, 0.0f, 0.0f}); + const math::quaternion 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(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(params.rotation, dt); - - // Update transform rotation - transform.world.rotation = math::normalize(math::quaternion{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); } } } diff --git a/src/game/system/constraint.hpp b/src/game/system/constraint.hpp index c3e7fd7..d631dbe 100644 --- a/src/game/system/constraint.hpp +++ b/src/game/system/constraint.hpp @@ -22,6 +22,7 @@ #include "game/system/updatable.hpp" #include "game/component/transform.hpp" +#include "game/component/constraint/constraint.hpp" #include "entity/id.hpp" namespace game { @@ -39,18 +40,25 @@ class constraint: { public: constraint(entity::registry& registry); + ~constraint(); + virtual void update(double t, double dt); 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 diff --git a/src/game/system/orbit.cpp b/src/game/system/orbit.cpp index 2d4bf59..deaf36c 100644 --- a/src/game/system/orbit.cpp +++ b/src/game/system/orbit.cpp @@ -31,13 +31,13 @@ orbit::orbit(entity::registry& registry): time_scale(1.0) { registry.on_construct().connect<&orbit::on_orbit_construct>(this); - registry.on_replace().connect<&orbit::on_orbit_replace>(this); + registry.on_update().connect<&orbit::on_orbit_update>(this); } orbit::~orbit() { registry.on_construct().disconnect<&orbit::on_orbit_construct>(this); - registry.on_replace().disconnect<&orbit::on_orbit_replace>(this); + registry.on_update().disconnect<&orbit::on_orbit_update>(this); } void orbit::update(double t, double dt) @@ -84,13 +84,15 @@ void orbit::set_time_scale(double 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(entity_id); 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(entity_id); ephemeris_indices.insert(component.ephemeris_index); } diff --git a/src/game/system/orbit.hpp b/src/game/system/orbit.hpp index 3b5e560..b450cc9 100644 --- a/src/game/system/orbit.hpp +++ b/src/game/system/orbit.hpp @@ -70,8 +70,8 @@ public: void set_ephemeris(const physics::orbit::ephemeris* ephemeris); 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* ephemeris; double time; diff --git a/src/game/system/proteome.cpp b/src/game/system/proteome.cpp index 8ec5416..2aae9a4 100644 --- a/src/game/system/proteome.cpp +++ b/src/game/system/proteome.cpp @@ -29,19 +29,27 @@ proteome::proteome(entity::registry& registry): updatable(registry) { registry.on_construct().connect<&proteome::on_genome_construct>(this); - registry.on_replace().connect<&proteome::on_genome_replace>(this); + registry.on_update().connect<&proteome::on_genome_update>(this); +} + +proteome::~proteome() +{ + registry.on_construct().disconnect<&proteome::on_genome_construct>(this); + registry.on_update().disconnect<&proteome::on_genome_update>(this); } 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(entity_id); + // Allocate a 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 - registry.assign_or_replace(entity_id, proteome_component); + registry.emplace_or_replace(entity_id, proteome_component); } } // namespace system diff --git a/src/game/system/proteome.hpp b/src/game/system/proteome.hpp index 34d7182..75d32d9 100644 --- a/src/game/system/proteome.hpp +++ b/src/game/system/proteome.hpp @@ -35,6 +35,7 @@ class proteome: { public: proteome(entity::registry& registry); + ~proteome(); /** * 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); 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 diff --git a/src/game/system/render.cpp b/src/game/system/render.cpp index 436987f..c75aeb7 100644 --- a/src/game/system/render.cpp +++ b/src/game/system/render.cpp @@ -36,14 +36,23 @@ render::render(entity::registry& registry): renderer(nullptr) { registry.on_construct().connect<&render::on_model_construct>(this); - registry.on_replace().connect<&render::on_model_replace>(this); + registry.on_update().connect<&render::on_model_update>(this); registry.on_destroy().connect<&render::on_model_destroy>(this); - registry.on_construct().connect<&render::on_light_construct>(this); - registry.on_replace().connect<&render::on_light_replace>(this); + registry.on_update().connect<&render::on_light_update>(this); registry.on_destroy().connect<&render::on_light_destroy>(this); } +render::~render() +{ + registry.on_construct().disconnect<&render::on_model_construct>(this); + registry.on_update().disconnect<&render::on_model_update>(this); + registry.on_destroy().disconnect<&render::on_model_destroy>(this); + registry.on_construct().disconnect<&render::on_light_construct>(this); + registry.on_update().disconnect<&render::on_light_update>(this); + registry.on_destroy().disconnect<&render::on_light_destroy>(this); +} + void render::update(double t, double dt) { 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(entity_id); + scene::model_instance* model_instance = new scene::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(entity_id); + update_model_and_materials(entity_id, component); } 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(entity_id); + scene::light* light = nullptr; 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(entity_id); + update_light(entity_id, component); } void render::on_light_destroy(entity::registry& registry, entity::id entity_id) diff --git a/src/game/system/render.hpp b/src/game/system/render.hpp index 2b92d74..ecff8a9 100644 --- a/src/game/system/render.hpp +++ b/src/game/system/render.hpp @@ -38,6 +38,8 @@ class render: public updatable { public: render(entity::registry& registry); + ~render(); + virtual void update(double t, double dt); void draw(double alpha); @@ -55,12 +57,11 @@ private: 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 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_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); double t; diff --git a/src/game/system/spatial.cpp b/src/game/system/spatial.cpp index 7539160..3f9ed18 100644 --- a/src/game/system/spatial.cpp +++ b/src/game/system/spatial.cpp @@ -18,42 +18,27 @@ */ #include "spatial.hpp" -#include "game/component/parent.hpp" #include "game/component/transform.hpp" +#include "game/component/constraint-stack.hpp" +#include namespace game { namespace system { spatial::spatial(entity::registry& registry): - updatable(registry) + updatable(registry), + updated_unconstrained_transforms(registry, entt::collector.update().where(entt::exclude)) {} void spatial::update(double t, double dt) { - /// @TODO: sort transforms by parent, for more multi-level hierarchies - - // Process parent transforms first - registry.view().each( - [&](entity::id entity_id, auto& transform) - { - if (!this->registry.has(entity_id)) - { - transform.world = transform.local; - } - }); - - // Process child transforms second - registry.view().each( - [&](entity::id entity_id, auto& transform) - { - if (this->registry.has(entity_id)) - { - entity::id parent = this->registry.get(entity_id).parent; - const component::transform& parent_transform = this->registry.get(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(transform_eid); + transform.world = transform.local; + } + updated_unconstrained_transforms.clear(); } } // namespace system diff --git a/src/game/system/spatial.hpp b/src/game/system/spatial.hpp index 6773106..f73bbea 100644 --- a/src/game/system/spatial.hpp +++ b/src/game/system/spatial.hpp @@ -21,6 +21,7 @@ #define ANTKEEPER_GAME_SYSTEM_SPATIAL_HPP #include "game/system/updatable.hpp" +#include namespace game { namespace system { @@ -31,6 +32,10 @@ class spatial: public: spatial(entity::registry& registry); virtual void update(double t, double dt); + +private: + /// Observes entities with updated, unconstrained transforms. + entt::observer updated_unconstrained_transforms; }; } // namespace system diff --git a/src/game/system/steering.cpp b/src/game/system/steering.cpp index 203fb91..b69cf04 100644 --- a/src/game/system/steering.cpp +++ b/src/game/system/steering.cpp @@ -83,8 +83,15 @@ void steering::update(double t, double dt) } // Update transform - transform.local.translation = agent.position; - transform.local.rotation = agent.orientation; + registry.patch + ( + entity_id, + [&agent](auto& component) + { + component.local.translation = agent.position; + component.local.rotation = agent.orientation; + } + ); } ); } diff --git a/src/game/system/terrain.cpp b/src/game/system/terrain.cpp index 6a2e53d..bace8ec 100644 --- a/src/game/system/terrain.cpp +++ b/src/game/system/terrain.cpp @@ -68,7 +68,10 @@ terrain::terrain(entity::registry& registry): } terrain::~terrain() -{} +{ + registry.on_construct().disconnect<&terrain::on_terrain_construct>(this); + registry.on_destroy().disconnect<&terrain::on_terrain_destroy>(this); +} void terrain::update(double t, double dt) { @@ -294,7 +297,7 @@ void terrain::set_max_error(double 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_quadspheres[entity_id] = quadsphere; diff --git a/src/game/system/terrain.hpp b/src/game/system/terrain.hpp index b3c57d9..04c1498 100644 --- a/src/game/system/terrain.hpp +++ b/src/game/system/terrain.hpp @@ -101,7 +101,7 @@ private: 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); /** diff --git a/src/game/system/vegetation.cpp b/src/game/system/vegetation.cpp index dbf9f77..55c4185 100644 --- a/src/game/system/vegetation.cpp +++ b/src/game/system/vegetation.cpp @@ -45,7 +45,10 @@ vegetation::vegetation(entity::registry& registry): } vegetation::~vegetation() -{} +{ + registry.on_construct().disconnect<&vegetation::on_terrain_construct>(this); + registry.on_destroy().disconnect<&vegetation::on_terrain_destroy>(this); +} void vegetation::update(double t, double dt) {} @@ -79,7 +82,7 @@ void vegetation::set_scene(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) diff --git a/src/game/system/vegetation.hpp b/src/game/system/vegetation.hpp index f965fc8..a16a572 100644 --- a/src/game/system/vegetation.hpp +++ b/src/game/system/vegetation.hpp @@ -61,7 +61,7 @@ public: void set_scene(scene::collection* collection); 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); float terrain_patch_size; diff --git a/src/game/tools.cpp b/src/game/tools.cpp deleted file mode 100644 index 9916a5d..0000000 --- a/src/game/tools.cpp +++ /dev/null @@ -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 . - */ - -#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* tint = static_cast*>(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(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(planet_eid); - //body.axial_rotation = math::radians(360.0f) * ((float)mouse_x / (float)window_w); - ctx.entity_registry->replace(planet_eid, body); - }; - - // Add tool component to time tool entity - ctx.entity_registry->assign(tool_eid, tool); - - return tool_eid; -} - -} // namespace game diff --git a/src/game/world.cpp b/src/game/world.cpp index d3d1b67..e1cd8e2 100644 --- a/src/game/world.cpp +++ b/src/game/world.cpp @@ -30,6 +30,7 @@ #include "game/component/observer.hpp" #include "game/system/astronomy.hpp" #include "game/system/orbit.hpp" +#include "game/system/atmosphere.hpp" #include "entity/commands.hpp" #include "entity/archetype.hpp" #include "geom/spherical.hpp" @@ -115,7 +116,10 @@ void create_observer(game::context& ctx) observer.longitude = 0.0; // Assign observer component to observer entity - ctx.entity_registry->assign(observer_eid, observer); + ctx.entity_registry->emplace(observer_eid, observer); + + // Set atmosphere system active atmosphere + ctx.atmosphere_system->set_active_atmosphere(observer.reference_body_eid); // Set astronomy system observer ctx.astronomy_system->set_observer(observer_eid); @@ -496,7 +500,7 @@ void create_earth(game::context& ctx) }; terrain.max_lod = 0; terrain.patch_material = nullptr; - //ctx.entity_registry->assign(earth_eid, terrain); + //ctx.entity_registry->emplace(earth_eid, terrain); } catch (const std::exception&) { diff --git a/src/render/material-property.hpp b/src/render/material-property.hpp index 2f3823b..f217b32 100644 --- a/src/render/material-property.hpp +++ b/src/render/material-property.hpp @@ -249,7 +249,7 @@ bool material_property::upload(double a) const { 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(a)))) return false; } @@ -257,7 +257,7 @@ bool material_property::upload(double a) const } else { - return input->upload(values[0].interpolate(a)); + return input->upload(values[0].interpolate(static_cast(a))); } } diff --git a/src/resources/entity-archetype-loader.cpp b/src/resources/entity-archetype-loader.cpp index 73276f6..b5b591f 100644 --- a/src/resources/entity-archetype-loader.cpp +++ b/src/resources/entity-archetype-loader.cpp @@ -76,7 +76,13 @@ static bool load_component_atmosphere(entity::archetype& archetype, const json& component.airglow_illuminance.z = airglow_illuminance[2].get(); } - archetype.set(component); + archetype.stamps.push_back + ( + [component](entt::handle& handle) + { + handle.emplace_or_replace(component); + } + ); return true; } @@ -91,7 +97,13 @@ static bool load_component_behavior(entity::archetype& archetype, resource_manag component.behavior_tree = resource_manager.load(element["file"].get()); } - archetype.set(component); + archetype.stamps.push_back + ( + [component](entt::handle& handle) + { + handle.emplace_or_replace(component); + } + ); return (component.behavior_tree != nullptr); } @@ -104,7 +116,13 @@ static bool load_component_blackbody(entity::archetype& archetype, const json& e if (element.contains("temperature")) component.temperature = element["temperature"].get(); - archetype.set(component); + archetype.stamps.push_back + ( + [component](entt::handle& handle) + { + handle.emplace_or_replace(component); + } + ); return true; } @@ -140,8 +158,14 @@ static bool load_component_celestial_body(entity::archetype& archetype, const js } if (element.contains("albedo")) component.albedo = element["albedo"].get(); - - archetype.set(component); + + archetype.stamps.push_back + ( + [component](entt::handle& handle) + { + handle.emplace_or_replace(component); + } + ); return true; } @@ -156,7 +180,13 @@ static bool load_component_collision(entity::archetype& archetype, resource_mana component.mesh = resource_manager.load(element["file"].get()); } - archetype.set(component); + archetype.stamps.push_back + ( + [component](entt::handle& handle) + { + handle.emplace_or_replace(component); + } + ); return (component.mesh != nullptr); } @@ -168,8 +198,14 @@ static bool load_component_diffuse_reflector(entity::archetype& archetype, const if (element.contains("albedo")) component.albedo = element["albedo"].get(); - - archetype.set(component); + + archetype.stamps.push_back + ( + [component](entt::handle& handle) + { + handle.emplace_or_replace(component); + } + ); return true; } @@ -186,7 +222,13 @@ static bool load_component_model(entity::archetype& archetype, resource_manager& component.render_model = resource_manager.load(element["file"].get()); } - archetype.set(component); + archetype.stamps.push_back + ( + [component](entt::handle& handle) + { + handle.emplace_or_replace(component); + } + ); return true; } @@ -204,8 +246,14 @@ static bool load_component_orbit(entity::archetype& archetype, const json& eleme component.ephemeris_index = element["ephemeris_index"].get(); if (element.contains("scale")) component.scale = element["scale"].get(); - - archetype.set(component); + + archetype.stamps.push_back + ( + [component](entt::handle& handle) + { + handle.emplace_or_replace(component); + } + ); return true; } @@ -242,7 +290,14 @@ static bool load_component_transform(entity::archetype& archetype, const json& e } component.world = component.local; - archetype.set(component); + + archetype.stamps.push_back + ( + [component](entt::handle& handle) + { + handle.emplace_or_replace(component); + } + ); 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() + "\""); - return true; + return false; } template <> entity::archetype* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) { // Allocate archetype - entity::archetype* archetype = new entity::archetype(resource_manager->get_archetype_registry()); + entity::archetype* archetype = new entity::archetype(); // Read file into buffer std::size_t size = static_cast(PHYSFS_fileLength(file));