diff --git a/CMakeLists.txt b/CMakeLists.txt index 8b94016..7faddc1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,6 @@ cmake_minimum_required(VERSION 3.7) + option(VERSION_STRING "Project version string" "0.0.0") project(antkeeper VERSION ${VERSION_STRING} LANGUAGES CXX) diff --git a/src/animation/spring.hpp b/src/animation/spring.hpp index 2c5d2dc..1cd80ea 100644 --- a/src/animation/spring.hpp +++ b/src/animation/spring.hpp @@ -84,6 +84,15 @@ T hz_to_rads(T hz); template T rads_to_hz(T rads); +/** + * Converts a period from seconds to radians per second. + * + * @param t Period, in seconds. + * @return Angular frequency, in radians per second. + */ +template +T period_to_rads(T t); + template void spring(T& x0, T& v, const T& x1, S z, S w, S dt) { @@ -116,4 +125,10 @@ inline T rads_to_hz(T rads) return rads / math::two_pi; } +template +inline T period_to_rads(T t) +{ + return math::two_pi / t; +} + #endif // ANTKEEPER_SPRING_HPP diff --git a/src/game/component/spring.hpp b/src/game/component/spring.hpp new file mode 100644 index 0000000..be225e1 --- /dev/null +++ b/src/game/component/spring.hpp @@ -0,0 +1,81 @@ +/* + * 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_SPRING_HPP +#define ANTKEEPER_GAME_COMPONENT_SPRING_HPP + +#include "animation/spring.hpp" +#include "utility/fundamental-types.hpp" +#include + +namespace game { +namespace component { + +/** + * Numeric spring with one component. + */ +struct spring1 +{ + /// Numeric spring with one component. + numeric_spring spring; + + /// Spring solved callback. + std::function callback; +}; + +/** + * Numeric spring with two components. + */ +struct spring2 +{ + /// Numeric spring with two components. + numeric_spring spring; + + /// Spring solved callback. + std::function callback; +}; + +/** + * Numeric spring with three components. + */ +struct spring3 +{ + /// Numeric spring with three components. + numeric_spring spring; + + /// Spring solved callback. + std::function callback; +}; + +/** + * Numeric spring with four components. + */ +struct spring4 +{ + /// Numeric spring with four components. + numeric_spring spring; + + /// Spring solved callback. + std::function callback; +}; + +} // namespace component +} // namespace game + +#endif // ANTKEEPER_GAME_COMPONENT_SPRING_HPP diff --git a/src/game/context.hpp b/src/game/context.hpp index bf4f9b0..6ec4d47 100644 --- a/src/game/context.hpp +++ b/src/game/context.hpp @@ -98,6 +98,7 @@ namespace game class samara; class proteome; class steering; + class spring; } } @@ -286,6 +287,7 @@ struct context game::system::subterrain* subterrain_system; game::system::terrain* terrain_system; game::system::vegetation* vegetation_system; + game::system::spring* spring_system; game::system::spatial* spatial_system; game::system::painting* painting_system; game::system::blackbody* blackbody_system; diff --git a/src/game/state/boot.cpp b/src/game/state/boot.cpp index 3ad0132..7c670ee 100644 --- a/src/game/state/boot.cpp +++ b/src/game/state/boot.cpp @@ -75,6 +75,7 @@ #include "game/system/orbit.hpp" #include "game/system/proteome.hpp" #include "game/system/steering.hpp" +#include "game/system/spring.hpp" #include "entity/commands.hpp" #include "utility/paths.hpp" #include "event/event-dispatcher.hpp" @@ -874,6 +875,9 @@ void boot::setup_systems() // Setup steering system ctx.steering_system = new game::system::steering(*ctx.entity_registry); + // Setup spring system + ctx.spring_system = new game::system::spring(*ctx.entity_registry); + // Setup spatial system ctx.spatial_system = new game::system::spatial(*ctx.entity_registry); @@ -892,7 +896,7 @@ void boot::setup_systems() ctx.blackbody_system->set_illuminant(color::illuminant::deg2::d55); // RGB wavelengths for atmospheric scatteering - ctx.rgb_wavelengths = {645, 575, 440}; + ctx.rgb_wavelengths = {640, 575, 440}; // Setup atmosphere system ctx.atmosphere_system = new game::system::atmosphere(*ctx.entity_registry); @@ -1139,6 +1143,7 @@ void boot::setup_loop() ctx.blackbody_system->update(t, dt); ctx.atmosphere_system->update(t, dt); ctx.astronomy_system->update(t, dt); + ctx.spring_system->update(t, dt); ctx.spatial_system->update(t, dt); ctx.constraint_system->update(t, dt); ctx.painting_system->update(t, dt); diff --git a/src/game/state/main-menu.cpp b/src/game/state/main-menu.cpp index e5674e1..c5e8af7 100644 --- a/src/game/state/main-menu.cpp +++ b/src/game/state/main-menu.cpp @@ -246,7 +246,7 @@ main_menu::main_menu(game::context& ctx, bool fade_in): { game::world::cosmogenesis(ctx); game::world::create_observer(ctx); - game::load::biome(ctx, "debug.bio"); + game::load::biome(ctx, "desert-scrub.bio"); } // Set world time @@ -257,9 +257,13 @@ main_menu::main_menu(game::context& ctx, bool fade_in): ctx.surface_camera->set_active(true); const float ev100_sunny16 = physics::light::ev::from_settings(16.0f, 1.0f / 100.0f, 100.0f); - ctx.surface_camera->set_exposure(15.5f); + ctx.surface_camera->set_exposure(ev100_sunny16); - ctx.surface_camera->look_at({0, 10, 0}, {0, 0, 0}, {0, 0, 1}); + const auto& viewport_dimensions = ctx.app->get_viewport_dimensions(); + const float aspect_ratio = static_cast(viewport_dimensions[0]) / static_cast(viewport_dimensions[1]); + + ctx.surface_camera->look_at({0, 3.0f, 0}, {0, 0, 0}, {0, 0, 1}); + ctx.surface_camera->set_perspective(math::vertical_fov(math::radians(100.0f), aspect_ratio), ctx.surface_camera->get_aspect_ratio(), ctx.surface_camera->get_clip_near(), ctx.surface_camera->get_clip_far()); ctx.surface_camera->update_tweens(); // Setup and enable sky and ground passes @@ -271,7 +275,6 @@ main_menu::main_menu(game::context& ctx, bool fade_in): //if (!ctx.menu_bg_billboard->is_active()) // game::menu::fade_in_bg(ctx); - ctx.logger->pop_task(EXIT_SUCCESS); } diff --git a/src/game/state/nest-selection.cpp b/src/game/state/nest-selection.cpp index 099a787..6192c0d 100644 --- a/src/game/state/nest-selection.cpp +++ b/src/game/state/nest-selection.cpp @@ -19,20 +19,22 @@ #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/system/collision.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/constraint.hpp" #include "game/component/constraint-stack.hpp" #include "game/component/steering.hpp" +#include "game/component/picking.hpp" +#include "game/component/spring.hpp" +#include "game/controls.hpp" #include "entity/commands.hpp" #include "animation/screen-transition.hpp" #include "animation/ease.hpp" @@ -48,7 +50,9 @@ #include "game/ant/morphogenesis.hpp" #include "math/interpolation.hpp" #include "physics/light/exposure.hpp" -#include "color/color.hpp" +#include "application.hpp" +#include "input/mouse.hpp" +#include "math/projection.hpp" #include using namespace game::ant; @@ -61,37 +65,6 @@ nest_selection::nest_selection(game::context& 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); @@ -105,55 +78,62 @@ nest_selection::nest_selection(game::context& ctx): game::world::create_observer(ctx); } - // Load biome - game::load::biome(ctx, "desert-scrub.bio"); + // Init time scale + double time_scale = 1.0; - // Set world time - game::world::set_time(ctx, 2022, 10, 9, 12, 0, 0.0); + // Read time scale settings + if (ctx.config->contains("time_scale")) + time_scale = (*ctx.config)["time_scale"].get(); - // Set world time scale - game::world::set_time_scale(ctx, 60.0); + // Set time scale + game::world::set_time_scale(ctx, time_scale); // 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; - } + // Switch to surface camera + ctx.underground_camera->set_active(false); + ctx.surface_camera->set_active(true); - // Start as ant-keeper - is_keeper = true; + // 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); - // Setup camera - setup_camera(); + const auto& viewport_dimensions = ctx.app->get_viewport_dimensions(); + const float aspect_ratio = static_cast(viewport_dimensions[0]) / static_cast(viewport_dimensions[1]); + + // Init first person camera rig parameters + first_person_camera_rig_translation_spring_angular_frequency = period_to_rads(0.125f); + first_person_camera_rig_rotation_spring_angular_frequency = period_to_rads(0.125f); + first_person_camera_rig_fov_spring_angular_frequency = period_to_rads(0.125f); + first_person_camera_rig_min_elevation = 0.25f; + first_person_camera_rig_max_elevation = 150.0f; + first_person_camera_near_fov = math::vertical_fov(math::radians(100.0f), aspect_ratio); + first_person_camera_far_fov = math::vertical_fov(math::radians(60.0f), aspect_ratio); + first_person_camera_near_speed = 5.0f; + first_person_camera_far_speed = 90.0f; + first_person_camera_rig_pedestal_speed = 2.0f; + first_person_camera_rig_pedestal = 0.0f; + + // Read first person camera rig settings + if (ctx.config->contains("standing_eye_height")) + first_person_camera_rig_max_elevation = (*ctx.config)["standing_eye_height"].get(); + if (ctx.config->contains("walking_speed")) + first_person_camera_far_speed = (*ctx.config)["walking_speed"].get(); + if (ctx.config->contains("near_fov")) + first_person_camera_near_fov = math::vertical_fov(math::radians((*ctx.config)["near_fov"].get()), aspect_ratio); + if (ctx.config->contains("far_fov")) + first_person_camera_far_fov = math::vertical_fov(math::radians((*ctx.config)["far_fov"].get()), aspect_ratio); + + // Create first person camera rig + create_first_person_camera_rig(); + + // Satisfy first person camera rig constraints + satisfy_first_person_camera_rig_constraints(); // Queue control setup - ctx.function_queue.push(std::bind(&nest_selection::enable_keeper_controls, this)); + ctx.function_queue.push(std::bind(&nest_selection::enable_controls, this)); ctx.logger->pop_task(EXIT_SUCCESS); } @@ -162,126 +142,201 @@ nest_selection::~nest_selection() { ctx.logger->push_task("Exiting nest selection state"); + destroy_first_person_camera_rig(); + ctx.logger->pop_task(EXIT_SUCCESS); } -void nest_selection::setup_camera() +void nest_selection::create_first_person_camera_rig() +{ + // Construct first person camera rig spring rotation constraint + component::constraint::spring_rotation first_person_camera_rig_spring_rotation; + first_person_camera_rig_spring_rotation.spring = + { + {0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 0.0f}, + 1.0f, + first_person_camera_rig_rotation_spring_angular_frequency + }; + component::constraint_stack_node first_person_camera_rig_spring_rotation_node; + first_person_camera_rig_spring_rotation_node.active = true; + first_person_camera_rig_spring_rotation_node.weight = 1.0f; + first_person_camera_rig_spring_rotation_node.next = entt::null; + first_person_camera_rig_spring_rotation_eid = ctx.entity_registry->create(); + ctx.entity_registry->emplace(first_person_camera_rig_spring_rotation_eid, first_person_camera_rig_spring_rotation); + ctx.entity_registry->emplace(first_person_camera_rig_spring_rotation_eid, first_person_camera_rig_spring_rotation_node); + + // Construct first person camera rig spring translation constraint + component::constraint::spring_translation first_person_camera_rig_spring_translation; + first_person_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, + first_person_camera_rig_translation_spring_angular_frequency + }; + component::constraint_stack_node first_person_camera_rig_spring_translation_node; + first_person_camera_rig_spring_translation_node.active = true; + first_person_camera_rig_spring_translation_node.weight = 1.0f; + first_person_camera_rig_spring_translation_node.next = first_person_camera_rig_spring_rotation_eid; + first_person_camera_rig_spring_translation_eid = ctx.entity_registry->create(); + ctx.entity_registry->emplace(first_person_camera_rig_spring_translation_eid, first_person_camera_rig_spring_translation); + ctx.entity_registry->emplace(first_person_camera_rig_spring_translation_eid, first_person_camera_rig_spring_translation_node); + + // Construct first person camera rig constraint stack + component::constraint_stack first_person_camera_rig_constraint_stack; + first_person_camera_rig_constraint_stack.priority = 2; + first_person_camera_rig_constraint_stack.head = first_person_camera_rig_spring_translation_eid; + + // Construct first person camera rig transform component + component::transform first_person_camera_rig_transform; + first_person_camera_rig_transform.local = math::transform::identity; + first_person_camera_rig_transform.world = first_person_camera_rig_transform.local; + first_person_camera_rig_transform.warp = true; + + // Construct first person camera rig camera component + component::camera first_person_camera_rig_camera; + first_person_camera_rig_camera.object = ctx.surface_camera; + + // Construct first person camera rig entity + first_person_camera_rig_eid = ctx.entity_registry->create(); + ctx.entity_registry->emplace(first_person_camera_rig_eid, first_person_camera_rig_camera); + ctx.entity_registry->emplace(first_person_camera_rig_eid, first_person_camera_rig_transform); + ctx.entity_registry->emplace(first_person_camera_rig_eid, first_person_camera_rig_constraint_stack); + + // Construct first person camera rig fov spring + component::spring1 first_person_camera_rig_fov_spring; + first_person_camera_rig_fov_spring.spring = + { + 0.0f, + 0.0f, + 0.0f, + 1.0f, + first_person_camera_rig_fov_spring_angular_frequency + }; + first_person_camera_rig_fov_spring.callback = [&](float fov) + { + ctx.surface_camera->set_perspective(fov, ctx.surface_camera->get_aspect_ratio(), ctx.surface_camera->get_clip_near(), ctx.surface_camera->get_clip_far()); + }; + + // Construct first person camera rig fov spring entity + first_person_camera_rig_fov_spring_eid = ctx.entity_registry->create(); + ctx.entity_registry->emplace(first_person_camera_rig_fov_spring_eid, first_person_camera_rig_fov_spring); + + set_first_person_camera_rig_pedestal(first_person_camera_rig_pedestal); +} + +void nest_selection::destroy_first_person_camera_rig() { - // Switch to surface camera - ctx.underground_camera->set_active(false); - ctx.surface_camera->set_active(true); + ctx.entity_registry->destroy(first_person_camera_rig_eid); + ctx.entity_registry->destroy(first_person_camera_rig_spring_translation_eid); + ctx.entity_registry->destroy(first_person_camera_rig_spring_rotation_eid); + ctx.entity_registry->destroy(first_person_camera_rig_fov_spring_eid); +} + +void nest_selection::set_first_person_camera_rig_pedestal(float pedestal) +{ + first_person_camera_rig_pedestal = pedestal; + const float elevation = math::log_lerp(first_person_camera_rig_min_elevation, first_person_camera_rig_max_elevation, first_person_camera_rig_pedestal); + const float fov = math::log_lerp(first_person_camera_near_fov, first_person_camera_far_fov, first_person_camera_rig_pedestal); - // 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; + ctx.entity_registry->patch + ( + first_person_camera_rig_spring_translation_eid, + [&](auto& component) { - // 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); + component.spring.x1[1] = elevation; } - - // 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; + ); + + ctx.entity_registry->patch + ( + first_person_camera_rig_fov_spring_eid, + [&](auto& component) { - // 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); + component.spring.x1 = fov; } - - // Create camera spring to constraint entity - entity::id spring_constraint_eid = ctx.entity_registry->create(); + ); +} + +void nest_selection::move_first_person_camera_rig(const float2& direction, float factor) +{ + const float speed = math::log_lerp(first_person_camera_near_speed, first_person_camera_far_speed, first_person_camera_rig_pedestal) * factor; + + const component::constraint::spring_rotation& first_person_camera_rig_spring_rotation = ctx.entity_registry->get(first_person_camera_rig_spring_rotation_eid); + + const math::quaternion yaw_rotation = math::angle_axis(first_person_camera_rig_spring_rotation.spring.x0[0], float3{0.0f, 1.0f, 0.0f}); + const float3 rotated_direction = math::normalize(yaw_rotation * float3{direction[0], 0.0f, direction[1]}); + const float3 velocity = rotated_direction * speed; + + ctx.entity_registry->patch + ( + first_person_camera_rig_spring_translation_eid, + [&](auto& component) { - // 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); + component.spring.x1 += velocity * static_cast(ctx.loop.get_update_period()); } - - // 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); - } + ); +} + +void nest_selection::satisfy_first_person_camera_rig_constraints() +{ + // Satisfy first person camera rig spring translation constraint + ctx.entity_registry->patch + ( + first_person_camera_rig_spring_translation_eid, + [&](auto& component) + { + component.spring.x0 = component.spring.x1; + component.spring.v *= 0.0f; + } + ); - game::ant::create_swarm(ctx); + // Satisfy first person camera rig spring rotation constraint + ctx.entity_registry->patch + ( + first_person_camera_rig_spring_rotation_eid, + [&](auto& component) + { + component.spring.x0 = component.spring.x1; + component.spring.v *= 0.0f; + } + ); - 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); + // Satisfy first person camera rig fov spring + ctx.entity_registry->patch + ( + first_person_camera_rig_fov_spring_eid, + [&](auto& component) + { + component.spring.x0 = component.spring.x1; + component.spring.v *= 0.0f; + } + ); } -void nest_selection::enable_keeper_controls() +void nest_selection::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"]; + // Reset mouse look + mouse_look = false; + + double time_scale = 0.0; + double ff_time_scale = 60.0 * 200.0; - 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; + // Init control settings float mouse_tilt_sensitivity = 1.0f; float mouse_pan_sensitivity = 1.0f; bool mouse_invert_tilt = false; bool mouse_invert_pan = false; + bool mouse_look_toggle = 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; + // Read control settings 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")) @@ -292,7 +347,6 @@ void nest_selection::enable_keeper_controls() 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")) @@ -302,246 +356,242 @@ void nest_selection::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); + // Determine tilt and pan factors according to sensitivity and inversion + 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); - ctx.controls["move_forward"]->set_active_callback + // Mouse look control + ctx.controls["mouse_look"]->set_activated_callback ( - [&ctx = this->ctx, target_eid, three_dof_eid, dolly_speed, move_slow, move_fast, slow_modifier, fast_modifier](float value) + [&, mouse_look_toggle]() { - if (move_slow->is_active()) - value *= slow_modifier; - if (move_fast->is_active()) - value *= fast_modifier; + if (mouse_look_toggle) + mouse_look = !mouse_look; + else + mouse_look = true; - 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); + ctx.app->set_relative_mouse_mode(mouse_look); } ); - - // Dolly backward - ctx.controls["move_back"]->set_active_callback + ctx.controls["mouse_look"]->set_deactivated_callback ( - [&ctx = this->ctx, target_eid, three_dof_eid, dolly_speed, move_slow, move_fast, slow_modifier, fast_modifier](float value) + [&, mouse_look_toggle]() { - 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); + if (!mouse_look_toggle && mouse_look) + { + mouse_look = false; + ctx.app->set_relative_mouse_mode(false); + } } ); - // Truck right - ctx.controls["move_right"]->set_active_callback + // Look right control + ctx.controls["look_right_mouse"]->set_active_callback ( - [&ctx = this->ctx, target_eid, three_dof_eid, truck_speed, move_slow, move_fast, slow_modifier, fast_modifier](float value) + [&, mouse_pan_factor](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}); + if (!mouse_look) + return; - 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); + ctx.entity_registry->patch + ( + first_person_camera_rig_spring_rotation_eid, + [&, mouse_pan_factor](auto& component) + { + component.spring.x1[0] -= mouse_pan_factor * value; + } + ); + } + ); + ctx.controls["look_right_gamepad"]->set_active_callback + ( + [&, gamepad_pan_factor](float value) + { + ctx.entity_registry->patch + ( + first_person_camera_rig_spring_rotation_eid, + [&, gamepad_pan_factor](auto& component) + { + component.spring.x1[0] -= gamepad_pan_factor * value * static_cast(ctx.loop.get_update_period()); + } + ); } ); - // Truck left - ctx.controls["move_left"]->set_active_callback + // Look left control + ctx.controls["look_left_mouse"]->set_active_callback ( - [&ctx = this->ctx, target_eid, three_dof_eid, truck_speed, move_slow, move_fast, slow_modifier, fast_modifier](float value) + [&, mouse_pan_factor](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}); + if (!mouse_look) + return; - 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); + ctx.entity_registry->patch + ( + first_person_camera_rig_spring_rotation_eid, + [&, mouse_pan_factor](auto& component) + { + component.spring.x1[0] += mouse_pan_factor * value; + } + ); } ); - - // Pedestal up - ctx.controls["move_up"]->set_active_callback + ctx.controls["look_left_gamepad"]->set_active_callback ( - [&ctx = this->ctx, target_eid, pedestal_speed, move_slow, move_fast, slow_modifier, fast_modifier, max_elevation](float value) + [&, gamepad_pan_factor](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); + ctx.entity_registry->patch + ( + first_person_camera_rig_spring_rotation_eid, + [&, gamepad_pan_factor](auto& component) + { + component.spring.x1[0] += gamepad_pan_factor * value * static_cast(ctx.loop.get_update_period()); + } + ); } ); - // Pedestal down - ctx.controls["move_down"]->set_active_callback + // Look up control + ctx.controls["look_up_mouse"]->set_active_callback ( - [&ctx = this->ctx, target_eid, pedestal_speed, move_slow, move_fast, slow_modifier, fast_modifier, min_elevation](float value) + [&, mouse_tilt_factor](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; + if (!mouse_look) + return; - entity::command::translate(*ctx.entity_registry, target_eid, movement); + ctx.entity_registry->patch + ( + first_person_camera_rig_spring_rotation_eid, + [&, mouse_tilt_factor](auto& component) + { + component.spring.x1[1] -= mouse_tilt_factor * value; + component.spring.x1[1] = std::max(-math::half_pi, component.spring.x1[1]); + } + ); } ); - - // Mouse rotate - ctx.controls["mouse_look"]->set_activated_callback + ctx.controls["look_up_gamepad"]->set_active_callback ( - [&ctx = this->ctx, mouse_look_toggle]() + [&, gamepad_tilt_factor](float value) { - 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.entity_registry->patch + ( + first_person_camera_rig_spring_rotation_eid, + [&, gamepad_tilt_factor](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]); + } + ); } ); - ctx.controls["mouse_look"]->set_deactivated_callback + + // Look down control + ctx.controls["look_down_mouse"]->set_active_callback ( - [&ctx = this->ctx, mouse_look_toggle]() + [&, mouse_tilt_factor](float value) { - if (!mouse_look_toggle) - { - ctx.mouse_look = false; - ctx.app->set_relative_mouse_mode(false); - } + if (!mouse_look) + return; + + ctx.entity_registry->patch + ( + first_person_camera_rig_spring_rotation_eid, + [&, mouse_tilt_factor](auto& component) + { + component.spring.x1[1] += mouse_tilt_factor * value; + component.spring.x1[1] = std::min(math::half_pi, component.spring.x1[1]); + } + ); } ); - // Pan left - ctx.controls["look_left_gamepad"]->set_active_callback + ctx.controls["look_down_gamepad"]->set_active_callback ( - [&ctx = this->ctx, three_dof_eid, gamepad_pan_factor](float value) + [&, gamepad_tilt_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.entity_registry->patch + ( + first_person_camera_rig_spring_rotation_eid, + [&, gamepad_tilt_factor](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_left_mouse"]->set_active_callback + + // Pedestal up control + ctx.controls["move_up"]->set_active_callback ( - [&ctx = this->ctx, three_dof_eid, mouse_pan_factor](float value) + [&](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()); + set_first_person_camera_rig_pedestal(std::min(1.0f, first_person_camera_rig_pedestal + first_person_camera_rig_pedestal_speed * static_cast(ctx.loop.get_update_period()))); } ); - // Pan right - ctx.controls["look_right_gamepad"]->set_active_callback + // Pedestal down control + ctx.controls["move_down"]->set_active_callback ( - [&ctx = this->ctx, three_dof_eid, gamepad_pan_factor](float value) + [&](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()); + set_first_person_camera_rig_pedestal(std::max(0.0f, first_person_camera_rig_pedestal - first_person_camera_rig_pedestal_speed * static_cast(ctx.loop.get_update_period()))); } ); - ctx.controls["look_right_mouse"]->set_active_callback + + // Mouse select control + ctx.controls["select_mouse"]->set_activated_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 + + // Move forward control + ctx.controls["move_forward"]->set_active_callback ( - [&ctx = this->ctx, three_dof_eid, gamepad_tilt_factor](float value) + [&](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); + move_first_person_camera_rig({0, -1}, value); } ); - ctx.controls["look_up_mouse"]->set_active_callback + + // Move back control + ctx.controls["move_back"]->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 * static_cast(ctx.loop.get_update_period()); - three_dof.pitch = std::max(math::radians(-90.0f), three_dof.pitch); + move_first_person_camera_rig({0, 1}, value); } ); - // Tilt down - ctx.controls["look_down_gamepad"]->set_active_callback + + // Move right control + ctx.controls["move_right"]->set_active_callback ( - [&ctx = this->ctx, three_dof_eid, gamepad_tilt_factor](float value) + [&](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); + move_first_person_camera_rig({1, 0}, value); } ); - ctx.controls["look_down_mouse"]->set_active_callback + + // Move left control + ctx.controls["move_left"]->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 * static_cast(ctx.loop.get_update_period()); - three_dof.pitch = std::min(math::radians(90.0f), three_dof.pitch); + move_first_person_camera_rig({-1, 0}, value); } ); - // Setup switch POV control - ctx.controls["switch_pov"]->set_activated_callback + // Action control + ctx.controls["action"]->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(); } ); @@ -631,7 +681,7 @@ void nest_selection::enable_keeper_controls() ( [&ctx = this->ctx, wavelength_speed](float) { - ctx.rgb_wavelengths.x += wavelength_speed* ctx.loop.get_update_period(); + 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; @@ -643,7 +693,7 @@ void nest_selection::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; @@ -654,7 +704,7 @@ void nest_selection::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; @@ -666,7 +716,7 @@ void nest_selection::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; @@ -677,7 +727,7 @@ void nest_selection::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; @@ -686,342 +736,45 @@ void nest_selection::enable_keeper_controls() ); } -void nest_selection::disable_keeper_controls() +void nest_selection::disable_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); + if (mouse_look) + { + mouse_look = false; + ctx.app->set_relative_mouse_mode(false); + } + 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_right_gamepad"]->set_active_callback(nullptr); + ctx.controls["look_left_mouse"]->set_active_callback(nullptr); + ctx.controls["look_left_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_up_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["look_down_gamepad"]->set_active_callback(nullptr); + ctx.controls["move_up"]->set_active_callback(nullptr); + ctx.controls["move_down"]->set_active_callback(nullptr); + ctx.controls["select_mouse"]->set_activated_callback(nullptr); 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["action"]->set_activated_callback(nullptr); ctx.controls["fast_forward"]->set_activated_callback(nullptr); + ctx.controls["fast_forward"]->set_deactivated_callback(nullptr); ctx.controls["rewind"]->set_activated_callback(nullptr); + ctx.controls["rewind"]->set_deactivated_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(); + ctx.controls["increase_exposure"]->set_active_callback(nullptr); + ctx.controls["decrease_exposure"]->set_active_callback(nullptr); + ctx.controls["dec_red"]->set_active_callback(nullptr); + ctx.controls["inc_red"]->set_active_callback(nullptr); + ctx.controls["dec_green"]->set_active_callback(nullptr); + ctx.controls["inc_green"]->set_active_callback(nullptr); + ctx.controls["dec_blue"]->set_active_callback(nullptr); + ctx.controls["inc_blue"]->set_active_callback(nullptr); } } // namespace state diff --git a/src/game/state/nest-selection.hpp b/src/game/state/nest-selection.hpp index 1e52186..50b2f3f 100644 --- a/src/game/state/nest-selection.hpp +++ b/src/game/state/nest-selection.hpp @@ -21,6 +21,8 @@ #define ANTKEEPER_GAME_STATE_NEST_SELECTION_HPP #include "game/state/base.hpp" +#include "entity/id.hpp" +#include "utility/fundamental-types.hpp" namespace game { namespace state { @@ -32,15 +34,33 @@ public: virtual ~nest_selection(); private: - void setup_camera(); + void create_first_person_camera_rig(); + void destroy_first_person_camera_rig(); + + void set_first_person_camera_rig_pedestal(float pedestal); + void move_first_person_camera_rig(const float2& direction, float factor); + void satisfy_first_person_camera_rig_constraints(); - 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 first_person_camera_rig_eid; + entity::id first_person_camera_rig_spring_translation_eid; + entity::id first_person_camera_rig_spring_rotation_eid; + entity::id first_person_camera_rig_fov_spring_eid; + float first_person_camera_rig_translation_spring_angular_frequency; + float first_person_camera_rig_rotation_spring_angular_frequency; + float first_person_camera_rig_fov_spring_angular_frequency; + float first_person_camera_rig_min_elevation; + float first_person_camera_rig_max_elevation; + float first_person_camera_near_fov; + float first_person_camera_far_fov; + float first_person_camera_near_speed; + float first_person_camera_far_speed; + float first_person_camera_rig_pedestal_speed; + float first_person_camera_rig_pedestal; + + bool mouse_look; }; } // namespace state diff --git a/src/game/state/nuptial-flight.cpp b/src/game/state/nuptial-flight.cpp index d2cba09..efb63ac 100644 --- a/src/game/state/nuptial-flight.cpp +++ b/src/game/state/nuptial-flight.cpp @@ -19,6 +19,7 @@ #include "game/state/nuptial-flight.hpp" #include "game/state/pause-menu.hpp" +#include "game/state/nest-selection.hpp" #include "game/ant/swarm.hpp" #include "entity/archetype.hpp" #include "game/system/camera.hpp" @@ -34,6 +35,8 @@ #include "game/component/constraint-stack.hpp" #include "game/component/steering.hpp" #include "game/component/picking.hpp" +#include "game/component/spring.hpp" +#include "math/projection.hpp" #include "game/controls.hpp" #include "entity/commands.hpp" #include "animation/screen-transition.hpp" @@ -83,7 +86,7 @@ nuptial_flight::nuptial_flight(game::context& ctx): } // Load biome - game::load::biome(ctx, "desert-scrub.bio"); + //game::load::biome(ctx, "desert-scrub.bio"); // Set world time game::world::set_time(ctx, 2022, 6, 21, 12, 0, 0.0); @@ -106,6 +109,26 @@ nuptial_flight::nuptial_flight(game::context& ctx): const float ev100_sunny16 = physics::light::ev::from_settings(16.0f, 1.0f / 100.0f, 100.0f); ctx.surface_camera->set_exposure(ev100_sunny16); + const auto& viewport_dimensions = ctx.app->get_viewport_dimensions(); + const float aspect_ratio = static_cast(viewport_dimensions[0]) / static_cast(viewport_dimensions[1]); + + // Init camera rig params + camera_rig_near_distance = 1.0f; + camera_rig_far_distance = 150.0f; + camera_rig_near_fov = math::vertical_fov(math::radians(100.0f), aspect_ratio); + camera_rig_far_fov = math::vertical_fov(math::radians(60.0f), aspect_ratio); + camera_rig_zoom_speed = 4.0f; + camera_rig_translation_spring_angular_frequency = period_to_rads(0.125f); + camera_rig_rotation_spring_angular_frequency = period_to_rads(0.125f); + camera_rig_fov_spring_angular_frequency = period_to_rads(0.125f); + camera_rig_focus_ease_to_duration = 1.0f; + + // Read camera rig settingss + if (ctx.config->contains("near_fov")) + camera_rig_near_fov = math::vertical_fov(math::radians((*ctx.config)["near_fov"].get()), aspect_ratio); + if (ctx.config->contains("far_fov")) + camera_rig_far_fov = math::vertical_fov(math::radians((*ctx.config)["far_fov"].get()), aspect_ratio); + // Create camera rig create_camera_rig(); @@ -150,7 +173,7 @@ void nuptial_flight::create_camera_rig() component::constraint::ease_to camera_rig_focus_ease_to; camera_rig_focus_ease_to.target = selected_eid; camera_rig_focus_ease_to.start = {0, 0, 0}; - camera_rig_focus_ease_to.duration = 1.0f; + camera_rig_focus_ease_to.duration = camera_rig_focus_ease_to_duration; camera_rig_focus_ease_to.t = camera_rig_focus_ease_to.duration; camera_rig_focus_ease_to.function = &ease::out_expo; component::constraint_stack_node camera_rig_focus_ease_to_node; @@ -215,7 +238,7 @@ void nuptial_flight::create_camera_rig() {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}, 1.0f, - hz_to_rads(8.0f) + camera_rig_rotation_spring_angular_frequency }; component::constraint_stack_node camera_rig_spring_rotation_node; camera_rig_spring_rotation_node.active = true; @@ -233,7 +256,7 @@ void nuptial_flight::create_camera_rig() {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}, 1.0f, - hz_to_rads(8.0f) + camera_rig_translation_spring_angular_frequency }; component::constraint_stack_node camera_rig_spring_translation_node; camera_rig_spring_translation_node.active = true; @@ -264,6 +287,25 @@ void nuptial_flight::create_camera_rig() ctx.entity_registry->emplace(camera_rig_eid, camera_rig_transform); ctx.entity_registry->emplace(camera_rig_eid, camera_rig_constraint_stack); + // Construct camera rig fov spring + component::spring1 camera_rig_fov_spring; + camera_rig_fov_spring.spring = + { + 0.0f, + 0.0f, + 0.0f, + 1.0f, + camera_rig_fov_spring_angular_frequency + }; + camera_rig_fov_spring.callback = [&](float fov) + { + ctx.surface_camera->set_perspective(fov, ctx.surface_camera->get_aspect_ratio(), ctx.surface_camera->get_clip_near(), ctx.surface_camera->get_clip_far()); + }; + + // Construct camera rig fov spring entity + camera_rig_fov_spring_eid = ctx.entity_registry->create(); + ctx.entity_registry->emplace(camera_rig_fov_spring_eid, camera_rig_fov_spring); + set_camera_rig_zoom(0.25f); } @@ -277,16 +319,15 @@ void nuptial_flight::destroy_camera_rig() ctx.entity_registry->destroy(camera_rig_focus_eid); ctx.entity_registry->destroy(camera_rig_focus_ease_to_eid); + + ctx.entity_registry->destroy(camera_rig_fov_spring_eid); } void nuptial_flight::set_camera_rig_zoom(float zoom) { - const float near_distance = 1.0f; - const float far_distance = 50.0f; - camera_rig_zoom = zoom; - const float distance = math::log_lerp(far_distance, near_distance, camera_rig_zoom); + const float distance = math::log_lerp(camera_rig_far_distance, camera_rig_near_distance, camera_rig_zoom); ctx.entity_registry->patch ( camera_rig_spring_translation_eid, @@ -295,11 +336,21 @@ void nuptial_flight::set_camera_rig_zoom(float zoom) component.spring.x1[2] = distance; } ); + + const float fov = math::log_lerp(camera_rig_far_fov, camera_rig_near_fov, camera_rig_zoom); + ctx.entity_registry->patch + ( + camera_rig_fov_spring_eid, + [&](auto& component) + { + component.spring.x1 = fov; + } + ); } void nuptial_flight::satisfy_camera_rig_constraints() { - // Warp camera rig focus ease to + // Satisfy camera rig focus ease to constraint ctx.entity_registry->patch ( camera_rig_focus_ease_to_eid, @@ -309,7 +360,7 @@ void nuptial_flight::satisfy_camera_rig_constraints() } ); - // Warp camera rig spring translation + // Satisfy camera rig spring translation constraint ctx.entity_registry->patch ( camera_rig_spring_translation_eid, @@ -320,7 +371,7 @@ void nuptial_flight::satisfy_camera_rig_constraints() } ); - // Warp camera rig spring rotation + // Satisfy camera rig spring rotation constraint ctx.entity_registry->patch ( camera_rig_spring_rotation_eid, @@ -330,6 +381,17 @@ void nuptial_flight::satisfy_camera_rig_constraints() component.spring.v *= 0.0f; } ); + + // Satisfycamera rig fov spring + ctx.entity_registry->patch + ( + camera_rig_fov_spring_eid, + [&](auto& component) + { + component.spring.x0 = component.spring.x1; + component.spring.v *= 0.0f; + } + ); } void nuptial_flight::enable_controls() @@ -339,7 +401,6 @@ void nuptial_flight::enable_controls() double time_scale = 0.0; double ff_time_scale = 60.0 * 200.0; - const float dolly_zoom_speed = 4.0f; // Init control settings float mouse_tilt_sensitivity = 1.0f; @@ -414,7 +475,7 @@ void nuptial_flight::enable_controls() ctx.entity_registry->patch ( camera_rig_spring_rotation_eid, - [&](auto& component) + [&, mouse_pan_factor](auto& component) { component.spring.x1[0] -= mouse_pan_factor * value; } @@ -428,7 +489,7 @@ void nuptial_flight::enable_controls() ctx.entity_registry->patch ( camera_rig_spring_rotation_eid, - [&](auto& component) + [&, gamepad_pan_factor](auto& component) { component.spring.x1[0] -= gamepad_pan_factor * value * static_cast(ctx.loop.get_update_period()); } @@ -447,7 +508,7 @@ void nuptial_flight::enable_controls() ctx.entity_registry->patch ( camera_rig_spring_rotation_eid, - [&](auto& component) + [&, mouse_pan_factor](auto& component) { component.spring.x1[0] += mouse_pan_factor * value; } @@ -461,7 +522,7 @@ void nuptial_flight::enable_controls() ctx.entity_registry->patch ( camera_rig_spring_rotation_eid, - [&](auto& component) + [&, gamepad_pan_factor](auto& component) { component.spring.x1[0] += gamepad_pan_factor * value * static_cast(ctx.loop.get_update_period()); } @@ -480,7 +541,7 @@ void nuptial_flight::enable_controls() ctx.entity_registry->patch ( camera_rig_spring_rotation_eid, - [&](auto& component) + [&, mouse_tilt_factor](auto& component) { component.spring.x1[1] -= mouse_tilt_factor * value; component.spring.x1[1] = std::max(-math::half_pi, component.spring.x1[1]); @@ -495,7 +556,7 @@ void nuptial_flight::enable_controls() ctx.entity_registry->patch ( camera_rig_spring_rotation_eid, - [&](auto& component) + [&, gamepad_tilt_factor](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]); @@ -515,7 +576,7 @@ void nuptial_flight::enable_controls() ctx.entity_registry->patch ( camera_rig_spring_rotation_eid, - [&](auto& component) + [&, mouse_tilt_factor](auto& component) { component.spring.x1[1] += mouse_tilt_factor * value; component.spring.x1[1] = std::min(math::half_pi, component.spring.x1[1]); @@ -530,7 +591,7 @@ void nuptial_flight::enable_controls() ctx.entity_registry->patch ( camera_rig_spring_rotation_eid, - [&](auto& component) + [&, gamepad_tilt_factor](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]); @@ -542,18 +603,18 @@ void nuptial_flight::enable_controls() // Dolly in control ctx.controls["move_up"]->set_active_callback ( - [&, dolly_zoom_speed](float value) + [&](float value) { - set_camera_rig_zoom(std::min(1.0f, camera_rig_zoom + dolly_zoom_speed * static_cast(ctx.loop.get_update_period()))); + set_camera_rig_zoom(std::min(1.0f, camera_rig_zoom + camera_rig_zoom_speed * static_cast(ctx.loop.get_update_period()))); } ); // Dolly out control ctx.controls["move_down"]->set_active_callback ( - [&, dolly_zoom_speed](float value) + [&](float value) { - set_camera_rig_zoom(std::max(0.0f, camera_rig_zoom - dolly_zoom_speed * static_cast(ctx.loop.get_update_period()))); + set_camera_rig_zoom(std::max(0.0f, camera_rig_zoom - camera_rig_zoom_speed * static_cast(ctx.loop.get_update_period()))); } ); @@ -628,7 +689,12 @@ void nuptial_flight::enable_controls() ( [&]() { + // Disable controls + disable_controls(); + // Change to nest selection state + ctx.state_machine.pop(); + ctx.state_machine.emplace(new game::state::nest_selection(ctx)); } ); @@ -781,30 +847,37 @@ void nuptial_flight::disable_controls() ctx.app->set_relative_mouse_mode(false); } - ctx.controls["move_forward"]->set_activated_callback(nullptr); - ctx.controls["move_back"]->set_activated_callback(nullptr); - ctx.controls["move_right"]->set_activated_callback(nullptr); - ctx.controls["move_left"]->set_activated_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_right_gamepad"]->set_active_callback(nullptr); + ctx.controls["look_left_mouse"]->set_active_callback(nullptr); + ctx.controls["look_left_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_up_gamepad"]->set_active_callback(nullptr); ctx.controls["look_down_mouse"]->set_active_callback(nullptr); + ctx.controls["look_down_gamepad"]->set_active_callback(nullptr); + ctx.controls["move_up"]->set_active_callback(nullptr); + ctx.controls["move_down"]->set_active_callback(nullptr); ctx.controls["select_mouse"]->set_activated_callback(nullptr); + ctx.controls["move_forward"]->set_activated_callback(nullptr); + ctx.controls["move_back"]->set_activated_callback(nullptr); + ctx.controls["move_right"]->set_activated_callback(nullptr); + ctx.controls["move_left"]->set_activated_callback(nullptr); ctx.controls["action"]->set_activated_callback(nullptr); - ctx.controls["switch_pov"]->set_activated_callback(nullptr); ctx.controls["fast_forward"]->set_activated_callback(nullptr); + ctx.controls["fast_forward"]->set_deactivated_callback(nullptr); ctx.controls["rewind"]->set_activated_callback(nullptr); + ctx.controls["rewind"]->set_deactivated_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); + ctx.controls["increase_exposure"]->set_active_callback(nullptr); + ctx.controls["decrease_exposure"]->set_active_callback(nullptr); + ctx.controls["dec_red"]->set_active_callback(nullptr); + ctx.controls["inc_red"]->set_active_callback(nullptr); + ctx.controls["dec_green"]->set_active_callback(nullptr); + ctx.controls["inc_green"]->set_active_callback(nullptr); + ctx.controls["dec_blue"]->set_active_callback(nullptr); + ctx.controls["inc_blue"]->set_active_callback(nullptr); } void nuptial_flight::select_entity(entity::id entity_id) diff --git a/src/game/state/nuptial-flight.hpp b/src/game/state/nuptial-flight.hpp index 489081b..c858226 100644 --- a/src/game/state/nuptial-flight.hpp +++ b/src/game/state/nuptial-flight.hpp @@ -54,9 +54,21 @@ private: entity::id camera_rig_spring_rotation_eid; entity::id camera_rig_copy_translation_eid; entity::id camera_rig_pivot_eid; - + float camera_rig_near_distance; + float camera_rig_far_distance; + float camera_rig_near_fov; + float camera_rig_far_fov; + float camera_rig_zoom_speed; float camera_rig_zoom; + entity::id camera_rig_fov_spring_eid; + + float camera_rig_translation_spring_angular_frequency; + float camera_rig_rotation_spring_angular_frequency; + float camera_rig_fov_spring_angular_frequency; + float camera_rig_focus_ease_to_duration; + + entity::id swarm_eid; std::uint32_t selected_picking_flag; diff --git a/src/game/state/splash.cpp b/src/game/state/splash.cpp index ad25328..616c41d 100644 --- a/src/game/state/splash.cpp +++ b/src/game/state/splash.cpp @@ -19,7 +19,6 @@ #include "game/state/splash.hpp" #include "game/state/main-menu.hpp" -#include "game/state/nuptial-flight.hpp" #include "animation/screen-transition.hpp" #include "animation/animation.hpp" #include "animation/animator.hpp" @@ -118,7 +117,6 @@ splash::splash(game::context& ctx): { ctx.state_machine.pop(); ctx.state_machine.emplace(new game::state::main_menu(ctx, true)); - //ctx.state_machine.emplace(new game::state::nuptial_flight(ctx)); } ); } @@ -147,7 +145,6 @@ splash::splash(game::context& ctx): // Change to main menu state ctx.state_machine.pop(); ctx.state_machine.emplace(new game::state::main_menu(ctx, true)); - //ctx.state_machine.emplace(new game::state::nuptial_flight(ctx)); } } ); diff --git a/src/game/system/constraint.cpp b/src/game/system/constraint.cpp index 50601e9..cbeb3a2 100644 --- a/src/game/system/constraint.cpp +++ b/src/game/system/constraint.cpp @@ -17,7 +17,7 @@ * along with Antkeeper source code. If not, see . */ -#include "constraint.hpp" +#include "game/system/constraint.hpp" #include "game/component/constraint-stack.hpp" namespace game { diff --git a/src/game/system/spring.cpp b/src/game/system/spring.cpp new file mode 100644 index 0000000..381d158 --- /dev/null +++ b/src/game/system/spring.cpp @@ -0,0 +1,80 @@ +/* + * 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/system/spring.hpp" +#include "game/component/spring.hpp" +#include "entity/id.hpp" + +namespace game { +namespace system { + +spring::spring(entity::registry& registry): + updatable(registry) +{} + +spring::~spring() +{} + +void spring::update(double t, double dt) +{ + const float dtf = static_cast(dt); + + registry.view().each + ( + [&](entity::id spring_eid, auto& component) + { + solve_numeric_spring(component.spring, dtf); + if (component.callback) + component.callback(component.spring.x0); + } + ); + + registry.view().each + ( + [&](entity::id spring_eid, auto& component) + { + solve_numeric_spring(component.spring, dtf); + if (component.callback) + component.callback(component.spring.x0); + } + ); + + registry.view().each + ( + [&](entity::id spring_eid, auto& component) + { + solve_numeric_spring(component.spring, dtf); + if (component.callback) + component.callback(component.spring.x0); + } + ); + + registry.view().each + ( + [&](entity::id spring_eid, auto& component) + { + solve_numeric_spring(component.spring, dtf); + if (component.callback) + component.callback(component.spring.x0); + } + ); +} + +} // namespace system +} // namespace game diff --git a/src/game/system/spring.hpp b/src/game/system/spring.hpp new file mode 100644 index 0000000..60ecdd5 --- /dev/null +++ b/src/game/system/spring.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_SYSTEM_SPRING_HPP +#define ANTKEEPER_GAME_SYSTEM_SPRING_HPP + +#include "game/system/updatable.hpp" + +namespace game { +namespace system { + +/** + * Solves numeric springs. + */ +class spring: + public updatable +{ +public: + spring(entity::registry& registry); + ~spring(); + + virtual void update(double t, double dt); +}; + +} // namespace system +} // namespace game + +#endif // ANTKEEPER_GAME_SYSTEM_SPRING_HPP diff --git a/src/geom/marching-cubes.cpp b/src/geom/marching-cubes.cpp index 2d9805d..3220139 100644 --- a/src/geom/marching-cubes.cpp +++ b/src/geom/marching-cubes.cpp @@ -41,297 +41,297 @@ static constexpr std::uint_fast8_t vertex_table[12][2] = static constexpr std::uint_fast16_t edge_table[256] = { - 0x000, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, - 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00, - 0x190, 0x099, 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, - 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90, - 0x230, 0x339, 0x033, 0x13a, 0x636, 0x73f, 0x435, 0x53c, - 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30, - 0x3a0, 0x2a9, 0x1a3, 0x0aa, 0x7a6, 0x6af, 0x5a5, 0x4ac, - 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0, - 0x460, 0x569, 0x663, 0x76a, 0x066, 0x16f, 0x265, 0x36c, - 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60, - 0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0x0ff, 0x3f5, 0x2fc, - 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0, - 0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x055, 0x15c, - 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950, - 0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0x0cc, - 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0, - 0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, - 0x0cc, 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0, - 0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, - 0x15c, 0x055, 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650, - 0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, - 0x2fc, 0x3f5, 0x0ff, 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0, - 0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, - 0x36c, 0x265, 0x16f, 0x066, 0x76a, 0x663, 0x569, 0x460, - 0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, - 0x4ac, 0x5a5, 0x6af, 0x7a6, 0x0aa, 0x1a3, 0x2a9, 0x3a0, - 0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, - 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x033, 0x339, 0x230, - 0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, - 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x099, 0x190, - 0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, - 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x000 + 0x000, 0x109, 0x203, 0x30A, 0x406, 0x50F, 0x605, 0x70C, + 0x80C, 0x905, 0xA0F, 0xB06, 0xC0A, 0xD03, 0xE09, 0xF00, + 0x190, 0x099, 0x393, 0x29A, 0x596, 0x49F, 0x795, 0x69C, + 0x99C, 0x895, 0xB9F, 0xA96, 0xD9A, 0xC93, 0xF99, 0xE90, + 0x230, 0x339, 0x033, 0x13A, 0x636, 0x73F, 0x435, 0x53C, + 0xA3C, 0xB35, 0x83F, 0x936, 0xE3A, 0xF33, 0xC39, 0xD30, + 0x3A0, 0x2A9, 0x1A3, 0x0AA, 0x7A6, 0x6AF, 0x5A5, 0x4AC, + 0xBAC, 0xAA5, 0x9AF, 0x8A6, 0xFAA, 0xEA3, 0xDA9, 0xCA0, + 0x460, 0x569, 0x663, 0x76A, 0x066, 0x16F, 0x265, 0x36C, + 0xC6C, 0xD65, 0xE6F, 0xF66, 0x86A, 0x963, 0xA69, 0xB60, + 0x5F0, 0x4F9, 0x7F3, 0x6FA, 0x1F6, 0x0FF, 0x3F5, 0x2FC, + 0xDFC, 0xCF5, 0xFFF, 0xEF6, 0x9FA, 0x8F3, 0xBF9, 0xAF0, + 0x650, 0x759, 0x453, 0x55A, 0x256, 0x35F, 0x055, 0x15C, + 0xE5C, 0xF55, 0xC5F, 0xD56, 0xA5A, 0xB53, 0x859, 0x950, + 0x7C0, 0x6C9, 0x5C3, 0x4CA, 0x3C6, 0x2CF, 0x1C5, 0x0CC, + 0xFCC, 0xEC5, 0xDCF, 0xCC6, 0xBCA, 0xAC3, 0x9C9, 0x8C0, + 0x8C0, 0x9C9, 0xAC3, 0xBCA, 0xCC6, 0xDCF, 0xEC5, 0xFCC, + 0x0CC, 0x1C5, 0x2CF, 0x3C6, 0x4CA, 0x5C3, 0x6C9, 0x7C0, + 0x950, 0x859, 0xB53, 0xA5A, 0xD56, 0xC5F, 0xF55, 0xE5C, + 0x15C, 0x055, 0x35F, 0x256, 0x55A, 0x453, 0x759, 0x650, + 0xAF0, 0xBF9, 0x8F3, 0x9FA, 0xEF6, 0xFFF, 0xCF5, 0xDFC, + 0x2FC, 0x3F5, 0x0FF, 0x1F6, 0x6FA, 0x7F3, 0x4F9, 0x5F0, + 0xB60, 0xA69, 0x963, 0x86A, 0xF66, 0xE6F, 0xD65, 0xC6C, + 0x36C, 0x265, 0x16F, 0x066, 0x76A, 0x663, 0x569, 0x460, + 0xCA0, 0xDA9, 0xEA3, 0xFAA, 0x8A6, 0x9AF, 0xAA5, 0xBAC, + 0x4AC, 0x5A5, 0x6AF, 0x7A6, 0x0AA, 0x1A3, 0x2A9, 0x3A0, + 0xD30, 0xC39, 0xF33, 0xE3A, 0x936, 0x83F, 0xB35, 0xA3C, + 0x53C, 0x435, 0x73F, 0x636, 0x13A, 0x033, 0x339, 0x230, + 0xE90, 0xF99, 0xC93, 0xD9A, 0xA96, 0xB9F, 0x895, 0x99C, + 0x69C, 0x795, 0x49F, 0x596, 0x29A, 0x393, 0x099, 0x190, + 0xF00, 0xE09, 0xD03, 0xC0A, 0xB06, 0xA0F, 0x905, 0x80C, + 0x70C, 0x605, 0x50F, 0x406, 0x30A, 0x203, 0x109, 0x000 }; static constexpr std::int_fast8_t triangle_table[256][16] = -{ +{ {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1}, - {3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1}, - {3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1}, - {3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1}, - {9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1}, - {1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1}, - {9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1}, - {2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1}, - {8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1}, - {9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1}, - {4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1}, - {3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1}, - {1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1}, - {4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1}, - {4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1}, - {9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1}, - {1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1}, - {5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1}, - {2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1}, - {9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1}, - {0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1}, - {2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1}, - {10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1}, - {4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1}, - {5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1}, - {5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1}, - {9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1}, - {0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1}, - {1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1}, - {10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1}, - {8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1}, - {2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1}, - {7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1}, - {9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1}, - {2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1}, - {11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1}, - {9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1}, - {5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1}, - {11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1}, - {11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1}, - {1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1}, - {9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1}, - {5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1}, - {2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1}, - {0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1}, - {5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1}, - {6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1}, - {0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1}, - {3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1}, - {6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1}, - {5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1}, - {1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1}, - {10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1}, - {6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1}, - {1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1}, - {8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1}, - {7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1}, - {3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1}, - {5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1}, - {0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1}, - {9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1}, - {8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1}, - {5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1}, - {0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1}, - {6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1}, - {10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1}, - {10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1}, - {8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1}, - {1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1}, - {3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1}, - {0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1}, - {10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1}, - {0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1}, - {3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1}, - {6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1}, - {9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1}, - {8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1}, - {3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1}, - {6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1}, - {0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1}, - {10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1}, - {10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1}, - {1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1}, - {2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1}, - {7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1}, - {7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1}, - {2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1}, - {1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1}, - {11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1}, - {8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1}, - {0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1}, - {7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1}, - {10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1}, - {2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1}, - {6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1}, - {7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1}, - {2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1}, - {1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1}, - {10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1}, - {10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1}, - {0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1}, - {7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1}, - {6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1}, - {8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1}, - {9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1}, - {6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1}, - {1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1}, - {4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1}, - {10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1}, - {8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1}, - {0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1}, - {1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1}, - {8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1}, - {10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1}, - {4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1}, - {10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1}, - {5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1}, - {11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1}, - {9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1}, - {6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1}, - {7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1}, - {3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1}, - {7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1}, - {9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1}, - {3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1}, - {6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1}, - {9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1}, - {1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1}, - {4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1}, - {7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1}, - {6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1}, - {3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1}, - {0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1}, - {6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1}, - {1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1}, - {0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1}, - {11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1}, - {6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1}, - {5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1}, - {9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1}, - {1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1}, - {1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1}, - {10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1}, - {0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1}, - {5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1}, - {10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1}, - {11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1}, - {0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1}, - {9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1}, - {7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1}, - {2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1}, - {8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1}, - {9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1}, - {9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1}, - {1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1}, - {9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1}, - {9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1}, - {5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1}, - {0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1}, - {10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1}, - {2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1}, - {0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1}, - {0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1}, - {9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1}, - {5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1}, - {3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1}, - {5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1}, - {8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1}, - {0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1}, - {9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1}, - {0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1}, - {1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1}, - {3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1}, - {4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1}, - {9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1}, - {11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1}, - {11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1}, - {2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1}, - {9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1}, - {3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1}, - {1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1}, - {4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1}, - {4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1}, - {0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1}, - {3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1}, - {3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1}, - {0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1}, - {9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1}, - {1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - {0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1}, + { 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1}, + { 3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1}, + { 3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1}, + { 9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1}, + { 9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1}, + { 2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1}, + { 8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1}, + { 9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1}, + { 4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1}, + { 3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1}, + { 1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1}, + { 4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1}, + { 4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1}, + { 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1}, + { 1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1}, + { 5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1}, + { 2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1}, + { 9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1}, + { 0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1}, + { 2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1}, + {10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1}, + { 4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1}, + { 5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1}, + { 5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1}, + { 9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1}, + { 0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1}, + { 1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1}, + {10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1}, + { 8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1}, + { 2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1}, + { 7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1}, + { 9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1}, + { 2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1}, + {11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1}, + { 9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1}, + { 5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1}, + {11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1}, + {11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1}, + { 1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1}, + { 9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1}, + { 5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1}, + { 2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1}, + { 0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1}, + { 5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1}, + { 6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1}, + { 0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1}, + { 3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1}, + { 6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1}, + { 5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1}, + { 1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1}, + {10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1}, + { 6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1}, + { 1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1}, + { 8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1}, + { 7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1}, + { 3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1}, + { 5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1}, + { 0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1}, + { 9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1}, + { 8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1}, + { 5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1}, + { 0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1}, + { 6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1}, + {10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1}, + {10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1}, + { 8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1}, + { 1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1}, + { 3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1}, + { 0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1}, + {10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1}, + { 0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1}, + { 3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1}, + { 6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1}, + { 9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1}, + { 8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1}, + { 3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1}, + { 6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1}, + { 0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1}, + {10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1}, + {10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1}, + { 1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1}, + { 2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1}, + { 7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1}, + { 7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1}, + { 2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1}, + { 1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1}, + {11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1}, + { 8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1}, + { 0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1}, + { 7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1}, + {10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1}, + { 2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1}, + { 6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1}, + { 7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1}, + { 2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1}, + { 1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1}, + {10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1}, + {10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1}, + { 0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1}, + { 7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1}, + { 6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1}, + { 8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1}, + { 9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1}, + { 6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1}, + { 4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1}, + {10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1}, + { 8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1}, + { 0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1}, + { 1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1}, + { 8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1}, + {10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1}, + { 4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1}, + {10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1}, + { 5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1}, + {11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1}, + { 9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1}, + { 6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1}, + { 7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1}, + { 3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1}, + { 7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1}, + { 9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1}, + { 3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1}, + { 6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1}, + { 9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1}, + { 1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1}, + { 4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1}, + { 7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1}, + { 6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1}, + { 3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1}, + { 0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1}, + { 6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1}, + { 0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1}, + {11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1}, + { 6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1}, + { 5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1}, + { 9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1}, + { 1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1}, + { 1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1}, + {10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1}, + { 0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1}, + { 5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1}, + {10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1}, + {11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1}, + { 9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1}, + { 7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1}, + { 2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1}, + { 8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1}, + { 9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1}, + { 9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1}, + { 1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1}, + { 9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1}, + { 9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1}, + { 5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1}, + { 0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1}, + {10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1}, + { 2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1}, + { 0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1}, + { 0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1}, + { 9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1}, + { 5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1}, + { 3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1}, + { 5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1}, + { 8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1}, + { 9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1}, + { 0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1}, + { 1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1}, + { 3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1}, + { 4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1}, + { 9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1}, + {11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1}, + {11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1}, + { 2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1}, + { 9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1}, + { 3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1}, + { 1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1}, + { 4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1}, + { 4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1}, + { 0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1}, + { 3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1}, + { 3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1}, + { 0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1}, + { 9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1}, + { 1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1} }; diff --git a/src/geom/marching-cubes.hpp b/src/geom/marching-cubes.hpp index 46a216d..7d7adf1 100644 --- a/src/geom/marching-cubes.hpp +++ b/src/geom/marching-cubes.hpp @@ -56,4 +56,3 @@ constexpr float unit_cube[8][3] = } // namespace geom #endif // ANTKEEPER_GEOM_MARCHING_CUBES_HPP - diff --git a/src/geom/mesh-accelerator.hpp b/src/geom/mesh-accelerator.hpp index da7556f..a28cc14 100644 --- a/src/geom/mesh-accelerator.hpp +++ b/src/geom/mesh-accelerator.hpp @@ -54,7 +54,7 @@ public: * Finds the first intersection between a ray and a triangle in the mesh. * * @param ray Ray to test for intersection. - * @return Ray query result on intersection, and `std::nullopt` if no intersection occurreda. + * @return Ray query result on intersection, and `std::nullopt` if no intersection occurred. */ std::optional query_nearest(const ray& ray) const; diff --git a/src/math/math.hpp b/src/math/math.hpp index 2c7a45c..bd94e42 100644 --- a/src/math/math.hpp +++ b/src/math/math.hpp @@ -48,5 +48,6 @@ namespace math {} #include "math/interpolation.hpp" #include "math/random.hpp" #include "math/map.hpp" +#include "math/projection.hpp" #endif // ANTKEEPER_MATH_HPP diff --git a/src/math/projection.hpp b/src/math/projection.hpp new file mode 100644 index 0000000..a72ab69 --- /dev/null +++ b/src/math/projection.hpp @@ -0,0 +1,61 @@ +/* + * 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_MATH_PROJECTION_HPP +#define ANTKEEPER_MATH_PROJECTION_HPP + +#include + +namespace math { + +/** + * Calculates a horizontal FoV given a vertical FoV and aspect ratio. + * + * @param v Vertical FoV, in radians. + * @param r Ratio of width to height. + * + * @return Horizontal FoV, in radians. + * + * @see https://en.wikipedia.org/wiki/Field_of_view_in_video_games + */ +template +T horizontal_fov(T v, T r) +{ + return T{2} * std::atan(std::tan(v * T{0.5}) * r); +} + +/** + * Calculates a vertical FoV given a horizontal FoV and aspect ratio. + * + * @param h Horizontal FoV, in radians. + * @param r Ratio of width to height. + * + * @return Vertical FoV, in radians. + * + * @see https://en.wikipedia.org/wiki/Field_of_view_in_video_games + */ +template +T vertical_fov(T h, T r) +{ + return T{2} * std::atan(std::tan(h * T{0.5}) / r); +} + +} // namespace math + +#endif // ANTKEEPER_MATH_PROJECTION_HPP