diff --git a/CMakeLists.txt b/CMakeLists.txt index baf682a..03b2f5a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,6 @@ cmake_minimum_required(VERSION 3.25) + option(APPLICATION_NAME "Application name" "Antkeeper") option(APPLICATION_VERSION "Application version string" "0.0.0") option(APPLICATION_AUTHOR "Application author" "C. J. Howard") @@ -41,15 +42,15 @@ set(SHARED_LIBS ${OPENGL_gl_LIBRARY}) # Generate configuration header file -configure_file(${PROJECT_SOURCE_DIR}/src/config.hpp.in - ${PROJECT_BINARY_DIR}/src/config.hpp) +configure_file(${PROJECT_SOURCE_DIR}/src/engine/config.hpp.in + ${PROJECT_BINARY_DIR}/src/engine/config.hpp) # Collect source files file(GLOB_RECURSE SOURCE_FILES ${PROJECT_SOURCE_DIR}/src/*.cpp) # Remove platform-specific source files -set(EXCLUDE_DIR "${PROJECT_SOURCE_DIR}/src/platform/") +set(EXCLUDE_DIR "${PROJECT_SOURCE_DIR}/src/game/platform/") foreach(TMP_PATH ${SOURCE_FILES}) string(FIND ${TMP_PATH} ${EXCLUDE_DIR} EXCLUDE_DIR_FOUND) if (NOT ${EXCLUDE_DIR_FOUND} EQUAL -1) @@ -59,21 +60,21 @@ endforeach(TMP_PATH) if(MSVC) # Add platform-specific source files - list(APPEND SOURCE_FILES "${PROJECT_SOURCE_DIR}/src/platform/windows/nvidia.cpp") + list(APPEND SOURCE_FILES "${PROJECT_SOURCE_DIR}/src/game/platform/windows/nvidia.cpp") # Generate Windows icon resource file set(ICON_FILE "${PROJECT_SOURCE_DIR}/../antkeeper-data/src/icons/antkeeper.ico") if(EXISTS "${ICON_FILE}") - configure_file(${PROJECT_SOURCE_DIR}/src/platform/windows/icon.rc.in ${PROJECT_BINARY_DIR}/src/platform/windows/icon.rc) - list(APPEND SOURCE_FILES "${PROJECT_BINARY_DIR}/src/platform/windows/icon.rc") + configure_file(${PROJECT_SOURCE_DIR}/src/game/platform/windows/icon.rc.in ${PROJECT_BINARY_DIR}/src/game/platform/windows/icon.rc) + list(APPEND SOURCE_FILES "${PROJECT_BINARY_DIR}/src/game/platform/windows/icon.rc") endif() # Generate Windows version-information resource file - configure_file(${PROJECT_SOURCE_DIR}/src/platform/windows/version.rc.in ${PROJECT_BINARY_DIR}/src/platform/windows/version.rc) - list(APPEND SOURCE_FILES "${PROJECT_BINARY_DIR}/src/platform/windows/version.rc") + configure_file(${PROJECT_SOURCE_DIR}/src/game/platform/windows/version.rc.in ${PROJECT_BINARY_DIR}/src/game/platform/windows/version.rc) + list(APPEND SOURCE_FILES "${PROJECT_BINARY_DIR}/src/game/platform/windows/version.rc") # Make executable DPI-aware on Windows - list(APPEND SOURCE_FILES "${PROJECT_SOURCE_DIR}/src/platform/windows/dpi-aware.manifest") + list(APPEND SOURCE_FILES "${PROJECT_SOURCE_DIR}/src/game/platform/windows/dpi-aware.manifest") endif() # Add executable target diff --git a/src/ai/ai.hpp b/src/ai/ai.hpp deleted file mode 100644 index c7cacb7..0000000 --- a/src/ai/ai.hpp +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_AI_HPP -#define ANTKEEPER_AI_HPP - -/// Artificial intelligence (AI) -namespace ai {} - -#include "bt/bt.hpp" -#include "steering/steering.hpp" - -#endif // ANTKEEPER_AI_HPP diff --git a/src/ai/bt/bt.hpp b/src/ai/bt/bt.hpp deleted file mode 100644 index 3a79b33..0000000 --- a/src/ai/bt/bt.hpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_AI_BT_HPP -#define ANTKEEPER_AI_BT_HPP - -#include -#include - -namespace ai { - -/// Behavior tree (BT) -namespace bt {} - -} // namespace ai - -#include "ai/bt/node.hpp" -#include "ai/bt/status.hpp" - -#endif // ANTKEEPER_AI_BT_HPP diff --git a/src/ai/bt/node.hpp b/src/ai/bt/node.hpp deleted file mode 100644 index 6931778..0000000 --- a/src/ai/bt/node.hpp +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_AI_BT_NODE_HPP -#define ANTKEEPER_AI_BT_NODE_HPP - -#include "ai/bt/status.hpp" - -namespace ai { -namespace bt { - -/** - * Abstract base class for behavior tree nodes. - * - * @tparam T Data type on which nodes operate. - */ -template -struct node -{ - /// Data type on which nodes operate. - typedef T context_type; - - /** - * Executes a node's function and returns its status. - * - * @param context Context data on which the node will operate. - */ - virtual status execute(context_type& context) const = 0; -}; - -/// Behavior tree node with no children. -template -using leaf_node = node; - - -/// A node with exactly one child. -template -struct decorator_node: public node -{ - node* child; -}; - -/// A node that can have one or more children. -template -struct composite_node: public node -{ - std::list*> children; -}; - -/// Executes a function on a context and returns the status. -template -struct action: public leaf_node -{ - virtual status execute(node::context_type& context) const final; - typedef std::function::context_type&)> function_type; - function_type function; -}; - -/// Evaluates a boolean condition (predicate) and returns either `status::success` or `status::failure`. -template -struct condition: public leaf_node -{ - virtual status execute(node::context_type& context) const final; - typedef std::function::context_type&)> predicate_type; - predicate_type predicate; -}; - -/// Executes a child node and returns its inverted status. If the child returns `status::success`, then `status::failure` will be returned. Otherwise if the child returns `status::failure`, then `status::success` will be returned. -template -struct inverter: public decorator_node -{ - virtual status execute(node::context_type& context) const final; -}; - -/// Attempts to execute a child node `n` times or until the child fails. -template -struct repeater: public decorator_node -{ - virtual status execute(node::context_type& context) const final; - int n; -}; - -/// Executes a child node and returns `status::success` regardless of the child node status. -template -struct succeeder: public decorator_node -{ - virtual status execute(node::context_type& context) const final; -}; - -/// Attempts to execute each child node sequentially until one fails. If all children are executed successfully, `status::success` will be returned. Otherwise if any children fail, `status::failure` will be returned. -template -struct sequence: public composite_node -{ - virtual status execute(node::context_type& context) const final; -}; - -/// Attempts to execute each child node sequentially until one succeeds. If a child succeeds, `status::success` will be returned. Otherwise if all children fail, `status::failure` will be returned. -template -struct selector: public composite_node -{ - virtual status execute(node::context_type& context) const final; -}; - -template -status action::execute(node::context_type& context) const -{ - return function(context); -} - -template -status condition::execute(node::context_type& context) const -{ - return (predicate(context)) ? status::success : status::failure; -} - -template -status inverter::execute(node::context_type& context) const -{ - status child_status = decorator_node::child->execute(context); - return (child_status == status::success) ? status::failure : (child_status == status::failure) ? status::success : child_status; -} - -template -status repeater::execute(node::context_type& context) const -{ - status child_status; - for (int i = 0; i < n; ++i) - { - child_status = decorator_node::child->execute(context); - if (child_status == status::failure) - break; - } - return child_status; -} - -template -status succeeder::execute(node::context_type& context) const -{ - decorator_node::child->execute(context); - return status::success; -} - -template -status sequence::execute(node::context_type& context) const -{ - for (const node* child: composite_node::children) - { - status child_status = child->execute(context); - if (child_status != status::success) - return child_status; - } - return status::success; -} - -template -status selector::execute(node::context_type& context) const -{ - for (const node* child: composite_node::children) - { - status child_status = child->execute(context); - if (child_status != status::failure) - return child_status; - } - return status::failure; -} - -} // namespace bt -} // namespace ai - -#endif // ANTKEEPER_AI_BT_NODE_HPP diff --git a/src/ai/steering/agent.hpp b/src/ai/steering/agent.hpp deleted file mode 100644 index 5c4242d..0000000 --- a/src/ai/steering/agent.hpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_AI_STEERING_AGENT_HPP -#define ANTKEEPER_AI_STEERING_AGENT_HPP - -#include "utility/fundamental-types.hpp" -#include "math/quaternion.hpp" - -namespace ai { -namespace steering { - -/** - * Autonomous agent governed by steering behaviors. - */ -struct agent -{ - /// Mass of the agent. - float mass; - - /// Cartesian position vector. - float3 position; - - /// Velocity vector. - float3 velocity; - - /// Acceleration vector. - float3 acceleration; - - /// Maximum force. - float max_force; - - /// Maximum speed. - float max_speed; - - /// Maximum speed squared. - float max_speed_squared; - - /// Orientation quaternion. - math::quaternion orientation; - - /// Orthonormal basis forward direction vector. - float3 forward; - - /// Orthonormal basis up direction vector. - float3 up; -}; - -} // namespace steering -} // namespace ai - -#endif // ANTKEEPER_AI_STEERING_AGENT_HPP diff --git a/src/ai/steering/behavior/flee.cpp b/src/ai/steering/behavior/flee.cpp deleted file mode 100644 index d3ab6ec..0000000 --- a/src/ai/steering/behavior/flee.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "ai/steering/behavior/flee.hpp" - -namespace ai { -namespace steering { -namespace behavior { - -float3 flee(const agent& agent, const float3& target) -{ - float3 force = {0, 0, 0}; - const float3 difference = target - agent.position; - const float sqr_distance = math::dot(difference, difference); - - if (sqr_distance) - { - const float inverse_distance = 1.0f / std::sqrt(sqr_distance); - force = difference * inverse_distance * agent.max_force; - force = agent.velocity - force; - } - - return force; -} - -} // namespace behavior -} // namespace steering -} // namespace ai diff --git a/src/ai/steering/behavior/flee.hpp b/src/ai/steering/behavior/flee.hpp deleted file mode 100644 index 9e3a3ba..0000000 --- a/src/ai/steering/behavior/flee.hpp +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_AI_STEERING_BEHAVIOR_FLEE_HPP -#define ANTKEEPER_AI_STEERING_BEHAVIOR_FLEE_HPP - -#include "ai/steering/agent.hpp" -#include "utility/fundamental-types.hpp" - -namespace ai { -namespace steering { -namespace behavior { - -/** - * Attempts to steer an agent so that it moves away from a target. - * - * @param agent Autonomous agent to steer. - * @param target Target position. - * @return Flee force. - */ -float3 flee(const agent& agent, const float3& target); - -} // namespace behavior -} // namespace steering -} // namespace ai - -#endif // ANTKEEPER_AI_STEERING_BEHAVIOR_FLEE_HPP diff --git a/src/ai/steering/behavior/seek.cpp b/src/ai/steering/behavior/seek.cpp deleted file mode 100644 index 7733673..0000000 --- a/src/ai/steering/behavior/seek.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "ai/steering/behavior/seek.hpp" - -namespace ai { -namespace steering { -namespace behavior { - -float3 seek(const agent& agent, const float3& target) -{ - float3 force = {0, 0, 0}; - const float3 difference = target - agent.position; - const float sqr_distance = math::dot(difference, difference); - - if (sqr_distance) - { - const float inverse_distance = 1.0f / std::sqrt(sqr_distance); - force = difference * inverse_distance * agent.max_force; - force -= agent.velocity; - } - - return force; -} - -} // namespace behavior -} // namespace steering -} // namespace ai diff --git a/src/ai/steering/behavior/seek.hpp b/src/ai/steering/behavior/seek.hpp deleted file mode 100644 index 3967c85..0000000 --- a/src/ai/steering/behavior/seek.hpp +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_AI_STEERING_BEHAVIOR_SEEK_HPP -#define ANTKEEPER_AI_STEERING_BEHAVIOR_SEEK_HPP - -#include "ai/steering/agent.hpp" -#include "utility/fundamental-types.hpp" - -namespace ai { -namespace steering { -namespace behavior { - -/** - * Attempts to steer an agent so that it moves toward a target. - * - * @param agent Autonomous agent to steer. - * @param target Target position. - * @return Seek force. - */ -float3 seek(const agent& agent, const float3& target); - -} // namespace behavior -} // namespace steering -} // namespace ai - -#endif // ANTKEEPER_AI_STEERING_BEHAVIOR_SEEK_HPP diff --git a/src/ai/steering/behavior/wander.cpp b/src/ai/steering/behavior/wander.cpp deleted file mode 100644 index 0377ad6..0000000 --- a/src/ai/steering/behavior/wander.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "ai/steering/behavior/wander.hpp" -#include "ai/steering/behavior/seek.hpp" -#include "math/random.hpp" -#include "math/quaternion.hpp" - -namespace ai { -namespace steering { -namespace behavior { - -float3 wander_2d(const agent& agent, float noise, float distance, float radius, float& angle) -{ - // Shift wander angle - angle += math::random(-noise, noise); - - // Calculate center of wander circle - const float3 center = agent.position + agent.forward * distance; - - // Decompose orientation into swing and twist rotations - math::quaternion swing, twist; - math::swing_twist(agent.orientation, agent.up, swing, twist); - - // Calculate offset to point on wander circle - const float3 offset = math::conjugate(twist) * (math::angle_axis(angle, agent.up) * agent.forward * radius); - - // Seek toward point on wander circle - return seek(agent, center + offset); -} - -float3 wander_3d(const agent& agent, float noise, float distance, float radius, float& theta, float& phi) -{ - // Shift wander angles - theta += math::random(-noise, noise); - phi += math::random(-noise, noise); - - // Calculate center of wander sphere - const float3 center = agent.position + agent.forward * distance; - - // Convert spherical coordinates to Cartesian point on wander sphere - const float r_cos_theta = radius * std::cos(theta); - const float3 offset = - { - r_cos_theta * std::cos(phi), - r_cos_theta * std::sin(phi), - radius * std::sin(theta) - }; - - // Seek toward point on wander sphere - return seek(agent, center + offset); -} - -} // namespace behavior -} // namespace steering -} // namespace ai diff --git a/src/ai/steering/behavior/wander.hpp b/src/ai/steering/behavior/wander.hpp deleted file mode 100644 index 4eeacac..0000000 --- a/src/ai/steering/behavior/wander.hpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_AI_STEERING_BEHAVIOR_WANDER_HPP -#define ANTKEEPER_AI_STEERING_BEHAVIOR_WANDER_HPP - -#include "ai/steering/agent.hpp" -#include "utility/fundamental-types.hpp" - -namespace ai { -namespace steering { -namespace behavior { - -/** - * Steers an agent in a continuously shifting random direction on the yaw plane. - * - * @param agent Autonomous agent to steer. - * @param distance Distance to the center of the wander circle. - * @param noise Maximum wander angle shift, in radians. - * @param radius Radius of the wander circle. - * @param[in,out] angle Angular coordinate on the wander circle, in radians. - * - * @return Wander force. - */ -float3 wander_2d(const agent& agent, float noise, float distance, float radius, float& angle); - -/** - * Steers an agent in a continuously shifting random direction. - * - * @param agent Autonomous agent to steer. - * @param distance Distance to the wander sphere. - * @param radius Radius of the wander sphere. - * @param delta Maximum angle offset. - * @param[in,out] theta Polar wander angle, in radians. - * @param[in,out] phi Azimuthal wander angle, in radians. - * - * @return Wander force. - */ -float3 wander_3d(const agent& agent, float noise, float distance, float radius, float& theta, float& phi); - -} // namespace behavior -} // namespace steering -} // namespace ai - -#endif // ANTKEEPER_AI_STEERING_BEHAVIOR_WANDER_HPP diff --git a/src/ai/steering/steering.hpp b/src/ai/steering/steering.hpp deleted file mode 100644 index 6f98526..0000000 --- a/src/ai/steering/steering.hpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_AI_STEERING_HPP -#define ANTKEEPER_AI_STEERING_HPP - -namespace ai { - -/** - * Autonomous agent steering. - * - * @see Reynolds, Craig. (2002). Steering Behaviors For Autonomous Characters. - */ -namespace steering {} - -#include "ai/steering/agent.hpp" -#include "ai/steering/behavior/flee.hpp" -#include "ai/steering/behavior/seek.hpp" -#include "ai/steering/behavior/wander.hpp" - -} // namespace ai - -#endif // ANTKEEPER_AI_STEERING_HPP diff --git a/src/animation/animation.cpp b/src/animation/animation.cpp deleted file mode 100644 index b702642..0000000 --- a/src/animation/animation.cpp +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "animation.hpp" - -animation_base::animation_base(): - looped(false), - loop_count(0), - paused(false), - stopped(true), - position(0.0), - speed(1.0), - start_callback(nullptr), - end_callback(nullptr), - loop_callback(nullptr) -{} - -void animation_base::seek(double t) -{ - position = t; -} - -void animation_base::rewind() -{ - seek(0.0); -} - -void animation_base::loop(bool enabled) -{ - looped = enabled; -} - -void animation_base::pause() -{ - paused = true; -} - -void animation_base::play() -{ - if (stopped) - { - stopped = false; - - if (start_callback) - { - start_callback(); - } - } - - paused = false; -} - -void animation_base::stop() -{ - rewind(); - stopped = true; - paused = false; - loop_count = 0; -} - -void animation_base::set_speed(double speed) -{ - this->speed = speed; -} - -void animation_base::set_start_callback(std::function callback) -{ - start_callback = callback; -} - -void animation_base::set_end_callback(std::function callback) -{ - end_callback = callback; -} - -void animation_base::set_loop_callback(std::function callback) -{ - loop_callback = callback; -} diff --git a/src/animation/animation.hpp b/src/animation/animation.hpp deleted file mode 100644 index 3592416..0000000 --- a/src/animation/animation.hpp +++ /dev/null @@ -1,387 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_ANIMATION_HPP -#define ANTKEEPER_ANIMATION_HPP - -#include "animation-channel.hpp" -#include -#include -#include -#include - -/** - * Abstract base class for keyframe animations. - */ -class animation_base -{ -public: - animation_base(); - - /** - * Advances the animation position (t) by @p dt. - * - * @param dt Delta time by which the animation position will be advanced. - */ - virtual void advance(double dt) = 0; - - /** - * Sets the animation position to @p t. - * - * @param t Position in time to which the animation position will be set. - */ - void seek(double t); - - /// Sets the animation position to `0.0`. - void rewind(); - - /// Enables or disables looping of the animation. - void loop(bool enabled); - - /// Pauses the animation. - void pause(); - - /// Plays the animation. - void play(); - - /// Stops the animation, rewinds it, and resets the loop count. - void stop(); - - /** - * Sets the speed of the animation. - * - * @param speed Speed multiplier. - */ - void set_speed(double speed); - - /// Returns `true` if looping of the animation is enabled, `false` otherwise. - bool is_looped() const; - - /// Returns `true` if the animation is paused, `false` otherwise. - bool is_paused() const; - - /// Returns `true` if the animation is stopped, `false` otherwise. - bool is_stopped() const; - - /// Returns the current position in time of the animation. - double get_position() const; - - /// Returns the current loop count of the animation. - int get_loop_count() const; - - /// Returns the duration of the animation. - virtual double get_duration() const = 0; - - /// Sets the callback that's executed when the animation is started from a stopped state. - void set_start_callback(std::function callback); - - /// Sets the callback that's executed when a non-looped animation has finished. - void set_end_callback(std::function callback); - - /** - * Sets the callback that's executed when the animation loops. - * - * @param callback Loop callback function which is passed the current loop count. - */ - void set_loop_callback(std::function callback); - -protected: - bool looped; - int loop_count; - bool paused; - bool stopped; - double position; - double speed; - - std::function start_callback; - std::function end_callback; - std::function loop_callback; -}; - -inline bool animation_base::is_looped() const -{ - return looped; -} - -inline bool animation_base::is_paused() const -{ - return paused; -} - -inline bool animation_base::is_stopped() const -{ - return stopped; -} - -inline double animation_base::get_position() const -{ - return position; -} - -inline int animation_base::get_loop_count() const -{ - return loop_count; -} - -/** - * Keyframe animation. - * - * @tparam T Animated data type. - */ -template -class animation: public animation_base -{ -public: - /// Channel for this animation type. - typedef animation_channel channel; - - // Keyframe type for this animation. - typedef typename channel::keyframe keyframe; - - /// Interpolator function type. - typedef typename std::decay>::type interpolator_type; - - /// Creates an animation. - animation(); - - /// @copydoc animation_base::advance() - virtual void advance(double dt); - - /** - * Adds a channel to the animation. - * - * @param id ID of the channel. - * @return Added or pre-existing channel. - */ - channel* add_channel(int id); - - /** - * Removes a channel from the animation. - * - * @param id ID of the channel to remove. - */ - void remove_channel(int id); - - /// Removes all channels from the animation. - void remove_channels(); - - /** - * Sets the frame interpolator function object. - * - * @param interpolator Frame interpolator function object. - */ - void set_interpolator(interpolator_type interpolator); - - /** - * Sets the callback that's executed on each frame of animation. - * - * @param callback Frame callback which receives the index of an animation channel and value of an interpolated frame. - */ - void set_frame_callback(std::function callback); - - /** - * Returns the channel with the specified ID. - * - * @param id ID of the channel to get. - */ - const channel* get_channel(int id) const; - - /// @copydoc animation::get_channel(int) const - channel* get_channel(int id); - - /// @copydoc animation_base::get_duration() const - virtual double get_duration() const; - -private: - std::unordered_map channels; - interpolator_type interpolator; - std::function frame_callback; -}; - -template -animation::animation(): - interpolator(nullptr), - frame_callback(nullptr) -{} - -template -void animation::advance(double dt) -{ - if (paused || stopped) - { - return; - } - - // Advance position by dt - position += dt * speed; - - // Determine duration of the animation - double duration = get_duration(); - - if (position < duration) - { - if (frame_callback != nullptr && interpolator != nullptr) - { - for (std::size_t i = 0; i < channels.size(); ++i) - { - auto frames = channels[i].find_keyframes(position); - - if (frames[0] != nullptr && frames[1] != nullptr) - { - // Calculate interpolated frame - double t0 = std::get<0>(*frames[0]); - double t1 = std::get<0>(*frames[1]); - double alpha = (position - t0) / (t1 - t0); - T frame = interpolator(std::get<1>(*frames[0]), std::get<1>(*frames[1]), alpha); - - // Pass frame to frame callback - frame_callback(static_cast(i), frame); - } - else if (frames[0] != nullptr) - { - // Pass frame to frame callback - frame_callback(static_cast(i), std::get<1>(*frames[0])); - } - else if (frames[1] != nullptr) - { - // Pass frame to frame callback - frame_callback(static_cast(i), std::get<1>(*frames[1])); - } - } - } - } - else - { - if (looped) - { - ++loop_count; - - // Subtract duration of animation from position - position -= duration; - - // Execute loop callback - if (loop_callback) - { - loop_callback(loop_count); - } - - // Call frame callback on looped frame - if (frame_callback) - { - advance(0.0); - } - } - else - { - // Call frame callback for end frame - if (frame_callback != nullptr) - { - for (std::size_t i = 0; i < channels.size(); ++i) - { - auto frames = channels[i].find_keyframes(channels[i].get_duration()); - if (frames[0] != nullptr) - { - frame_callback(static_cast(i), std::get<1>(*frames[0])); - } - } - } - - stopped = true; - - // Call end callback - if (end_callback) - { - end_callback(); - } - } - } -} - -template -typename animation::channel* animation::add_channel(int id) -{ - return &(*channels.emplace(id, id).first).second; -} - -template -void animation::remove_channel(int id) -{ - auto it = channels.find(id); - if (it != channels.end()) - { - channels.erase(it); - } -} - -template -void animation::remove_channels() -{ - channels.clear(); -} - -template -void animation::set_interpolator(interpolator_type interpolator) -{ - this->interpolator = interpolator; -} - -template -void animation::set_frame_callback(std::function callback) -{ - this->frame_callback = callback; -} - -template -const typename animation::channel* animation::get_channel(int id) const -{ - auto it = channels.find(id); - if (it != channels.end()) - { - return &it->second; - } - - return nullptr; -} - -template -typename animation::channel* animation::get_channel(int id) -{ - auto it = channels.find(id); - if (it != channels.end()) - { - return &it->second; - } - - return nullptr; -} - -template -double animation::get_duration() const -{ - double duration = 0.0; - - for (auto it = channels.begin(); it != channels.end(); ++it) - { - duration = std::max(duration, it->second.get_duration()); - } - - return duration; -} - -#endif // ANTKEEPER_ANIMATION_HPP diff --git a/src/animation/animator.cpp b/src/animation/animator.cpp deleted file mode 100644 index b78b861..0000000 --- a/src/animation/animator.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "animator.hpp" -#include "animation/animation.hpp" -#include - -animator::animator(): - animating(0) -{} - -void animator::animate(double dt) -{ - // Advance animations - ++animating; - for (animation_base* animation: animations) - { - animation->advance(dt); - } - --animating; -} - -void animator::add_animation(animation_base* animation) -{ - if (animating) - throw std::runtime_error("Attempting to add animation to animator while animating"); - - animations.emplace(animation); -} - -void animator::remove_animation(animation_base* animation) -{ - if (animating) - throw std::runtime_error("Attempting to remove animation from animator while animating"); - - auto it = animations.find(animation); - if (it != animations.end()) - { - animations.erase(it); - } -} - -void animator::remove_animations() -{ - if (animating) - throw std::runtime_error("Attempting to remove animations from animator while animating"); - - animations.clear(); -} diff --git a/src/animation/ease.hpp b/src/animation/ease.hpp deleted file mode 100644 index 13ecbf4..0000000 --- a/src/animation/ease.hpp +++ /dev/null @@ -1,397 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -/* - * Easing Functions (Equations) - * - * Copyright (C) 2001 Robert Penner - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * * Neither the name of the author nor the names of contributors may be used to - * endorse or promote products derived from this software without specific - * prior written permission. - - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef ANTKEEPER_EASE_HPP -#define ANTKEEPER_EASE_HPP - -#include "math/interpolation.hpp" -#include - -/** - * Container for templated easing functions. - * - * All easing functions require the following operators to be defined: - * - * value_type operator+(const value_type&, const value_type&); - * value_type operator-(const value_type&, const value_type&); - * value_type operator*(const value_type&, scalar_type); - * - * @tparam T Value type. - * @tparam S Scalar type. - */ -template -struct ease -{ - typedef T value_type; - typedef S scalar_type; - - static T in_sine(const T& x, const T& y, S a); - static T out_sine(const T& x, const T& y, S a); - static T in_out_sine(const T& x, const T& y, S a); - - static T in_quad(const T& x, const T& y, S a); - static T out_quad(const T& x, const T& y, S a); - static T in_out_quad(const T& x, const T& y, S a); - - static T in_cubic(const T& x, const T& y, S a); - static T out_cubic(const T& x, const T& y, S a); - static T in_out_cubic(const T& x, const T& y, S a); - - static T in_quart(const T& x, const T& y, S a); - static T out_quart(const T& x, const T& y, S a); - static T in_out_quart(const T& x, const T& y, S a); - - static T in_quint(const T& x, const T& y, S a); - static T out_quint(const T& x, const T& y, S a); - static T in_out_quint(const T& x, const T& y, S a); - - static T in_expo(const T& x, const T& y, S a); - static T out_expo(const T& x, const T& y, S a); - static T in_out_expo(const T& x, const T& y, S a); - - static T in_circ(const T& x, const T& y, S a); - static T out_circ(const T& x, const T& y, S a); - static T in_out_circ(const T& x, const T& y, S a); - - static T in_back(const T& x, const T& y, S a); - static T out_back(const T& x, const T& y, S a); - static T in_out_back(const T& x, const T& y, S a); - - static T in_elastic(const T& x, const T& y, S a); - static T out_elastic(const T& x, const T& y, S a); - static T in_out_elastic(const T& x, const T& y, S a); - - static T in_bounce(const T& x, const T& y, S a); - static T out_bounce(const T& x, const T& y, S a); - static T in_out_bounce(const T& x, const T& y, S a); -}; - -template -T ease::in_sine(const T& x, const T& y, S a) -{ - return math::lerp(y, x, std::cos(a * math::half_pi)); -} - -template -T ease::out_sine(const T& x, const T& y, S a) -{ - return math::lerp(x, y, std::sin(a * math::half_pi)); -} - -template -T ease::in_out_sine(const T& x, const T& y, S a) -{ - return math::lerp(x, y, -(std::cos(a * math::pi) - S(1)) * S(0.5)); -} - -template -T ease::in_quad(const T& x, const T& y, S a) -{ - return math::lerp(x, y, a * a); -} - -template -T ease::out_quad(const T& x, const T& y, S a) -{ - return math::lerp(x, y, (S(2) - a) * a); -} - -template -T ease::in_out_quad(const T& x, const T& y, S a) -{ - return math::lerp(x, y, (a < S(0.5)) ? S(2) * a * a : -(S(2) * a * a - S(4) * a + S(1))); -} - -template -T ease::in_cubic(const T& x, const T& y, S a) -{ - return math::lerp(x, y, a * a * a); -} - -template -T ease::out_cubic(const T& x, const T& y, S a) -{ - return math::lerp(x, y, a * ((a - S(3)) * a + S(3))); -} - -template -T ease::in_out_cubic(const T& x, const T& y, S a) -{ - return math::lerp(x, y, (a < S(0.5)) ? S(4) * a * a * a : S(4) * a * a * a - S(12) * a * a + S(12) * a - 3); -} - -template -T ease::in_quart(const T& x, const T& y, S a) -{ - return math::lerp(x, y, a * a * a * a); -} - -template -T ease::out_quart(const T& x, const T& y, S a) -{ - return math::lerp(x, y, a * (a * ((S(4) - a) * a - S(6)) + S(4))); -} - -template -T ease::in_out_quart(const T& x, const T& y, S a) -{ - return math::lerp(x, y, (a < S(0.5)) ? S(8) * a * a * a * a : a * (a * ((S(32) - S(8) * a) * a - S(48)) + S(32)) - S(7)); -} - -template -T ease::in_quint(const T& x, const T& y, S a) -{ - return math::lerp(x, y, a * a * a * a * a); -} - -template -T ease::out_quint(const T& x, const T& y, S a) -{ - return math::lerp(x, y, a * (a * (a * ((a - S(5)) * a + S(10)) - S(10)) + S(5))); -} - -template -T ease::in_out_quint(const T& x, const T& y, S a) -{ - if (a < S(0.5)) - { - return math::lerp(x, y, S(16) * a * a * a * a * a); - } - else - { - a = S(2) * (S(1) - a); - return math::lerp(x, y, S(0.5) * (S(2) - a * a * a * a * a)); - } -} - -template -T ease::in_expo(const T& x, const T& y, S a) -{ - return (a == S(0)) ? x : math::lerp(x, y, std::pow(S(1024), a - S(1))); -} - -template -T ease::out_expo(const T& x, const T& y, S a) -{ - return (a == S(1)) ? y : math::lerp(y, x, std::pow(S(2), S(-10) * a)); -} - -template -T ease::in_out_expo(const T& x, const T& y, S a) -{ - if (a == S(0)) - { - return x; - } - else if (a == S(1)) - { - return y; - } - - return math::lerp(x, y, (a < S(0.5)) ? std::pow(S(2), S(20) * a - S(11)) : S(1) - std::pow(S(2), S(9) - S(20) * a)); -} - -template -T ease::in_circ(const T& x, const T& y, S a) -{ - return math::lerp(y, x, std::sqrt(S(1) - a * a)); -} - -template -T ease::out_circ(const T& x, const T& y, S a) -{ - return math::lerp(x, y, std::sqrt(-(a - S(2)) * a)); -} - -template -T ease::in_out_circ(const T& x, const T& y, S a) -{ - if (a < S(0.5)) - { - return math::lerp(x, y, S(0.5) - S(0.5) * std::sqrt(S(1) - S(4) * a * a)); - } - else - { - return math::lerp(x, y, S(0.5) * (std::sqrt(S(-4) * (a - S(2)) * a - S(3)) + S(1))); - } -} - -template -T ease::in_back(const T& x, const T& y, S a) -{ - const S c = S(1.70158); - return math::lerp(x, y, a * a * (a * c + a - c)); -} - -template -T ease::out_back(const T& x, const T& y, S a) -{ - const S c = S(1.70158); - a -= S(1); - return math::lerp(x, y, a * a * (a * c + a + c) + S(1)); -} - -template -T ease::in_out_back(const T& x, const T& y, S a) -{ - const S c = S(1.70158) * S(1.525f); - - if (a < S(0.5)) - { - return math::lerp(x, y, a * a * (a * (S(4) * c + S(4)) - S(2) * c)); - } - else - { - S b = S(1) - S(2) * a; - return math::lerp(x, y, b * b * (a * c + a - c * S(0.5) - S(1)) + S(1)); - } -} - -template -T ease::in_elastic(const T& x, const T& y, S a) -{ - if (a == S(0)) - { - return x; - } - else if (a == S(1)) - { - return y; - } - - return math::lerp(x, y, -std::pow(S(1024), a - S(1)) * std::sin(S(20.944) * (a - S(1.075)))); -} - -template -T ease::out_elastic(const T& x, const T& y, S a) -{ - if (a == S(0)) - { - return x; - } - else if (a == S(1)) - { - return y; - } - - return math::lerp(x, y, std::pow(S(2), S(-10) * a) * std::sin(S(20.944) * (a - S(0.075))) + S(1)); -} - -template -T ease::in_out_elastic(const T& x, const T& y, S a) -{ - if (a == S(0)) - { - return x; - } - else if (a == S(1)) - { - return y; - } - - if (a < S(0.5)) - { - return math::lerp(x, y, std::pow(S(2), S(20) * a - S(11)) * std::sin(S(15.5334) - S(27.5293) * a)); - } - else - { - return math::lerp(y, x, std::pow(2, S(9) - S(20) * a) * std::sin(S(15.5334) - S(27.5293) * a)); - } -} - -template -T ease::in_bounce(const T& x, const T& y, S a) -{ - return math::lerp(x, y, S(1) - ease::out_bounce(S(0), S(1), S(1) - a)); -} - -template -T ease::out_bounce(const T& x, const T& y, S a) -{ - const S n = S(7.5625); - const S d = S(2.75); - - if (a < S(1) / d) - { - a = n * a * a; - } - else if (a < S(2) / d) - { - a -= S(1.5) / d; - a = n * a * a + S(0.75); - } - else if (a < S(2.5) / d) - { - a -= S(2.25) / d; - a = n * a * a + S(0.9375); - } - else - { - a -= S(2.625) / d; - a = n * a * a + S(0.984375); - } - - return math::lerp(x, y, a); -} - -template -T ease::in_out_bounce(const T& x, const T& y, S a) -{ - if (a < S(0.5)) - { - return math::lerp(x, y, (S(1) - ease::out_bounce(S(0), S(1), S(1) - S(2) * a)) * S(0.5)); - } - else - { - return math::lerp(x, y, (S(1) + ease::out_bounce(S(0), S(1), S(2) * a - S(1))) * S(0.5)); - } -} - -#endif // ANTKEEPER_EASE_HPP diff --git a/src/animation/pose.cpp b/src/animation/pose.cpp deleted file mode 100644 index e01ec0f..0000000 --- a/src/animation/pose.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "animation/pose.hpp" -#include "math/transform-operators.hpp" -#include "math/transform-functions.hpp" - -void concatenate(const pose& bone_space, pose& skeleton_space) -{ - for (auto&& [bone, transform]: bone_space) - { - auto parent_index = bone_parent_index(bone); - - if (parent_index != bone_index(bone)) - { - auto parent = skeleton_space.find(parent_index); - skeleton_space[bone] = (parent != skeleton_space.end()) ? parent->second * transform : transform; - } - else - { - skeleton_space[bone] = transform; - } - } -} - -void inverse(const pose& x, pose& y) -{ - for (auto&& [bone, transform]: x) - { - y[bone] = math::inverse(transform); - } -} - -void matrix_palette(const pose& inverse_bind_pose, const pose& pose, float4x4* palette) -{ - for (auto&& [bone, transform]: pose) - { - auto index = ::bone_index(bone); - palette[index] = math::matrix_cast(inverse_bind_pose.at(bone) * transform); - } -} diff --git a/src/animation/pose.hpp b/src/animation/pose.hpp deleted file mode 100644 index c285e8c..0000000 --- a/src/animation/pose.hpp +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_ANIMATION_POSE_HPP -#define ANTKEEPER_ANIMATION_POSE_HPP - -#include "animation/bone.hpp" -#include "math/transform-type.hpp" -#include "utility/fundamental-types.hpp" -#include - -/** - * Skeletal animation pose. - */ -typedef std::map, bone_index_compare> pose; - -/** - * Transforms a pose from bone-space into skeleton-space. - * - * @param[in] bone_space Bone-space pose. - * @param[out] skeleton_space Skeleton-space pose. - * - * @warning If the index of any child bone is greater than its parent index, the concatenated pose may be incorrect. - */ -void concatenate(const pose& bone_space, pose& skeleton_space); - -/** - * Inverses each transform in a pose. - * - * @param[in] x Input pose. - * @param[out] y Output pose. - */ -void inverse(const pose& x, pose& y); - -/** - * Generates a skinning matrix palette from a pose. - * - * @param inverse_bind_pose Inverse of the skeleton-space bind pose. - * @param pose Bone-space Skeleton-space pose. - */ -void matrix_palette(const pose& inverse_bind_pose, const pose& pose, float4x4* palette); - -#endif // ANTKEEPER_ANIMATION_POSE_HPP diff --git a/src/animation/screen-transition.cpp b/src/animation/screen-transition.cpp deleted file mode 100644 index 48f1588..0000000 --- a/src/animation/screen-transition.cpp +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "screen-transition.hpp" -#include "render/material-flags.hpp" -#include - -screen_transition::screen_transition() -{ - // Setup material - //material.set_flags(MATERIAL_FLAG_X_RAY); - material.set_blend_mode(render::blend_mode::translucent); - progress = material.add_property("progress"); - - // Setup billboard - billboard.set_material(&material); - billboard.set_active(false); - - // Add single channel to transition animation - channel = animation.add_channel(0); - - // Setup animation start callback to show transition billboard - animation.set_start_callback - ( - std::bind(&scene::object_base::set_active, &billboard, true) - ); - - // Setup animation end callback to hide transition billboard - animation.set_end_callback - ( - std::bind(&scene::object_base::set_active, &billboard, false) - ); - - // Setup animation frame callback to update transition progress material property - animation.set_frame_callback - ( - [this](int channel, float progress) - { - this->progress->set_value(progress); - } - ); - - // Setup animation frame callback to update transition progress material property - animation.set_frame_callback - ( - [this](int channel, float progress) - { - this->progress->set_value(progress); - } - ); -} - -void screen_transition::set_visible(bool visible) -{ - billboard.set_active(visible); -} - -void screen_transition::transition(float duration, bool reverse, ::animation::interpolator_type interpolator, bool hide, const std::function& callback) -{ - float initial_state = (reverse) ? 1.0f : 0.0f; - float final_state = (reverse) ? 0.0f : 1.0f; - - // Build transition animation - channel->remove_keyframes(); - channel->insert_keyframe({0.0f, initial_state}); - channel->insert_keyframe({duration, final_state}); - - // Set transition animation interpolator - animation.set_interpolator(interpolator); - - this->callback = callback; - if (hide) - { - // Setup animation end callback to hide transition billboard - animation.set_end_callback - ( - [this]() - { - this->billboard.set_active(false); - if (this->callback) - this->callback(); - } - ); - } - else - { - animation.set_end_callback(callback); - } - - // Update tweens - progress->set_value(initial_state); - material.update_tweens(); - - // Reset and play transition animation - animation.stop(); - animation.play(); -} diff --git a/src/animation/screen-transition.hpp b/src/animation/screen-transition.hpp deleted file mode 100644 index a3fa2d8..0000000 --- a/src/animation/screen-transition.hpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_SCREEN_TRANSITION_HPP -#define ANTKEEPER_SCREEN_TRANSITION_HPP - -#include "animation/animation.hpp" -#include "render/material.hpp" -#include "render/material-property.hpp" -#include "scene/billboard.hpp" - -/** - * Encapsulates a shader-based animated screen transition. - */ -class screen_transition -{ -public: - screen_transition(); - - void set_visible(bool visible); - - void transition(float duration, bool reverse, animation::interpolator_type interpolator, bool hide = true, const std::function& callback = nullptr); - - scene::billboard* get_billboard(); - render::material* get_material(); - ::animation* get_animation(); - -private: - scene::billboard billboard; - render::material material; - render::material_property* progress; - ::animation animation; - ::animation::channel* channel; - std::function callback; -}; - -inline scene::billboard* screen_transition::get_billboard() -{ - return &billboard; -} - -inline render::material* screen_transition::get_material() -{ - return &material; -} - -inline animation* screen_transition::get_animation() -{ - return &animation; -} - -#endif // ANTKEEPER_SCREEN_TRANSITION_HPP diff --git a/src/animation/skeleton.cpp b/src/animation/skeleton.cpp deleted file mode 100644 index 4c1431f..0000000 --- a/src/animation/skeleton.cpp +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "animation/skeleton.hpp" - diff --git a/src/animation/skeleton.hpp b/src/animation/skeleton.hpp deleted file mode 100644 index b3bf89a..0000000 --- a/src/animation/skeleton.hpp +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_ANIMATION_SKELETON_HPP -#define ANTKEEPER_ANIMATION_SKELETON_HPP - -#include "animation/bone.hpp" -#include "animation/pose.hpp" -#include -#include - -/** - * Skeletal animation skeleton. - */ -struct skeleton -{ - /// Bone-space bind pose of the skeleton. - pose bind_pose; - - /// Inverse skeleton-space bind pose of the skeleton. - pose inverse_bind_pose; - - /// Maps bone names to bone identifiers. - std::unordered_map bone_map; -}; - -#endif // ANTKEEPER_ANIMATION_SKELETON_HPP diff --git a/src/animation/spring.hpp b/src/animation/spring.hpp deleted file mode 100644 index 30411f6..0000000 --- a/src/animation/spring.hpp +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_SPRING_HPP -#define ANTKEEPER_SPRING_HPP - -#include "math/numbers.hpp" - -/** - * Contains the variables required for numeric springing. - * - * @tparam T Value type. - * @tparam S Scalar type. - * - * @see spring() - * @see solve_numeric_spring() - */ -template -struct numeric_spring -{ - T x0; ///< Start value - T x1; ///< End value - T v; ///< Velocity - S z; ///< Damping ratio, which can be undamped (z = 0), underdamped (z < 1), critically damped (z = 1), or overdamped (z > 1). - S w; ///< Angular frequency of the oscillation, in radians per second (2pi = 1Hz). -}; - -/** - * Solves a number spring using the implicit Euler method. - * - * @tparam T Value type. - * @tparam S Scalar type. - * - * @param[in,out] x0 Start value, which will be oscillated by this function. - * @param[in,out] v Velocity, which will be modified by this function. - * @param[in] x1 End value. - * @param[in] z Damping ratio, which can be undamped (z = 0), underdamped (z < 1), critically damped (z = 1), or overdamped (z > 1). - * @param[in] w Angular frequency of the oscillation, in radians per second (2pi = 1Hz). - * @param[in] dt Delta time, in seconds. - */ -template -void spring(T& x0, T& v, const T& x1, S z, S w, S dt); - -/** - * Solves a number spring using the implicit Euler method. - * - * @param[in,out] ns Numeric spring to be sovled. - * @param dt Delta time, in seconds. - * - * @see spring() - */ -template -void solve_numeric_spring(numeric_spring& ns, S dt); - -/** - * Converts a frequency from hertz to radians per second. - * - * @param hz Frequency in hertz. - * @return Frequency in radians per second. - */ -template -T hz_to_rads(T hz); - -/** - * Converts a frequency from radians per second to hertz. - * - * @param rads Frequency in radians per second. - * @return Frequency in hertz. - */ -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) -{ - const S ww_dt = w * w * dt; - const S ww_dtdt = ww_dt * dt; - const S f = z * w * dt * S{2} + S{1}; - const T det_x = x0 * f + v * dt + x1 * ww_dtdt; - const T det_v = v + (x1 - x0) * ww_dt; - const S inv_det = S{1} / (f + ww_dtdt); - - x0 = det_x * inv_det; - v = det_v * inv_det; -} - -template -void solve_numeric_spring(numeric_spring& ns, S dt) -{ - spring(ns.x0, ns.v, ns.x1, ns.z, ns.w, dt); -} - -template -inline T hz_to_rads(T hz) -{ - return hz * math::two_pi; -} - -template -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/animation/timeline.cpp b/src/animation/timeline.cpp deleted file mode 100644 index 528a5b3..0000000 --- a/src/animation/timeline.cpp +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "timeline.hpp" - -auto cue_compare = [](const typename timeline::cue& a, const typename timeline::cue& b) -{ - return std::get<0>(a) < std::get<0>(b); -}; - -timeline::timeline(): - cues(cue_compare), - position(0.0f), - autoremove(false) -{} - -void timeline::advance(float dt) -{ - auto lower_bound = cues.lower_bound({position, nullptr}); - auto upper_bound = cues.upper_bound({position + dt, nullptr}); - - for (auto iterator = lower_bound; iterator != upper_bound; ++iterator) - { - std::get<1>(*iterator)(); - } - - if (autoremove && lower_bound != upper_bound) - { - cues.erase(lower_bound, upper_bound); - } - - position += dt; -} - -void timeline::seek(float t) -{ - position = t; -} - -void timeline::add_cue(const cue& c) -{ - cues.emplace(c); -} - -void timeline::remove_cue(const cue& c) -{ - cues.erase(c); -} - -void timeline::remove_cues(float start, float end) -{ - auto lower_bound = cues.lower_bound({start, nullptr}); - auto upper_bound = cues.upper_bound({end, nullptr}); - cues.erase(lower_bound, upper_bound); -} - -void timeline::add_sequence(const sequence& s) -{ - for (const cue& c: s) - { - add_cue(c); - } -} - -void timeline::remove_sequence(const sequence& s) -{ - for (const cue& c: s) - { - remove_cue(c); - } -} - -void timeline::clear() -{ - cues.clear(); -} - -void timeline::set_autoremove(bool enabled) -{ - autoremove = enabled; -} - -typename timeline::sequence timeline::get_cues(float start, float end) const -{ - sequence s; - - auto lower_bound = cues.lower_bound({start, nullptr}); - auto upper_bound = cues.upper_bound({end, nullptr}); - for (auto iterator = lower_bound; iterator != upper_bound; ++iterator) - { - s.push_back(*iterator); - } - - return s; -} - diff --git a/src/app/display-events.hpp b/src/app/display-events.hpp deleted file mode 100644 index f343a8c..0000000 --- a/src/app/display-events.hpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_APP_DISPLAY_EVENTS_HPP -#define ANTKEEPER_APP_DISPLAY_EVENTS_HPP - -#include "app/display-orientation.hpp" - -namespace app { - -class display; - -/** - * Event generated when a display has been connected. - */ -struct display_connected_event -{ - /// Pointer to the display that has been connected. - const display* display; -}; - -/** - * Event generated when a display has been disconnected. - */ -struct display_disconnected_event -{ - /// Pointer to the display that has been disconnected. - const display* display; -}; - -/** - * Event generated when the orientation of a display has changed. - */ -struct display_orientation_changed_event -{ - /// Pointer to the display that has had it's orientation changed. - const display* display; - - /// Orientation of the display. - display_orientation orientation; -}; - -} // namespace app - -#endif // ANTKEEPER_APP_DISPLAY_EVENTS_HPP diff --git a/src/app/display.hpp b/src/app/display.hpp deleted file mode 100644 index bd646f1..0000000 --- a/src/app/display.hpp +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_APP_DISPLAY_HPP -#define ANTKEEPER_APP_DISPLAY_HPP - -#include "app/display-orientation.hpp" -#include "app/display-events.hpp" -#include "geom/primitive/rectangle.hpp" -#include "event/publisher.hpp" -#include - -namespace app { - -/** - * Virtual display. - */ -class display -{ -public: - /** - * Sets the index of the display. - * - * @param index Index of the display. - */ - inline void set_index(int index) noexcept - { - this->index = index; - } - - /** - * Sets the name of the display. - * - * @param name Name of the display. - */ - inline void set_name(const std::string& name) noexcept - { - this->name = name; - } - - /** - * Sets the bounds of the display. - * - * @param bounds Bounds of the display, in display units. - */ - inline void set_bounds(const geom::primitive::rectangle& bounds) noexcept - { - this->bounds = bounds; - } - - /** - * Sets the usable bounds of the display, which excludes areas reserved by the OS for things like menus or docks. - */ - inline void set_usable_bounds(const geom::primitive::rectangle& bounds) noexcept - { - this->usable_bounds = bounds; - } - - /** - * Sets the refresh rate of the display. - * - * @param rate Refresh rate, in Hz. - */ - inline void set_refresh_rate(int rate) noexcept - { - this->refresh_rate = rate; - } - - /** - * Sets the DPI of the display. - * - * @param dpi DPI. - */ - inline void set_dpi(float dpi) noexcept - { - this->dpi = dpi; - } - - /** - * Sets the orientation of the display. - * - * @param orientation Display orientation. - */ - inline void set_orientation(display_orientation orientation) noexcept - { - this->orientation = orientation; - } - - /// Returns the index of the display. - [[nodiscard]] inline const int& get_index() const noexcept - { - return index; - } - - /// Returns the name of the display. - [[nodiscard]] inline const std::string& get_name() const noexcept - { - return name; - } - - /// Returns the bounds of the display, in display units. - [[nodiscard]] inline const geom::primitive::rectangle& get_bounds() const noexcept - { - return bounds; - } - - /// Returns the usable bounds of the display, which excludes areas reserved by the OS for things like menus or docks, in display units. - [[nodiscard]] inline const geom::primitive::rectangle& get_usable_bounds() const noexcept - { - return usable_bounds; - } - - /// Returns the refresh rate of the display, in Hz. - [[nodiscard]] inline const int& get_refresh_rate() const noexcept - { - return refresh_rate; - } - - /// Returns the DPI of the display. - [[nodiscard]] inline const float& get_dpi() const noexcept - { - return dpi; - } - - /// Returns the current orientation of the display. - [[nodiscard]] inline const display_orientation& get_orientation() const noexcept - { - return orientation; - } - - /// Returns `true` if the display is connected, `false` otherwise. - [[nodiscard]] inline const bool& is_connected() const noexcept - { - return connected; - } - - /// Returns the channel through which display connected events are published. - [[nodiscard]] inline event::channel& get_connected_channel() noexcept - { - return connected_publisher.channel(); - } - - /// Returns the channel through which display disconnected events are published. - [[nodiscard]] inline event::channel& get_disconnected_channel() noexcept - { - return disconnected_publisher.channel(); - } - - /// Returns the channel through which display orientation changed events are published. - [[nodiscard]] inline event::channel& get_orientation_changed_channel() noexcept - { - return orientation_changed_publisher.channel(); - } - -private: - friend class window_manager; - friend class sdl_window_manager; - - int index; - std::string name; - geom::primitive::rectangle bounds; - geom::primitive::rectangle usable_bounds; - int refresh_rate; - float dpi; - display_orientation orientation; - bool connected; - - event::publisher connected_publisher; - event::publisher disconnected_publisher; - event::publisher orientation_changed_publisher; -}; - -} // namespace app - -#endif // ANTKEEPER_APP_DISPLAY_HPP diff --git a/src/app/input-manager.cpp b/src/app/input-manager.cpp deleted file mode 100644 index 907cf38..0000000 --- a/src/app/input-manager.cpp +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "app/input-manager.hpp" -#include "app/sdl/sdl-input-manager.hpp" - -namespace app { - -input_manager* input_manager::instance() -{ - return new sdl_input_manager(); -} - -void input_manager::register_device(input::device& device) -{ - switch (device.get_device_type()) - { - case input::device_type::gamepad: - register_gamepad(static_cast(device)); - break; - - case input::device_type::keyboard: - register_keyboard(static_cast(device)); - break; - - case input::device_type::mouse: - register_mouse(static_cast(device)); - break; - - default: - //std::unreachable(); - break; - } -} - -void input_manager::register_gamepad(input::gamepad& device) -{ - // Connect gamepad event signals to the event queue - subscriptions.emplace(&device, device.get_connected_channel().subscribe(event_queue)); - subscriptions.emplace(&device, device.get_disconnected_channel().subscribe(event_queue)); - subscriptions.emplace(&device, device.get_axis_moved_channel().subscribe(event_queue)); - subscriptions.emplace(&device, device.get_button_pressed_channel().subscribe(event_queue)); - subscriptions.emplace(&device, device.get_button_released_channel().subscribe(event_queue)); - - // Add gamepad to list of gamepads - gamepads.emplace(&device); -} - -void input_manager::register_keyboard(input::keyboard& device) -{ - // Connect keyboard event signals to the event queue - subscriptions.emplace(&device, device.get_connected_channel().subscribe(event_queue)); - subscriptions.emplace(&device, device.get_disconnected_channel().subscribe(event_queue)); - subscriptions.emplace(&device, device.get_key_pressed_channel().subscribe(event_queue)); - subscriptions.emplace(&device, device.get_key_released_channel().subscribe(event_queue)); - - // Add keyboard to list of keyboards - keyboards.emplace(&device); -} - -void input_manager::register_mouse(input::mouse& device) -{ - // Connect mouse event signals to the event queue - subscriptions.emplace(&device, device.get_connected_channel().subscribe(event_queue)); - subscriptions.emplace(&device, device.get_disconnected_channel().subscribe(event_queue)); - subscriptions.emplace(&device, device.get_button_pressed_channel().subscribe(event_queue)); - subscriptions.emplace(&device, device.get_button_released_channel().subscribe(event_queue)); - subscriptions.emplace(&device, device.get_moved_channel().subscribe(event_queue)); - subscriptions.emplace(&device, device.get_scrolled_channel().subscribe(event_queue)); - - // Add mouse to list of mice - mice.emplace(&device); -} - -void input_manager::unregister_device(input::device& device) -{ - subscriptions.erase(&device); - - switch (device.get_device_type()) - { - case input::device_type::gamepad: - unregister_gamepad(static_cast(device)); - break; - - case input::device_type::keyboard: - unregister_keyboard(static_cast(device)); - break; - - case input::device_type::mouse: - unregister_mouse(static_cast(device)); - break; - - default: - //std::unreachable(); - break; - } -} - -void input_manager::unregister_gamepad(input::gamepad& gamepad) -{ - gamepads.erase(&gamepad); -} - -void input_manager::unregister_keyboard(input::keyboard& keyboard) -{ - keyboards.erase(&keyboard); -} - -void input_manager::unregister_mouse(input::mouse& mouse) -{ - mice.erase(&mouse); -} - -} // namespace app diff --git a/src/app/input-manager.hpp b/src/app/input-manager.hpp deleted file mode 100644 index 626ee1c..0000000 --- a/src/app/input-manager.hpp +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_APP_INPUT_MANAGER_HPP -#define ANTKEEPER_APP_INPUT_MANAGER_HPP - -#include "input/device.hpp" -#include "input/gamepad.hpp" -#include "input/keyboard.hpp" -#include "input/mouse.hpp" -#include "event/queue.hpp" -#include -#include -#include - -namespace app { - -/** - * Manages virtual input devices. - */ -class input_manager -{ -public: - /** - * Allocates and returns an input manager. - */ - static input_manager* instance(); - - /// Destructs an input manager. - virtual ~input_manager() = default; - - /** - * Processes input events. - */ - virtual void update() = 0; - - /** - * Makes the cursor visible. - */ - virtual void show_cursor() = 0; - - /** - * Makes the cursor invisible. - */ - virtual void hide_cursor() = 0; - - /** - * Returns the event queue associated with registered input devices. - */ - [[nodiscard]] inline const ::event::queue& get_event_queue() const noexcept - { - return event_queue; - } - - /** - * Returns the event queue associated with registered input devices. - */ - [[nodiscard]] inline ::event::queue& get_event_queue() noexcept - { - return event_queue; - } - - /// Returns the set of registered gamepads. - [[nodiscard]] inline const std::unordered_set& get_gamepads() noexcept - { - return gamepads; - } - - /// Returns the set of registered keyboards. - [[nodiscard]] inline const std::unordered_set& get_keyboards() noexcept - { - return keyboards; - } - - /// Returns the set of registered mice. - [[nodiscard]] inline const std::unordered_set& get_mice() noexcept - { - return mice; - } - -protected: - /** - * Registers an input device. - * - * @param device Input device to register. - */ - /// @{ - void register_device(input::device& device); - void register_gamepad(input::gamepad& device); - void register_keyboard(input::keyboard& device); - void register_mouse(input::mouse& device); - /// @} - - /** - * Unregisters an input device. - * - * @param device Input device to unregister. - */ - /// @{ - void unregister_device(input::device& device); - void unregister_gamepad(input::gamepad& device); - void unregister_keyboard(input::keyboard& device); - void unregister_mouse(input::mouse& device); - /// @} - - ::event::queue event_queue; - -private: - std::multimap> subscriptions; - std::unordered_set gamepads; - std::unordered_set keyboards; - std::unordered_set mice; -}; - -} // namespace app - -#endif // ANTKEEPER_APP_INPUT_MANAGER_HPP diff --git a/src/app/sdl/sdl-input-manager.cpp b/src/app/sdl/sdl-input-manager.cpp deleted file mode 100644 index ba5a271..0000000 --- a/src/app/sdl/sdl-input-manager.cpp +++ /dev/null @@ -1,340 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "app/sdl/sdl-input-manager.hpp" -#include "input/application-events.hpp" -#include "debug/log.hpp" -#include "math/map.hpp" -#include -#include - -namespace app { - -sdl_input_manager::sdl_input_manager() -{ - // Init SDL joystick and controller subsystems - debug::log::trace("Initializing SDL joystick and controller subsystems..."); - if (SDL_InitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) != 0) - { - debug::log::error("Failed to initialize SDL joystick and controller subsytems: {}", SDL_GetError()); - throw std::runtime_error("Failed to initialize SDL joystick and controller subsytems"); - } - else - { - debug::log::trace("Initialized SDL joystick and controller subsystems"); - } - - // Register keyboard and mouse - register_keyboard(keyboard); - register_mouse(mouse); - - // Generate keyboard and mouse device connected events - keyboard.connect(); - mouse.connect(); -} - -sdl_input_manager::~sdl_input_manager() -{ - // Quit SDL joystick and controller subsystems - debug::log::trace("Quitting SDL joystick and controller subsystems..."); - SDL_QuitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER); - debug::log::trace("Quit SDL joystick and controller subsystems..."); -} - -void sdl_input_manager::update() -{ - // Active modifier keys - std::uint16_t sdl_key_mod = KMOD_NONE; - std::uint16_t modifier_keys = input::modifier_key::none; - - // Gather SDL events from event queue - SDL_PumpEvents(); - - // Handle OS events - for (;;) - { - // Get next display or window event - SDL_Event event; - int status = SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_FIRSTEVENT, SDL_LOCALECHANGED); - - if (!status) - { - break; - } - else if (status < 0) - { - debug::log::error("Failed to peep SDL events: {}", SDL_GetError()); - throw std::runtime_error("Failed to peep SDL events"); - } - - switch (event.type) - { - case SDL_QUIT: - debug::log::debug("Application quit requested"); - this->event_queue.enqueue({}); - break; - - default: - break; - } - } - - // Handle keyboard, mouse, and gamepad events - for (;;) - { - // Get next display or window event - SDL_Event event; - int status = SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_KEYDOWN, SDL_LASTEVENT); - - if (!status) - { - break; - } - else if (status < 0) - { - debug::log::error("Failed to peep SDL events: {}", SDL_GetError()); - throw std::runtime_error("Failed to peep SDL events"); - } - - switch (event.type) - { - [[likely]] case SDL_MOUSEMOTION: - { - mouse.move({event.motion.x, event.motion.y}, {event.motion.xrel, event.motion.yrel}); - break; - } - - [[likely]] case SDL_KEYDOWN: - case SDL_KEYUP: - { - // Get scancode of key - const input::scancode scancode = static_cast(event.key.keysym.scancode); - - // Rebuild modifier keys bit mask - if (sdl_key_mod != event.key.keysym.mod) - { - sdl_key_mod = event.key.keysym.mod; - - modifier_keys = input::modifier_key::none; - if (sdl_key_mod & KMOD_LSHIFT) - modifier_keys |= input::modifier_key::left_shift; - if (sdl_key_mod & KMOD_RSHIFT) - modifier_keys |= input::modifier_key::right_shift; - if (sdl_key_mod & KMOD_LCTRL) - modifier_keys |= input::modifier_key::left_ctrl; - if (sdl_key_mod & KMOD_RCTRL) - modifier_keys |= input::modifier_key::right_ctrl; - if (sdl_key_mod & KMOD_LALT) - modifier_keys |= input::modifier_key::left_alt; - if (sdl_key_mod & KMOD_RALT) - modifier_keys |= input::modifier_key::right_alt; - if (sdl_key_mod & KMOD_LGUI) - modifier_keys |= input::modifier_key::left_gui; - if (sdl_key_mod & KMOD_RGUI) - modifier_keys |= input::modifier_key::right_gui; - if (sdl_key_mod & KMOD_NUM) - modifier_keys |= input::modifier_key::num_lock; - if (sdl_key_mod & KMOD_CAPS) - modifier_keys |= input::modifier_key::caps_lock; - if (sdl_key_mod & KMOD_SCROLL) - modifier_keys |= input::modifier_key::scroll_lock; - if (sdl_key_mod & KMOD_MODE) - modifier_keys |= input::modifier_key::alt_gr; - } - - if (event.type == SDL_KEYDOWN) - { - keyboard.press(scancode, modifier_keys, (event.key.repeat > 0)); - } - else - { - keyboard.release(scancode, modifier_keys); - } - - break; - } - - case SDL_MOUSEWHEEL: - { - const float flip = (event.wheel.direction == SDL_MOUSEWHEEL_FLIPPED) ? -1.0f : 1.0f; - mouse.scroll({event.wheel.preciseX * flip, event.wheel.preciseY * flip}); - break; - } - - case SDL_MOUSEBUTTONDOWN: - { - mouse.press(static_cast(event.button.button)); - break; - } - - case SDL_MOUSEBUTTONUP: - { - mouse.release(static_cast(event.button.button)); - break; - } - - [[likely]] case SDL_CONTROLLERAXISMOTION: - { - if (event.caxis.axis != SDL_CONTROLLER_AXIS_INVALID) - { - if (auto it = gamepad_map.find(event.cdevice.which); it != gamepad_map.end()) - { - // Map axis position onto `[-1, 1]`. - const float position = math::map - ( - static_cast(event.caxis.value), - static_cast(std::numeric_limits::min()), - static_cast(std::numeric_limits::max()), - -1.0f, - 1.0f - ); - - // Generate gamepad axis moved event - it->second->move(static_cast(event.caxis.axis), position); - } - } - break; - } - - case SDL_CONTROLLERBUTTONDOWN: - { - if (event.cbutton.button != SDL_CONTROLLER_BUTTON_INVALID) - { - if (auto it = gamepad_map.find(event.cdevice.which); it != gamepad_map.end()) - { - it->second->press(static_cast(event.cbutton.button)); - } - } - break; - } - - case SDL_CONTROLLERBUTTONUP: - { - if (event.cbutton.button != SDL_CONTROLLER_BUTTON_INVALID) - { - if (auto it = gamepad_map.find(event.cdevice.which); it != gamepad_map.end()) - { - it->second->release(static_cast(event.cbutton.button)); - } - } - break; - } - - [[unlikely]] case SDL_CONTROLLERDEVICEADDED: - { - if (SDL_IsGameController(event.cdevice.which)) - { - SDL_GameController* sdl_controller = SDL_GameControllerOpen(event.cdevice.which); - if (sdl_controller) - { - // Get gamepad name - const char* controller_name = SDL_GameControllerNameForIndex(event.cdevice.which); - if (!controller_name) - { - controller_name = ""; - } - - if (auto it = gamepad_map.find(event.cdevice.which); it != gamepad_map.end()) - { - // Gamepad reconnected - debug::log::info("Reconnected gamepad {}", event.cdevice.which); - it->second->connect(); - } - else - { - // Get gamepad GUID - SDL_Joystick* sdl_joystick = SDL_GameControllerGetJoystick(sdl_controller); - SDL_JoystickGUID sdl_guid = SDL_JoystickGetGUID(sdl_joystick); - - // Copy into UUID struct - ::uuid gamepad_uuid; - std::memcpy(gamepad_uuid.data.data(), sdl_guid.data, gamepad_uuid.data.size()); - - debug::log::info("Connected gamepad {}; name: \"{}\"; UUID: {}", event.cdevice.which, controller_name, gamepad_uuid.string()); - - // Create new gamepad - input::gamepad* gamepad = new input::gamepad(); - gamepad->set_uuid(gamepad_uuid); - - // Add gamepad to gamepad map - gamepad_map[event.cdevice.which] = gamepad; - - // Register gamepad - register_device(*gamepad); - - // Generate gamepad connected event - gamepad->connect(); - } - } - else - { - debug::log::error("Failed to connect gamepad {}: {}", event.cdevice.which, SDL_GetError()); - SDL_ClearError(); - } - } - - break; - } - - [[unlikely]] case SDL_CONTROLLERDEVICEREMOVED: - { - SDL_GameController* sdl_controller = SDL_GameControllerFromInstanceID(event.cdevice.which); - - if (sdl_controller) - { - SDL_GameControllerClose(sdl_controller); - if (auto it = gamepad_map.find(event.cdevice.which); it != gamepad_map.end()) - { - it->second->disconnect(); - } - - debug::log::info("Disconnected gamepad {}", event.cdevice.which); - } - - break; - } - - default: - break; - } - } - - // Flush event queue - this->event_queue.flush(); -} - -void sdl_input_manager::show_cursor() -{ - if (SDL_ShowCursor(SDL_ENABLE) < 0) - { - debug::log::error("Failed to show cursor: \"{}\"", SDL_GetError()); - SDL_ClearError(); - } -} - -void sdl_input_manager::hide_cursor() -{ - if (SDL_ShowCursor(SDL_DISABLE) < 0) - { - debug::log::error("Failed to hide cursor: \"{}\"", SDL_GetError()); - SDL_ClearError(); - } -} - -} // namespace app diff --git a/src/app/sdl/sdl-input-manager.hpp b/src/app/sdl/sdl-input-manager.hpp deleted file mode 100644 index b4d4e00..0000000 --- a/src/app/sdl/sdl-input-manager.hpp +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_APP_SDL_INPUT_MANAGER_HPP -#define ANTKEEPER_APP_SDL_INPUT_MANAGER_HPP - -#include "app/input-manager.hpp" - -namespace app { - -class sdl_window; - -/** - * - */ -class sdl_input_manager: public input_manager -{ -public: - /** - * Constructs an SDL input manager. - */ - sdl_input_manager(); - - /** - * Destructs an SDL input manager. - */ - virtual ~sdl_input_manager(); - - virtual void update(); - virtual void show_cursor(); - virtual void hide_cursor(); - -private: - input::keyboard keyboard; - input::mouse mouse; - std::unordered_map gamepad_map; -}; - -} // namespace app - -#endif // ANTKEEPER_APP_SDL_INPUT_MANAGER_HPP diff --git a/src/app/sdl/sdl-window-manager.cpp b/src/app/sdl/sdl-window-manager.cpp deleted file mode 100644 index e122679..0000000 --- a/src/app/sdl/sdl-window-manager.cpp +++ /dev/null @@ -1,498 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "app/sdl/sdl-window-manager.hpp" -#include "app/sdl/sdl-window.hpp" -#include "debug/log.hpp" -#include "config.hpp" -#include - -namespace app { - -sdl_window_manager::sdl_window_manager() -{ - // Init SDL events and video subsystems - debug::log::trace("Initializing SDL events and video subsystems..."); - if (SDL_InitSubSystem(SDL_INIT_EVENTS | SDL_INIT_VIDEO) != 0) - { - debug::log::fatal("Failed to initialize SDL events and video subsystems: {}", SDL_GetError()); - throw std::runtime_error("Failed to initialize SDL events and video subsystems"); - } - debug::log::trace("Initialized SDL events and video subsystems"); - - // Query displays - const int display_count = SDL_GetNumVideoDisplays(); - if (display_count < 1) - { - debug::log::warning("No displays detected: {}", SDL_GetError()); - } - else - { - // Allocate displays - displays.resize(display_count); - debug::log::info("Display count: {}", display_count); - - for (int i = 0; i < display_count; ++i) - { - // Update display state - update_display(i); - - // Log display information - display& display = displays[i]; - const auto display_resolution = display.get_bounds().size(); - debug::log::info("Display {} name: \"{}\"; resolution: {}x{}; refresh rate: {}Hz; DPI: {}", i, display.get_name(), display_resolution.x(), display_resolution.y(), display.get_refresh_rate(), display.get_dpi()); - } - } - - // Load OpenGL library - debug::log::trace("Loading OpenGL library..."); - if (SDL_GL_LoadLibrary(nullptr) != 0) - { - debug::log::fatal("Failed to load OpenGL library: {}", SDL_GetError()); - throw std::runtime_error("Failed to load OpenGL library"); - } - debug::log::trace("Loaded OpenGL library"); - - // Set OpenGL-related window creation hints - SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1); - SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, config::opengl_version_major); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, config::opengl_version_minor); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); - SDL_GL_SetAttribute(SDL_GL_RED_SIZE, config::opengl_min_red_size); - SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, config::opengl_min_green_size); - SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, config::opengl_min_blue_size); - SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, config::opengl_min_alpha_size); - SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, config::opengl_min_depth_size); - SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, config::opengl_min_stencil_size); -} - -sdl_window_manager::~sdl_window_manager() -{ - // Quit SDL video subsystem - debug::log::trace("Quitting SDL video subsystem..."); - SDL_QuitSubSystem(SDL_INIT_VIDEO); - debug::log::trace("Quit SDL video subsystem"); -} - -window* sdl_window_manager::create_window -( - const std::string& title, - const math::vector& windowed_position, - const math::vector& windowed_size, - bool maximized, - bool fullscreen, - bool v_sync -) -{ - // Create new window - app::sdl_window* window = new app::sdl_window - ( - title, - windowed_position, - windowed_size, - maximized, - fullscreen, - v_sync - ); - - // Map internal SDL window to window - window_map[window->internal_window] = window; - - return window; -} - -void sdl_window_manager::update() -{ - // Gather SDL events from event queue - SDL_PumpEvents(); - - for (;;) - { - // Get next window or display event - SDL_Event event; - int status = SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_DISPLAYEVENT, SDL_SYSWMEVENT); - - if (!status) - { - break; - } - else if (status < 0) - { - debug::log::error("Failed to peep SDL events: {}", SDL_GetError()); - throw std::runtime_error("Failed to peep SDL events"); - } - - // Handle event - if (event.type == SDL_WINDOWEVENT) - { - switch (event.window.event) - { - case SDL_WINDOWEVENT_SIZE_CHANGED: - { - // Get window - SDL_Window* internal_window = SDL_GetWindowFromID(event.window.windowID); - app::sdl_window* window = get_window(internal_window); - - // Update window state - window->size = {event.window.data1, event.window.data2}; - const auto window_flags = SDL_GetWindowFlags(internal_window); - if (!(window_flags & SDL_WINDOW_MAXIMIZED) && !(window_flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_FULLSCREEN_DESKTOP))) - { - window->windowed_size = window->size; - } - SDL_GL_GetDrawableSize(internal_window, &window->viewport_size.x(), &window->viewport_size.y()); - window->rasterizer->context_resized(window->viewport_size.x(), window->viewport_size.y()); - - // Log window resized event - debug::log::debug("Window {} resized to {}x{}", event.window.windowID, event.window.data1, event.window.data2); - - // Publish window resized event - window->resized_publisher.publish({window, window->size}); - break; - } - - case SDL_WINDOWEVENT_MOVED: - { - // Get window - SDL_Window* internal_window = SDL_GetWindowFromID(event.window.windowID); - app::sdl_window* window = get_window(internal_window); - - // Update window state - window->position = {event.window.data1, event.window.data2}; - const auto window_flags = SDL_GetWindowFlags(internal_window); - if (!(window_flags & SDL_WINDOW_MAXIMIZED) && !(window_flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_FULLSCREEN_DESKTOP))) - { - window->windowed_position = window->position; - } - - // Log window moved event - debug::log::debug("Window {} moved to ({}, {})", event.window.windowID, event.window.data1, event.window.data2); - - // Publish window moved event - window->moved_publisher.publish({window, window->position}); - break; - } - - case SDL_WINDOWEVENT_FOCUS_GAINED: - { - // Get window - SDL_Window* internal_window = SDL_GetWindowFromID(event.window.windowID); - app::sdl_window* window = get_window(internal_window); - - // Log window focus gained event - debug::log::debug("Window {} gained focus", event.window.windowID); - - // Publish window focus gained event - window->focus_changed_publisher.publish({window, true}); - break; - } - - case SDL_WINDOWEVENT_FOCUS_LOST: - { - // Get window - SDL_Window* internal_window = SDL_GetWindowFromID(event.window.windowID); - app::sdl_window* window = get_window(internal_window); - - // Log window focus lost event - debug::log::debug("Window {} lost focus", event.window.windowID); - - // Publish window focus lost event - window->focus_changed_publisher.publish({window, false}); - break; - } - - case SDL_WINDOWEVENT_MAXIMIZED: - { - // Get window - SDL_Window* internal_window = SDL_GetWindowFromID(event.window.windowID); - app::sdl_window* window = get_window(internal_window); - - // Update window state - window->maximized = true; - - // Log window focus gained event - debug::log::debug("Window {} maximized", event.window.windowID); - - // Publish window maximized event - window->maximized_publisher.publish({window}); - break; - } - - case SDL_WINDOWEVENT_RESTORED: - { - // Get window - SDL_Window* internal_window = SDL_GetWindowFromID(event.window.windowID); - app::sdl_window* window = get_window(internal_window); - - // Update window state - window->maximized = false; - - // Log window restored event - debug::log::debug("Window {} restored", event.window.windowID); - - // Publish window restored event - window->restored_publisher.publish({window}); - break; - } - - case SDL_WINDOWEVENT_MINIMIZED: - { - // Get window - SDL_Window* internal_window = SDL_GetWindowFromID(event.window.windowID); - app::sdl_window* window = get_window(internal_window); - - // Log window focus gained event - debug::log::debug("Window {} minimized", event.window.windowID); - - // Publish window minimized event - window->minimized_publisher.publish({window}); - break; - } - - [[unlikely]] case SDL_WINDOWEVENT_CLOSE: - { - // Get window - SDL_Window* internal_window = SDL_GetWindowFromID(event.window.windowID); - app::sdl_window* window = get_window(internal_window); - - // Log window closed event - debug::log::debug("Window {} closed", event.window.windowID); - - // Publish window closed event - window->closed_publisher.publish({window}); - break; - } - - default: - break; - } - } - else if (event.type == SDL_DISPLAYEVENT) - { - switch (event.display.event) - { - case SDL_DISPLAYEVENT_CONNECTED: - if (event.display.display < displays.size()) - { - // Get previously connected display - display& display = displays[event.display.display]; - - // Update display state - display.connected = true; - - // Log display (re)connected event - debug::log::info("Reconnected display {}", event.display.display); - - // Publish display (re)connected event - display.connected_publisher.publish({&display}); - } - else if (event.display.display == displays.size()) - { - // Allocate new display - displays.resize(displays.size() + 1); - display& display = displays[event.display.display]; - - // Update display state - update_display(event.display.display); - - // Log display info - const auto display_resolution = display.get_bounds().size(); - debug::log::info("Connected display {}; name: \"{}\"; resolution: {}x{}; refresh rate: {}Hz; DPI: {}", event.display.display, display.get_name(), display_resolution.x(), display_resolution.y(), display.get_refresh_rate(), display.get_dpi()); - - // Publish display connected event - display.connected_publisher.publish({&display}); - } - else - { - debug::log::error("Index of connected display ({}) out of range", event.display.display); - } - break; - - case SDL_DISPLAYEVENT_DISCONNECTED: - if (event.display.display < displays.size()) - { - // Get display - display& display = displays[event.display.display]; - - // Update display state - display.connected = false; - - // Log display disconnected event - debug::log::info("Disconnected display {}", event.display.display); - - // Publish display disconnected event - display.disconnected_publisher.publish({&display}); - } - else - { - debug::log::error("Index of disconnected display ({}) out of range", event.display.display); - } - break; - - case SDL_DISPLAYEVENT_ORIENTATION: - if (event.display.display < displays.size()) - { - // Get display - display& display = displays[event.display.display]; - - // Update display state - switch (event.display.data1) - { - case SDL_ORIENTATION_LANDSCAPE: - display.set_orientation(display_orientation::landscape); - break; - case SDL_ORIENTATION_LANDSCAPE_FLIPPED: - display.set_orientation(display_orientation::landscape_flipped); - break; - case SDL_ORIENTATION_PORTRAIT: - display.set_orientation(display_orientation::portrait); - break; - case SDL_ORIENTATION_PORTRAIT_FLIPPED: - display.set_orientation(display_orientation::portrait_flipped); - break; - default: - display.set_orientation(display_orientation::unknown); - break; - } - - // Log display orientation changed event - debug::log::info("Display {} orientation changed", event.display.display); - - // Publish display orientation changed event - display.orientation_changed_publisher.publish({&display, display.get_orientation()}); - } - else - { - debug::log::error("Index of orientation-changed display ({}) out of range", event.display.display); - } - break; - - default: - break; - } - } - } -} - -sdl_window* sdl_window_manager::get_window(SDL_Window* internal_window) -{ - sdl_window* window = nullptr; - if (auto i = window_map.find(internal_window); i != window_map.end()) - { - window = i->second; - } - else - { - throw std::runtime_error("SDL window unrecognized by SDL window manager"); - } - return window; -} - -std::size_t sdl_window_manager::get_display_count() const -{ - return displays.size(); -} - -const display& sdl_window_manager::get_display(std::size_t index) const -{ - return displays[index]; -} - -void sdl_window_manager::update_display(int sdl_display_index) -{ - // Query display mode - SDL_DisplayMode sdl_display_mode; - if (SDL_GetDesktopDisplayMode(sdl_display_index, &sdl_display_mode) != 0) - { - debug::log::error("Failed to get mode of display {}: {}", sdl_display_index, SDL_GetError()); - SDL_ClearError(); - sdl_display_mode = {0, 0, 0, 0, nullptr}; - } - - // Query display name - const char* sdl_display_name = SDL_GetDisplayName(sdl_display_index); - if (!sdl_display_name) - { - debug::log::warning("Failed to get name of display {}: {}", sdl_display_index, SDL_GetError()); - SDL_ClearError(); - sdl_display_name = nullptr; - } - - // Query display bounds - SDL_Rect sdl_display_bounds; - if (SDL_GetDisplayBounds(sdl_display_index, &sdl_display_bounds) != 0) - { - debug::log::warning("Failed to get bounds of display {}: {}", sdl_display_index, SDL_GetError()); - SDL_ClearError(); - sdl_display_bounds = {0, 0, sdl_display_mode.w, sdl_display_mode.h}; - } - - // Query display usable bounds - SDL_Rect sdl_display_usable_bounds; - if (SDL_GetDisplayUsableBounds(sdl_display_index, &sdl_display_usable_bounds) != 0) - { - debug::log::warning("Failed to get usable bounds of display {}: {}", sdl_display_index, SDL_GetError()); - SDL_ClearError(); - sdl_display_usable_bounds = sdl_display_bounds; - } - - // Query display DPI - float sdl_display_dpi; - if (SDL_GetDisplayDPI(sdl_display_index, &sdl_display_dpi, nullptr, nullptr) != 0) - { - debug::log::warning("Failed to get DPI of display {}: {}", sdl_display_index, SDL_GetError()); - SDL_ClearError(); - sdl_display_dpi = 0.0f; - } - - // Query display orientation - SDL_DisplayOrientation sdl_display_orientation = SDL_GetDisplayOrientation(sdl_display_index); - - // Update display properties - display& display = displays[sdl_display_index]; - display.set_index(static_cast(sdl_display_index)); - display.set_name(sdl_display_name ? sdl_display_name : std::string()); - display.set_bounds({{sdl_display_bounds.x, sdl_display_bounds.y}, {sdl_display_bounds.x + sdl_display_bounds.w, sdl_display_bounds.y + sdl_display_bounds.h}}); - display.set_usable_bounds({{sdl_display_usable_bounds.x, sdl_display_usable_bounds.y}, {sdl_display_usable_bounds.x + sdl_display_usable_bounds.w, sdl_display_usable_bounds.y + sdl_display_usable_bounds.h}}); - display.set_refresh_rate(sdl_display_mode.refresh_rate); - display.set_dpi(sdl_display_dpi); - switch (sdl_display_orientation) - { - case SDL_ORIENTATION_LANDSCAPE: - display.set_orientation(display_orientation::landscape); - break; - case SDL_ORIENTATION_LANDSCAPE_FLIPPED: - display.set_orientation(display_orientation::landscape_flipped); - break; - case SDL_ORIENTATION_PORTRAIT: - display.set_orientation(display_orientation::portrait); - break; - case SDL_ORIENTATION_PORTRAIT_FLIPPED: - display.set_orientation(display_orientation::portrait_flipped); - break; - default: - display.set_orientation(display_orientation::unknown); - break; - } - display.connected = true; -} - -} // namespace app diff --git a/src/app/sdl/sdl-window-manager.hpp b/src/app/sdl/sdl-window-manager.hpp deleted file mode 100644 index 3b10875..0000000 --- a/src/app/sdl/sdl-window-manager.hpp +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_APP_SDL_WINDOW_MANAGER_HPP -#define ANTKEEPER_APP_SDL_WINDOW_MANAGER_HPP - -#include "app/window-manager.hpp" -#include "app/display.hpp" -#include -#include -#include - -namespace app { - -class sdl_window; - -/** - * - */ -class sdl_window_manager: public window_manager -{ -public: - /** - * Constructs an SDL window manager. - */ - sdl_window_manager(); - - /** - * Destructs an SDL window manager. - */ - virtual ~sdl_window_manager(); - - virtual void update(); - - /// @copydoc window::window() - [[nodiscard]] virtual window* create_window - ( - const std::string& title, - const math::vector& windowed_position, - const math::vector& windowed_size, - bool maximized, - bool fullscreen, - bool v_sync - ); - - [[nodiscard]] virtual std::size_t get_display_count() const; - [[nodiscard]] virtual const display& get_display(std::size_t index) const; - -private: - sdl_window* get_window(SDL_Window* internal_window); - void update_display(int sdl_display_index); - - std::vector displays; - std::unordered_map window_map; -}; - -} // namespace app - -#endif // ANTKEEPER_APP_SDL_WINDOW_MANAGER_HPP diff --git a/src/app/sdl/sdl-window.cpp b/src/app/sdl/sdl-window.cpp deleted file mode 100644 index dd6c500..0000000 --- a/src/app/sdl/sdl-window.cpp +++ /dev/null @@ -1,296 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "app/sdl/sdl-window.hpp" -#include "config.hpp" -#include "debug/log.hpp" -#include -#include - -namespace app { - -sdl_window::sdl_window -( - const std::string& title, - const math::vector& windowed_position, - const math::vector& windowed_size, - bool maximized, - bool fullscreen, - bool v_sync -) -{ - // Determine SDL window creation flags - Uint32 window_flags = SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE; - if (maximized) - { - window_flags |= SDL_WINDOW_MAXIMIZED; - } - if (fullscreen) - { - window_flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; - } - - // Create SDL window - debug::log::trace("Creating SDL window..."); - internal_window = SDL_CreateWindow - ( - title.c_str(), - windowed_position.x(), - windowed_position.y(), - windowed_size.x(), - windowed_size.y(), - window_flags - ); - if (!internal_window) - { - debug::log::fatal("Failed to create SDL window: {}", SDL_GetError()); - throw std::runtime_error("Failed to create SDL window"); - } - debug::log::trace("Created SDL window"); - - // Create OpenGL context - debug::log::trace("Creating OpenGL context..."); - internal_context = SDL_GL_CreateContext(internal_window); - if (!internal_context) - { - debug::log::fatal("Failed to create OpenGL context: {}", SDL_GetError()); - throw std::runtime_error("Failed to create OpenGL context"); - } - debug::log::trace("Created OpenGL context"); - - // Query OpenGL context info - int opengl_context_version_major = -1; - int opengl_context_version_minor = -1; - int opengl_context_red_size = -1; - int opengl_context_green_size = -1; - int opengl_context_blue_size = -1; - int opengl_context_alpha_size = -1; - int opengl_context_depth_size = -1; - int opengl_context_stencil_size = -1; - SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &opengl_context_version_major); - SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &opengl_context_version_minor); - SDL_GL_GetAttribute(SDL_GL_RED_SIZE, &opengl_context_red_size); - SDL_GL_GetAttribute(SDL_GL_GREEN_SIZE, &opengl_context_green_size); - SDL_GL_GetAttribute(SDL_GL_BLUE_SIZE, &opengl_context_blue_size); - SDL_GL_GetAttribute(SDL_GL_ALPHA_SIZE, &opengl_context_alpha_size); - SDL_GL_GetAttribute(SDL_GL_DEPTH_SIZE, &opengl_context_depth_size); - SDL_GL_GetAttribute(SDL_GL_STENCIL_SIZE, &opengl_context_stencil_size); - - // Log OpenGL context info - debug::log::info - ( - "OpenGL context version: {}.{}; format: R{}G{}B{}A{}D{}S{}", - opengl_context_version_major, - opengl_context_version_minor, - opengl_context_red_size, - opengl_context_green_size, - opengl_context_blue_size, - opengl_context_alpha_size, - opengl_context_depth_size, - opengl_context_stencil_size - ); - - // Compare OpenGL context version with requested version - if (opengl_context_version_major != config::opengl_version_major || - opengl_context_version_minor != config::opengl_version_minor) - { - debug::log::warning("Requested OpenGL context version {}.{} but got version {}.{}", config::opengl_version_major, config::opengl_version_minor, opengl_context_version_major, opengl_context_version_minor); - } - - // Compare OpenGL context format with requested format - if (opengl_context_red_size < config::opengl_min_red_size || - opengl_context_green_size < config::opengl_min_green_size || - opengl_context_blue_size < config::opengl_min_blue_size || - opengl_context_alpha_size < config::opengl_min_alpha_size || - opengl_context_depth_size < config::opengl_min_depth_size || - opengl_context_stencil_size < config::opengl_min_stencil_size) - { - debug::log::warning - ( - "OpenGL context format (R{}G{}B{}A{}D{}S{}) does not meet minimum requested format (R{}G{}B{}A{}D{}S{})", - opengl_context_red_size, - opengl_context_green_size, - opengl_context_blue_size, - opengl_context_alpha_size, - opengl_context_depth_size, - opengl_context_stencil_size, - config::opengl_min_red_size, - config::opengl_min_green_size, - config::opengl_min_blue_size, - config::opengl_min_alpha_size, - config::opengl_min_depth_size, - config::opengl_min_stencil_size - ); - } - - // Load OpenGL functions via GLAD - debug::log::trace("Loading OpenGL functions..."); - if (!gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress)) - { - debug::log::fatal("Failed to load OpenGL functions", SDL_GetError()); - throw std::runtime_error("Failed to load OpenGL functions"); - } - debug::log::trace("Loaded OpenGL functions"); - - // Log OpenGL information - debug::log::info - ( - "OpenGL vendor: {}; renderer: {}; version: {}; shading language version: {}", - reinterpret_cast(glGetString(GL_VENDOR)), - reinterpret_cast(glGetString(GL_RENDERER)), - reinterpret_cast(glGetString(GL_VERSION)), - reinterpret_cast(glGetString(GL_SHADING_LANGUAGE_VERSION)) - ); - - // Fill window with color - //glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT); - swap_buffers(); - - // Enable or disable v-sync - set_v_sync(v_sync); - - // Update window state - this->title = title; - this->windowed_position = windowed_position; - this->windowed_size = windowed_size; - this->maximized = maximized; - this->fullscreen = fullscreen; - SDL_GetWindowPosition(internal_window, &this->position.x(), &this->position.y()); - SDL_GetWindowSize(internal_window, &this->size.x(), &this->size.y()); - SDL_GetWindowMinimumSize(internal_window, &this->minimum_size.x(), &this->minimum_size.y()); - SDL_GetWindowMaximumSize(internal_window, &this->maximum_size.x(), &this->maximum_size.y()); - SDL_GL_GetDrawableSize(internal_window, &this->viewport_size.x(), &this->viewport_size.y()); - - // Allocate rasterizer - this->rasterizer = new gl::rasterizer(); -} - -sdl_window::~sdl_window() -{ - // Destruct rasterizer - delete rasterizer; - - // Destruct the OpenGL context - SDL_GL_DeleteContext(internal_context); - - // Destruct the SDL window - SDL_DestroyWindow(internal_window); -} - -void sdl_window::set_title(const std::string& title) -{ - SDL_SetWindowTitle(internal_window, title.c_str()); - this->title = title; -} - -void sdl_window::set_position(const math::vector& position) -{ - SDL_SetWindowPosition(internal_window, position.x(), position.y()); -} - -void sdl_window::set_size(const math::vector& size) -{ - SDL_SetWindowSize(internal_window, size.x(), size.y()); -} - -void sdl_window::set_minimum_size(const math::vector& size) -{ - SDL_SetWindowMinimumSize(internal_window, size.x(), size.y()); - this->minimum_size = size; -} - -void sdl_window::set_maximum_size(const math::vector& size) -{ - SDL_SetWindowMaximumSize(internal_window, size.x(), size.y()); - this->maximum_size = size; -} - -void sdl_window::set_maximized(bool maximized) -{ - if (maximized) - { - SDL_MaximizeWindow(internal_window); - } - else - { - SDL_RestoreWindow(internal_window); - } -} - -void sdl_window::set_fullscreen(bool fullscreen) -{ - //SDL_HideWindow(internal_window); - SDL_SetWindowFullscreen(internal_window, (fullscreen) ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0); - //SDL_ShowWindow(internal_window); - this->fullscreen = fullscreen; -} - -void sdl_window::set_v_sync(bool v_sync) -{ - if (v_sync) - { - debug::log::trace("Enabling adaptive v-sync..."); - if (SDL_GL_SetSwapInterval(-1) != 0) - { - debug::log::error("Failed to enable adaptive v-sync: {}", SDL_GetError()); - debug::log::trace("Enabling synchronized v-sync..."); - if (SDL_GL_SetSwapInterval(1) != 0) - { - debug::log::error("Failed to enable synchronized v-sync: {}", SDL_GetError()); - v_sync = false; - } - else - { - debug::log::debug("Enabled synchronized v-sync"); - } - } - else - { - debug::log::debug("Enabled adaptive v-sync"); - } - } - else - { - debug::log::trace("Disabling v-sync..."); - if (SDL_GL_SetSwapInterval(0) != 0) - { - debug::log::error("Failed to disable v-sync: {}", SDL_GetError()); - v_sync = true; - } - else - { - debug::log::debug("Disabled v-sync"); - } - } - - this->v_sync = v_sync; -} - -void sdl_window::make_current() -{ - SDL_GL_MakeCurrent(internal_window, internal_context); -} - -void sdl_window::swap_buffers() -{ - SDL_GL_SwapWindow(internal_window); -} - -} // namespace app diff --git a/src/app/sdl/sdl-window.hpp b/src/app/sdl/sdl-window.hpp deleted file mode 100644 index 909e830..0000000 --- a/src/app/sdl/sdl-window.hpp +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_APP_SDL_WINDOW_HPP -#define ANTKEEPER_APP_SDL_WINDOW_HPP - -#include "app/window.hpp" -#include - -namespace app { - -/** - * - */ -class sdl_window: public window -{ -public: - virtual ~sdl_window(); - virtual void set_title(const std::string& title); - virtual void set_position(const math::vector& position); - virtual void set_size(const math::vector& size); - virtual void set_minimum_size(const math::vector& size); - virtual void set_maximum_size(const math::vector& size); - virtual void set_maximized(bool maximized); - virtual void set_fullscreen(bool fullscreen); - virtual void set_v_sync(bool v_sync); - virtual void make_current(); - virtual void swap_buffers(); - - [[nodiscard]] inline virtual gl::rasterizer* get_rasterizer() noexcept - { - return rasterizer; - } - -private: - friend class sdl_window_manager; - - sdl_window - ( - const std::string& title, - const math::vector& windowed_position, - const math::vector& windowed_size, - bool maximized, - bool fullscreen, - bool v_sync - ); - - sdl_window(const sdl_window&) = delete; - sdl_window(sdl_window&&) = delete; - sdl_window& operator=(const sdl_window&) = delete; - sdl_window& operator=(sdl_window&&) = delete; - - SDL_Window* internal_window; - SDL_GLContext internal_context; - gl::rasterizer* rasterizer; -}; - -} // namespace app - -#endif // ANTKEEPER_APP_SDL_WINDOW_HPP diff --git a/src/app/window-events.hpp b/src/app/window-events.hpp deleted file mode 100644 index 5205041..0000000 --- a/src/app/window-events.hpp +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_APP_WINDOW_EVENTS_HPP -#define ANTKEEPER_APP_WINDOW_EVENTS_HPP - -#include "math/vector.hpp" - -namespace app { - -class window; - -/** - * Event generated when a window has been requested to close. - */ -struct window_closed_event -{ - /// Pointer to the window that has been requested to close. - window* window; -}; - -/** - * Event generated when a window has gained or lost focus. - */ -struct window_focus_changed_event -{ - /// Pointer to the window that has gained or lost focus. - window* window; - - /// `true` if the window is in focus, `false` otherwise. - bool in_focus; -}; - -/** - * Event generated when a window has been moved. - */ -struct window_moved_event -{ - /// Pointer to the window that has been moved. - window* window; - - /// Position of the window, in display units. - math::vector position; -}; - -/** - * Event generated when a window has been maximized. - */ -struct window_maximized_event -{ - /// Pointer to the window that has been maximized. - window* window; -}; - -/** - * Event generated when a window has been minimized. - */ -struct window_minimized_event -{ - /// Pointer to the window that has been minimized. - window* window; -}; - -/** - * Event generated when a window has been restored. - */ -struct window_restored_event -{ - /// Pointer to the window that has been restored. - window* window; -}; - -/** - * Event generated when a window has been resized. - */ -struct window_resized_event -{ - /// Pointer to the window that has been resized. - window* window; - - /// Window size, in display units. - math::vector size; -}; - -} // namespace app - -#endif // ANTKEEPER_APP_WINDOW_EVENTS_HPP diff --git a/src/app/window-manager.cpp b/src/app/window-manager.cpp deleted file mode 100644 index fa4040e..0000000 --- a/src/app/window-manager.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "app/window-manager.hpp" -#include "app/sdl/sdl-window-manager.hpp" - -namespace app { - -window_manager* window_manager::instance() -{ - return new sdl_window_manager(); -} - -} // namespace app diff --git a/src/app/window-manager.hpp b/src/app/window-manager.hpp deleted file mode 100644 index 012b830..0000000 --- a/src/app/window-manager.hpp +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_APP_WINDOW_MANAGER_HPP -#define ANTKEEPER_APP_WINDOW_MANAGER_HPP - -#include "app/display.hpp" -#include "app/window.hpp" -#include "math/vector.hpp" -#include - -namespace app { - -/** - * - */ -class window_manager -{ -public: - /** - * Allocates and returns a window manager. - */ - static window_manager* instance(); - - /// Destructs a window manager. - virtual ~window_manager() = default; - - /** - * Updates all managed windows. This should be called once per frame. - */ - virtual void update() = 0; - - /** - * Constructs a window. - * - * @param title Title of the window. - * @param windowed_position Windowed (non-maximized, non-fullscreen) position of the window, in display units. - * @param windowed_size Windowed (non-maximized, non-fullscreen) size of the window, in display units. - * @param maximized `true` if the window should start maximized, `false` otherwise. - * @param fullscreen `true` if the window should start fullscreen, `false` otherwise. - * @param v_sync `true` if v-sync should be enabled, `false` otherwise. - */ - [[nodiscard]] virtual window* create_window - ( - const std::string& title, - const math::vector& windowed_position, - const math::vector& windowed_size, - bool maximized, - bool fullscreen, - bool v_sync - ) = 0; - - /// Returns the number of available displays. - [[nodiscard]] virtual std::size_t get_display_count() const = 0; - - /** - * Returns the display with the given index. - * - * @param index Index of a display. - * - * @return Display with the given index. - */ - [[nodiscard]] virtual const display& get_display(std::size_t index) const = 0; -}; - -} // namespace app - -#endif // ANTKEEPER_APP_WINDOW_MANAGER_HPP diff --git a/src/app/window.cpp b/src/app/window.cpp deleted file mode 100644 index 4ac8546..0000000 --- a/src/app/window.cpp +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "app/window.hpp" - -namespace app { - - - -} // namespace app diff --git a/src/app/window.hpp b/src/app/window.hpp deleted file mode 100644 index b007e1f..0000000 --- a/src/app/window.hpp +++ /dev/null @@ -1,252 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_APP_WINDOW_HPP -#define ANTKEEPER_APP_WINDOW_HPP - -#include "math/vector.hpp" -#include "event/publisher.hpp" -#include "app/window-events.hpp" -#include "gl/rasterizer.hpp" -#include - -namespace app { - -class window_manager; - -/** - * - */ -class window -{ -public: - /** - * Constructs a window. - */ - window() = default; - - /** - * Destructs a window. - */ - virtual ~window() = default; - - /** - * Changes the title of the window. - * - * @param title Window title. - */ - virtual void set_title(const std::string& title) = 0; - - /** - * Changes the position of the window. - * - * @param position Position of the window, in display units. - */ - virtual void set_position(const math::vector& position) = 0; - - /** - * Changes the size of the window. - * - * @param size Size of the window, in display units. - */ - virtual void set_size(const math::vector& size) = 0; - - /** - * Sets the minimum size of the window. - * - * @param size Minimum size of the window, in display units. - */ - virtual void set_minimum_size(const math::vector& size) = 0; - - /** - * Sets the maximum size of the window. - * - * @param size Maximum size of the window, in display units. - */ - virtual void set_maximum_size(const math::vector& size) = 0; - - /** - * Maximizes or unmaximizes the window. - * - * @param maximized `true` if the window should be maximized, `false` otherwise. - */ - virtual void set_maximized(bool maximized) = 0; - - /** - * Enables or disables fullscreen mode. - * - * @param fullscreen `true` if the window should be in fullscreen mode, `false` otherwise. - */ - virtual void set_fullscreen(bool fullscreen) = 0; - - /** - * Enables or disables v-sync. - * - * @param v_sync `true` if the v-sync should be enabled, `false` otherwise. - */ - virtual void set_v_sync(bool v_sync) = 0; - - /** - * Makes the window's graphics context current. - */ - virtual void make_current() = 0; - - /** - * Swaps the front and back buffers of the window's graphics context. - */ - virtual void swap_buffers() = 0; - - /// Returns the title of the window. - [[nodiscard]] inline const std::string& get_title() const noexcept - { - return title; - } - - /// Returns the windowed (non-maximized, non-fullscreen) position of the window, in display units. - [[nodiscard]] inline const math::vector& get_windowed_position() const noexcept - { - return windowed_position; - } - - /// Returns the current position of the window, in display units. - [[nodiscard]] inline const math::vector& get_position() const noexcept - { - return position; - } - - /// Returns the windowed (non-maximized, non-fullscreen) size of the window, in display units. - [[nodiscard]] inline const math::vector& get_windowed_size() const noexcept - { - return windowed_size; - } - - /// Returns the current size of the window, in display units. - [[nodiscard]] inline const math::vector& get_size() const noexcept - { - return size; - } - - /// Returns the minimum size of the window, in display units. - [[nodiscard]] inline const math::vector& get_minimum_size() const noexcept - { - return minimum_size; - } - - /// Returns the maximum size of the window, in display units. - [[nodiscard]] inline const math::vector& get_maximum_size() const noexcept - { - return minimum_size; - } - - /// Returns the current size of the window's drawable viewport, in pixels. - [[nodiscard]] inline const math::vector& get_viewport_size() const noexcept - { - return viewport_size; - } - - /// Returns `true` if the window is maximized, `false` otherwise. - [[nodiscard]] inline bool is_maximized() const noexcept - { - return maximized; - } - - /// Returns `true` if the window is in fullscreen mode, `false` otherwise. - [[nodiscard]] inline bool is_fullscreen() const noexcept - { - return fullscreen; - } - - /// Returns `true` if the v-sync is enabled, `false` otherwise. - [[nodiscard]] inline bool get_v_sync() const noexcept - { - return v_sync; - } - - /// Returns the rasterizer associated with this window. - [[nodiscard]] virtual gl::rasterizer* get_rasterizer() noexcept = 0; - - /// Returns the channel through which window closed events are published. - [[nodiscard]] inline event::channel& get_closed_channel() noexcept - { - return closed_publisher.channel(); - } - - /// Returns the channel through which window focus changed events are published. - [[nodiscard]] inline event::channel& get_focus_changed_channel() noexcept - { - return focus_changed_publisher.channel(); - } - - /// Returns the channel through which window maximized events are published. - [[nodiscard]] inline event::channel& get_maximized_channel() noexcept - { - return maximized_publisher.channel(); - } - - /// Returns the channel through which window minimized events are published. - [[nodiscard]] inline event::channel& get_minimized_channel() noexcept - { - return minimized_publisher.channel(); - } - - /// Returns the channel through which window moved events are published. - [[nodiscard]] inline event::channel& get_moved_channel() noexcept - { - return moved_publisher.channel(); - } - - /// Returns the channel through which window resized events are published. - [[nodiscard]] inline event::channel& get_resized_channel() noexcept - { - return resized_publisher.channel(); - } - - /// Returns the channel through which window restored events are published. - [[nodiscard]] inline event::channel& get_restored_channel() noexcept - { - return restored_publisher.channel(); - } - -protected: - friend class window_manager; - - std::string title; - math::vector windowed_position; - math::vector position; - math::vector windowed_size; - math::vector size; - math::vector minimum_size; - math::vector maximum_size; - math::vector viewport_size; - bool maximized; - bool fullscreen; - bool v_sync; - - event::publisher closed_publisher; - event::publisher focus_changed_publisher; - event::publisher maximized_publisher; - event::publisher minimized_publisher; - event::publisher moved_publisher; - event::publisher resized_publisher; - event::publisher restored_publisher; -}; - -} // namespace app - -#endif // ANTKEEPER_APP_WINDOW_HPP diff --git a/src/color/aces.hpp b/src/color/aces.hpp deleted file mode 100644 index 4683659..0000000 --- a/src/color/aces.hpp +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_COLOR_ACES_HPP -#define ANTKEEPER_COLOR_ACES_HPP - -#include "color/rgb.hpp" -#include "math/matrix.hpp" -#include "math/vector.hpp" - -namespace color { - -/// ACES color spaces. -namespace aces { - -/// CIE xy chromaticity coordinates of the ACES white point (~D60). -template -constexpr math::vector2 white_point = {T{0.32168}, T{0.33767}}; - -/// ACES AP0 color space. -template -constexpr rgb::color_space ap0 -( - {T{0.7347}, T{ 0.2653}}, - {T{0.0000}, T{ 1.0000}}, - {T{0.0001}, T{-0.0770}}, - aces::white_point, - nullptr, - nullptr -); - -/// ACES AP1 color space. -template -constexpr rgb::color_space ap1 -( - {T{0.713}, T{0.293}}, - {T{0.165}, T{0.830}}, - {T{0.128}, T{0.044}}, - aces::white_point, - nullptr, - nullptr -); - -/** - * Constructs a saturation adjustment matrix. - * - * @param s Saturation adjustment factor. - * @param to_y Color space to CIE XYZ luminance vector. - * - * @return Saturation adjustment matrix. - */ -template -constexpr math::matrix adjust_saturation(T s, const math::vector3& to_y) -{ - const math::vector3 v = to_y * (T{1} - s); - return math::matrix - { - v[0] + s, v[0], v[0], - v[1], v[1] + s, v[1], - v[2], v[2], v[2] + s - }; -} - -/// ACES AP1 RRT saturation adjustment matrix. -template -constexpr math::matrix ap1_rrt_sat = aces::adjust_saturation(T{0.96}, ap1.to_y); - -/// ACES AP1 ODT saturation adjustment matrix. -template -constexpr math::matrix ap1_odt_sat = aces::adjust_saturation(T{0.93}, ap1.to_y); - -} // namespace aces - -} // namespace color - -#endif // ANTKEEPER_COLOR_ACES_HPP diff --git a/src/color/cat.hpp b/src/color/cat.hpp deleted file mode 100644 index de45f30..0000000 --- a/src/color/cat.hpp +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_COLOR_CAT_HPP -#define ANTKEEPER_COLOR_CAT_HPP - -#include "math/matrix.hpp" -#include "math/vector.hpp" - -namespace color { - -/// Chromatic adaption transforms (CAT). -namespace cat { - -/** - * Bradford cone response matrix. - * - * @see Specification ICC.1:2010 (Profile version 4.3.0.0). Image technology colour management — Architecture, profile format, and data structure, Annex E.3, pp. 102. - * @see http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html - */ -template -constexpr math::matrix bradford = -{ - 0.8951, -0.7502, 0.0389, - 0.2664, 1.7135, -0.0685, - -0.1614, 0.0367, 1.0296 -}; - -/** - * von Kries cone response matrix. - * - * @see http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html - */ -template -constexpr math::matrix von_kries = -{ - 0.40024, -0.22630, 0.00000, - 0.70760, 1.16532, 0.00000, - -0.08081, 0.04570, 0.91822 -}; - -/** - * XYZ scaling cone response matrix. - * - * @see http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html - */ -template -constexpr math::matrix xyz_scaling = -{ - T{1}, T{0}, T{0}, - T{0}, T{1}, T{0}, - T{0}, T{0}, T{1} -}; - -/** - * Constructs a chromatic adaptation transform (CAT) matrix. - * - * @param w0 CIE xy chromaticity coordinates of the source illuminant. - * @param w1 CIE xy chromaticity coordinates of the destination illuminant. - * @param cone_response Cone response matrix. - * - * @return CAT matrix. - * - * @see Specification ICC.1:2010 (Profile version 4.3.0.0). Image technology colour management — Architecture, profile format, and data structure, Annex E.3, pp. 102. - * @see http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html - */ -template -constexpr math::matrix matrix(const math::vector2& w0, const math::vector2& w1, const math::matrix& cone_response = bradford) -{ - // Convert CIE xy chromaticity coordinates to CIE XYZ colors - const math::vector3 w0_xyz = {w0[0] / w0[1], T{1}, (T{1} - w0[0] - w0[1]) / w0[1]}; - const math::vector3 w1_xyz = {w1[0] / w1[1], T{1}, (T{1} - w1[0] - w1[1]) / w1[1]}; - - // Calculate cone response of CIE XYZ colors - const math::vector3 w0_cone_response = cone_response * w0_xyz; - const math::vector3 w1_cone_response = cone_response * w1_xyz; - - const math::matrix scale = - { - w1_cone_response[0] / w0_cone_response[0], T{0}, T{0}, - T{0}, w1_cone_response[1] / w0_cone_response[1], T{0}, - T{0}, T{0}, w1_cone_response[2] / w0_cone_response[2], - }; - - return math::inverse(cone_response) * scale * cone_response; -} - -} // namespace cat -} // namespace color - -#endif // ANTKEEPER_COLOR_CAT_HPP diff --git a/src/color/cct.hpp b/src/color/cct.hpp deleted file mode 100644 index 9603858..0000000 --- a/src/color/cct.hpp +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_COLOR_CCT_HPP -#define ANTKEEPER_COLOR_CCT_HPP - -#include "color/ucs.hpp" -#include "color/xyy.hpp" -#include "math/vector.hpp" - -namespace color { - -/// Correlated color temperature (CCT). -namespace cct { - -/** - * Calculates CIE 1960 UCS colorspace chromaticity coordinates given a correlated color temperature using Krystek's algorithm. - * - * @param t Correlated color temperature, in Kelvin. - * @return CIE 1960 UCS colorspace chromaticity coordinates. - * - * @see Krystek, M. (1985), An algorithm to calculate correlated colour temperature. Color Res. Appl., 10: 38-40. - */ -template -math::vector2 to_ucs(T t) -{ - const T tt = t * t; - return math::vector2 - { - (T{0.860117757} + T{1.54118254e-4} * t + T{1.28641212e-7} * tt) / (T{1} + T{8.42420235e-4} * t + T{7.08145163e-7} * tt), - (T{0.317398726} + T{4.22806245e-5} * t + T{4.20481691e-8} * tt) / (T{1} - T{2.89741816e-5} * t + T{1.61456053e-7} * tt) - }; -} - -/** - * Calculates CIE xyY colorspace chromaticity coordinates given a correlated color temperature using Krystek's algorithm. - * - * @param t Correlated color temperature, in Kelvin. - * @return CIE xyY color with `Y = 1`. - */ -template -math::vector3 to_xyy(T t) -{ - return ucs::to_xyy(to_ucs(t), T{1}); -} - -/** - * Calculates CIE XYZ colorspace chromaticity coordinates given a correlated color temperature using Krystek's algorithm. - * - * @param t Correlated color temperature, in Kelvin. - * @return CIE XYZ color with `Y = 1`. - */ -template -math::vector3 to_xyz(T t) -{ - return xyy::to_xyz(to_xyy(t)); -} - -} // namespace cct -} // namespace color - -#endif // ANTKEEPER_COLOR_CCT_HPP diff --git a/src/color/color.hpp b/src/color/color.hpp deleted file mode 100644 index 2910ae1..0000000 --- a/src/color/color.hpp +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_COLOR_HPP -#define ANTKEEPER_COLOR_HPP - -/// Color manipulation. -namespace color {} - -#include "color/aces.hpp" -#include "color/cat.hpp" -#include "color/cct.hpp" -#include "color/illuminant.hpp" -#include "color/index.hpp" -#include "color/rgb.hpp" -#include "color/srgb.hpp" -#include "color/ucs.hpp" -#include "color/xyy.hpp" -#include "color/xyz.hpp" - -#endif // ANTKEEPER_COLOR_HPP diff --git a/src/color/illuminant.hpp b/src/color/illuminant.hpp deleted file mode 100644 index dfcbb12..0000000 --- a/src/color/illuminant.hpp +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_COLOR_ILLUMINANT_HPP -#define ANTKEEPER_COLOR_ILLUMINANT_HPP - -#include "math/vector.hpp" - -namespace color { - -/** - * CIE standard illuminants. - * - * @see https://en.wikipedia.org/wiki/Standard_illuminant - */ -namespace illuminant { - -/// CIE 1931 2 Degree Standard Observer illuminants. -namespace deg2 { - - template - constexpr math::vector2 a = {T{0.44757}, T{0.40745}}; - template - constexpr math::vector2 b = {T{0.34842}, T{0.35161}}; - template - constexpr math::vector2 c = {T{0.31006}, T{0.31616}}; - template - constexpr math::vector2 d50 = {T{0.34567}, T{0.35850}}; - template - constexpr math::vector2 d55 = {T{0.33242}, T{0.34743}}; - template - constexpr math::vector2 d65 = {T{0.31271}, T{0.32902}}; - template - constexpr math::vector2 d75 = {T{0.29902}, T{0.31485}}; - template - constexpr math::vector2 d93 = {T{0.28315}, T{0.29711}}; - template - constexpr math::vector2 e = {T{0.33333}, T{0.33333}}; - template - constexpr math::vector2 f1 = {T{0.31310}, T{0.33727}}; - template - constexpr math::vector2 f2 = {T{0.37208}, T{0.37529}}; - template - constexpr math::vector2 f3 = {T{0.40910}, T{0.39430}}; - template - constexpr math::vector2 f4 = {T{0.44018}, T{0.40329}}; - template - constexpr math::vector2 f5 = {T{0.31379}, T{0.34531}}; - template - constexpr math::vector2 f6 = {T{0.37790}, T{0.38835}}; - template - constexpr math::vector2 f7 = {T{0.31292}, T{0.32933}}; - template - constexpr math::vector2 f8 = {T{0.34588}, T{0.35875}}; - template - constexpr math::vector2 f9 = {T{0.37417}, T{0.37281}}; - template - constexpr math::vector2 f10 = {T{0.34609}, T{0.35986}}; - template - constexpr math::vector2 f11 = {T{0.38052}, T{0.37713}}; - template - constexpr math::vector2 f12 = {T{0.43695}, T{0.40441}}; - template - constexpr math::vector2 led_b1 = {T{0.4560}, T{0.4078}}; - template - constexpr math::vector2 led_b2 = {T{0.4357}, T{0.4012}}; - template - constexpr math::vector2 led_b3 = {T{0.3756}, T{0.3723}}; - template - constexpr math::vector2 led_b4 = {T{0.3422}, T{0.3502}}; - template - constexpr math::vector2 led_b5 = {T{0.3118}, T{0.3236}}; - template - constexpr math::vector2 led_bh1 = {T{0.4474}, T{0.4066}}; - template - constexpr math::vector2 led_rgb1 = {T{0.4557}, T{0.4211}}; - template - constexpr math::vector2 led_v1 = {T{0.4560}, T{0.4548}}; - template - constexpr math::vector2 led_v2 = {T{0.3781}, T{0.3775}}; - -} // deg2 - -/// CIE 1964 10 Degree Standard Observer illuminants. -namespace deg10 { - - template - constexpr math::vector2 a = {T{0.45117}, T{0.40594}}; - template - constexpr math::vector2 b = {T{0.34980}, T{0.35270}}; - template - constexpr math::vector2 c = {T{0.31039}, T{0.31905}}; - template - constexpr math::vector2 d50 = {T{0.34773}, T{0.35952}}; - template - constexpr math::vector2 d55 = {T{0.33411}, T{0.34877}}; - template - constexpr math::vector2 d65 = {T{0.31382}, T{0.33100}}; - template - constexpr math::vector2 d75 = {T{0.29968}, T{0.31740}}; - template - constexpr math::vector2 d93 = {T{0.28327}, T{0.30043}}; - template - constexpr math::vector2 e = {T{0.33333}, T{0.33333}}; - template - constexpr math::vector2 f1 = {T{0.31811}, T{0.33559}}; - template - constexpr math::vector2 f2 = {T{0.37925}, T{0.36733}}; - template - constexpr math::vector2 f3 = {T{0.41761}, T{0.38324}}; - template - constexpr math::vector2 f4 = {T{0.44920}, T{0.39074}}; - template - constexpr math::vector2 f5 = {T{0.31975}, T{0.34246}}; - template - constexpr math::vector2 f6 = {T{0.38660}, T{0.37847}}; - template - constexpr math::vector2 f7 = {T{0.31569}, T{0.32960}}; - template - constexpr math::vector2 f8 = {T{0.34902}, T{0.35939}}; - template - constexpr math::vector2 f9 = {T{0.37829}, T{0.37045}}; - template - constexpr math::vector2 f10 = {T{0.35090}, T{0.35444}}; - template - constexpr math::vector2 f11 = {T{0.38541}, T{0.37123}}; - template - constexpr math::vector2 f12 = {T{0.44256}, T{0.39717}}; - -} // namespace deg10 - -} // namespace illuminant - -} // namespace color - -#endif // ANTKEEPER_COLOR_ILLUMINANT_HPP diff --git a/src/color/rgb.hpp b/src/color/rgb.hpp deleted file mode 100644 index 7a69a48..0000000 --- a/src/color/rgb.hpp +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_COLOR_RGB_HPP -#define ANTKEEPER_COLOR_RGB_HPP - -#include "color/cat.hpp" -#include "math/matrix.hpp" -#include "math/vector.hpp" - -namespace color { - -/// RGB color spaces. -namespace rgb { - -/** - * Constructs a matrix which transforms an RGB color into a CIE XYZ color. - * - * @param r CIE xy chromaticity coordinates of the red primary. - * @param g CIE xy chromaticity coordinates of the green primary. - * @param b CIE xy chromaticity coordinates of the blue primary. - * @param w CIE xy chromaticity coordinates of the white point. - * - * @return Matrix which transforms an RGB color into a CIE XYZ color. - * - * @see https://www.ryanjuckett.com/rgb-color-space-conversion/ - * @see https://mina86.com/2019/srgb-xyz-matrix/ - */ -template -constexpr math::matrix to_xyz(const math::vector2& r, const math::vector2& g, const math::vector2& b, const math::vector2& w) -{ - const math::matrix m = - { - r[0], r[1], T{1} - (r[0] + r[1]), - g[0], g[1], T{1} - (g[0] + g[1]), - b[0], b[1], T{1} - (b[0] + b[1]) - }; - - const math::vector3 scale = math::inverse(m) * math::vector3{w[0] / w[1], T{1}, (T{1} - (w[0] + w[1])) / w[1]}; - - return math::matrix - { - m[0][0] * scale[0], m[0][1] * scale[0], m[0][2] * scale[0], - m[1][0] * scale[1], m[1][1] * scale[1], m[1][2] * scale[1], - m[2][0] * scale[2], m[2][1] * scale[2], m[2][2] * scale[2], - }; -} - -/** - * RGB color space. - */ -template -struct color_space -{ - /// Transfer function function pointer type. - typedef math::vector3 (*transfer_function_type)(const math::vector3&); - - /// CIE xy chromaticity coordinates of the red primary. - const math::vector2 r; - - /// CIE xy chromaticity coordinates of the green primary. - const math::vector2 g; - - /// CIE xy chromaticity coordinates of the blue primary. - const math::vector2 b; - - /// CIE xy chromaticity coordinates of the white point. - const math::vector2 w; - - /// Function pointer to the electro-optical transfer function. - const transfer_function_type eotf; - - /// Function pointer to the inverse electro-optical transfer function. - const transfer_function_type inverse_eotf; - - /// Matrix which transforms an RGB color to a CIE XYZ color. - const math::matrix3x3 to_xyz; - - /// Matrix which transforms a CIE XYZ color to an RGB color. - const math::matrix3x3 from_xyz; - - /// Vector which gives the luminance of an RGB color via dot product. - const math::vector3 to_y; - - /** - * Constructs an RGB color space. - * - * @param r CIE xy chromaticity coordinates of the red primary. - * @param g CIE xy chromaticity coordinates of the green primary. - * @param b CIE xy chromaticity coordinates of the blue primary. - * @param w CIE xy chromaticity coordinates of the white point. - */ - constexpr color_space(const math::vector2& r, const math::vector2& g, const math::vector2& b, const math::vector2& w, transfer_function_type eotf, transfer_function_type inverse_eotf); - - /** - * Measures the luminance of a linear RGB color. - * - * @param x Linear RGB color. - * @return return Luminance of @p x. - */ - constexpr T luminance(const math::vector3& x) const; -}; - -template -constexpr color_space::color_space(const math::vector2& r, const math::vector2& g, const math::vector2& b, const math::vector2& w, transfer_function_type eotf, transfer_function_type inverse_eotf): - r(r), - g(g), - b(b), - w(w), - eotf(eotf), - inverse_eotf(inverse_eotf), - to_xyz(color::rgb::to_xyz(r, g, b, w)), - from_xyz(math::inverse(to_xyz)), - to_y{to_xyz[0][1], to_xyz[1][1], to_xyz[2][1]} -{} - -template -constexpr T color_space::luminance(const math::vector3& x) const -{ - return math::dot(x, to_y); -} - -/** - * Constructs a matrix which transforms a color from one RGB color space to another RGB color space. - * - * @param s0 Source color space. - * @param s1 Destination color space. - * @param cone_response Chromatic adaptation transform cone response matrix. - * - * @return Color space transformation matrix. - */ -template -constexpr math::matrix3x3 to_rgb(const color_space& s0, const color_space& s1, const math::matrix3x3& cone_response = color::cat::bradford) -{ - return s1.from_xyz * color::cat::matrix(s0.w, s1.w, cone_response) * s0.to_xyz; -} - -} // namespace rgb -} // namespace color - -#endif // ANTKEEPER_COLOR_RGB_HPP diff --git a/src/color/srgb.hpp b/src/color/srgb.hpp deleted file mode 100644 index e2db636..0000000 --- a/src/color/srgb.hpp +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_COLOR_SRGB_HPP -#define ANTKEEPER_COLOR_SRGB_HPP - -#include "color/rgb.hpp" -#include "color/illuminant.hpp" -#include "math/vector.hpp" -#include - -namespace color { - -/** - * sRGB electro-optical transfer function (EOTF), also known as the sRGB decoding function. - * - * @param v sRGB electrical signal (gamma-encoded sRGB). - * - * @return Corresponding luminance of the signal (linear sRGB). - */ -template -math::vector3 srgb_eotf(const math::vector3& v) -{ - auto f = [](T x) -> T - { - return x < T{0.04045} ? x / T{12.92} : std::pow((x + T{0.055}) / T{1.055}, T{2.4}); - }; - - return math::vector3 - { - f(v[0]), - f(v[1]), - f(v[2]) - }; -} - -/** - * sRGB inverse electro-optical transfer function (EOTF), also known as the sRGB encoding function. - * - * @param l sRGB luminance (linear sRGB). - * - * @return Corresponding electrical signal (gamma-encoded sRGB). - */ -template -math::vector3 srgb_inverse_eotf(const math::vector3& l) -{ - auto f = [](T x) -> T - { - return x <= T{0.0031308} ? x * T{12.92} : std::pow(x, T{1} / T{2.4}) * T{1.055} - T{0.055}; - }; - - return math::vector3 - { - f(l[0]), - f(l[1]), - f(l[2]) - }; -} - -/// sRGB color space. -template -constexpr rgb::color_space srgb -( - {T{0.64}, T{0.33}}, - {T{0.30}, T{0.60}}, - {T{0.15}, T{0.06}}, - color::illuminant::deg2::d65, - &srgb_eotf, - &srgb_inverse_eotf -); - -} // namespace color - -#endif // ANTKEEPER_COLOR_SRGB_HPP diff --git a/src/color/ucs.hpp b/src/color/ucs.hpp deleted file mode 100644 index 1442aea..0000000 --- a/src/color/ucs.hpp +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_COLOR_UCS_HPP -#define ANTKEEPER_COLOR_UCS_HPP - -#include "math/vector.hpp" - -namespace color { - -/// CIE 1960 UCS color space. -namespace ucs { - -/** - * Transforms CIE 1960 UCS chromaticity coordinates into the CIE xyY colorspace. - * - * @param uv CIE 1960 UCS chromaticity coordinates. - * @param y Luminance or `Y` value of the resulting xyY color. - * @return CIE xyY color. - */ -template -constexpr math::vector3 to_xyy(const math::vector2& uv, T y = T{1}) -{ - const T d = T{1} / (T{2} * uv[0] - T{8} * uv[1] + T{4}); - return math::vector3{(T{3} * uv[0]) * d, (T{2} * uv[1]) * d, y}; -} - -} // namespace ucs -} // namespace color - -#endif // ANTKEEPER_COLOR_UCS_HPP diff --git a/src/color/xyy.hpp b/src/color/xyy.hpp deleted file mode 100644 index 6caaacc..0000000 --- a/src/color/xyy.hpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_COLOR_XYY_HPP -#define ANTKEEPER_COLOR_XYY_HPP - -#include "math/vector.hpp" - -namespace color { - -/// CIE xyY color space. -namespace xyy { - -/** - * Returns the luminance of a CIE xyY color. - * - * @param x CIE xyY color. - * @return return Luminance of @p x. - */ -template -inline constexpr T luminance(const math::vector3& x) -{ - return x[2]; -} - -/** - * Transforms a CIE xyY color into the CIE 1960 UCS colorspace. - * - * @param x CIE xyY color. - * @return CIE 1960 UCS color. - */ -template -constexpr math::vector2 to_ucs(const math::vector3& x) -{ - const T d = (T{1} / (T{-2} * x[0] + T{12} * x[1] + T{3})); - return math::vector2{(T{4} * x[0]) * d, (T{6} * x[1]) * d}; -} - -/** - * Transforms a CIE xyY color into the CIE XYZ colorspace. - * - * @param x CIE xyY color. - * @return CIE XYZ color. - */ -template -constexpr math::vector3 to_xyz(const math::vector3& x) -{ - return math::vector3{(x[0] * x[2]) / x[1], x[2], ((T{1} - x[0] - x[1]) * x[2]) / x[1]}; -} - -} // namespace xyy -} // namespace color - -#endif // ANTKEEPER_COLOR_XYY_HPP diff --git a/src/color/xyz.hpp b/src/color/xyz.hpp deleted file mode 100644 index c521db1..0000000 --- a/src/color/xyz.hpp +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_COLOR_XYZ_HPP -#define ANTKEEPER_COLOR_XYZ_HPP - -#include "math/vector.hpp" - -namespace color { - -/** - * CIE XYZ color space. - * - * @see https://en.wikipedia.org/wiki/CIE_1931_color_space - */ -namespace xyz { - -/** - * Returns the luminance of a CIE XYZ color. - * - * @param x CIE XYZ color. - * @return return Luminance of @p x. - */ -template -inline constexpr T luminance(const math::vector3& x) -{ - return x[1]; -} - -/** - * Transforms a CIE XYZ color into the CIE xyY color space. - * - * @param x CIE XYZ color. - * @return CIE xyY color. - */ -template -constexpr math::vector3 to_xyy(const math::vector3& x) -{ - const T sum = x[0] + x[1] + x[2]; - return math::vector3{x[0] / sum, x[1] / sum, x[1]}; -} - -/** - * CIE 1931 standard observer color matching function for the X tristimulus value. - * - * @param lambda Wavelength of light, in nanometers. - * @return Matching X tristimulus value. - * - * @see match(T) - */ -template -T match_x(T lambda) -{ - const T t0 = (lambda - T(442.0)) * ((lambda < T(442.0)) ? T(0.0624) : T(0.0374)); - const T t1 = (lambda - T(599.8)) * ((lambda < T(599.8)) ? T(0.0264) : T(0.0323)); - const T t2 = (lambda - T(501.1)) * ((lambda < T(501.1)) ? T(0.0490) : T(0.0382)); - - const T x0 = T( 0.362) * std::exp(T(-0.5) * t0 * t0); - const T x1 = T( 1.056) * std::exp(T(-0.5) * t1 * t1); - const T x2 = T(-0.065) * std::exp(T(-0.5) * t2 * t2); - - return x0 + x1 + x2; -} - -/** - * CIE 1931 standard observer color matching function for the Y tristimulus value. - * - * @param lambda Wavelength of light, in nanometers. - * @return Matching Y tristimulus value. - * - * @see match(T) - */ -template -T match_y(T lambda) -{ - const T t0 = (lambda - T(568.8)) * ((lambda < T(568.8)) ? T(0.0213) : T(0.0247)); - const T t1 = (lambda - T(530.9)) * ((lambda < T(530.9)) ? T(0.0613) : T(0.0322)); - - const T y0 = T(0.821) * std::exp(T(-0.5) * t0 * t0); - const T y1 = T(0.286) * std::exp(T(-0.5) * t1 * t1); - - return y0 + y1; -} - -/** - * CIE 1931 standard observer color matching function for the Z tristimulus value. - * - * @param lambda Wavelength of light, in nanometers. - * @return Matching Z tristimulus value. - * - * @see match(T) - */ -template -T match_z(T lambda) -{ - const T t0 = (lambda - T(437.0)) * ((lambda < T(437.0)) ? T(0.0845) : T(0.0278)); - const T t1 = (lambda - T(459.0)) * ((lambda < T(459.0)) ? T(0.0385) : T(0.0725)); - - const T z0 = T(1.217) * std::exp(T(-0.5) * t0 * t0); - const T z1 = T(0.681) * std::exp(T(-0.5) * t1 * t1); - - return z0 + z1; -} - -/** - * Fitted piecewise gaussian approximation to the CIE 1931 standard observer color matching function. - * - * @param lambda Wavelength of light, in nanometers. - * @return Matching CIE XYZ color. - * - * @see match_x(T) - * @see match_y(T) - * @see match_z(T) - * - * @see Wyman, C., Sloan, P.J., & Shirley, P. (2013). Simple Analytic Approximations to the CIE XYZ Color Matching Functions. - */ -template -math::vector3 match(T lambda) -{ - return math::vector3 - { - match_x(lambda), - match_y(lambda), - match_z(lambda) - }; -} - -} // namespace xyz -} // namespace color - -#endif // ANTKEEPER_COLOR_XYZ_HPP diff --git a/src/config.hpp.in b/src/config.hpp.in deleted file mode 100644 index 7f183ae..0000000 --- a/src/config.hpp.in +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_CONFIG_HPP -#define ANTKEEPER_CONFIG_HPP - -// Disable trace message logging on release builds -#if defined(NDEBUG) - #define ANTKEEPER_DEBUG_LOG_MIN_MESSAGE_SEVERITY 1 -#else - #define ANTKEEPER_DEBUG_LOG_MIN_MESSAGE_SEVERITY 0 -#endif - -#include "math/vector.hpp" - -/// Global configuration constants. -namespace config { - -/// @name Application config -/// @{ - -/// Application name string. -inline constexpr const char* application_name = "@APPLICATION_NAME@"; - -/// Application slug string. -inline constexpr const char* application_slug = "@APPLICATION_SLUG@"; - -/// Application major version number. -inline constexpr int application_version_major = @APPLICATION_VERSION_MAJOR@; - -/// Application minor version number. -inline constexpr int application_version_minor = @APPLICATION_VERSION_MINOR@; - -/// Application patch version number. -inline constexpr int application_version_patch = @APPLICATION_VERSION_PATCH@; - -/// Application version string ("`major.minor.patch`") -inline constexpr const char* application_version_string = "@APPLICATION_VERSION@"; - -/// @} - -/// @name Debug config -/// @{ - -/// Maximum number of debug logs to archive. -inline constexpr std::size_t debug_log_archive_capacity = 5; - -/// @} - -/// @name OpenGL config -/// @{ - -/// OpenGL major version number, used when creating OpenGL contexts. -inline constexpr int opengl_version_major = 3; - -/// OpenGL minor version number, used when creating OpenGL contexts. -inline constexpr int opengl_version_minor = 3; - -/// Minimum number of bits in the red channel of the color attachment of the OpenGL default framebuffer. -inline constexpr int opengl_min_red_size = 8; - -/// Minimum number of bits in the green channel of the color attachment of the OpenGL default framebuffer. -inline constexpr int opengl_min_green_size = 8; - -/// Minimum number of bits in the blue channel of the color attachment of the OpenGL default framebuffer. -inline constexpr int opengl_min_blue_size = 8; - -/// Minimum number of bits in the alpha channel of the color attachment of the OpenGL default framebuffer. -inline constexpr int opengl_min_alpha_size = 0; - -/// Minimum number of bits in the depth attachment, if any, of the OpenGL default framebuffer. -inline constexpr int opengl_min_depth_size = 0; - -/// Minimum number of bits in the stencil attachment, if any, of the OpenGL default framebuffer. -inline constexpr int opengl_min_stencil_size = 0; - -/// @} - -inline constexpr math::vector global_forward = {0.0f, 0.0f, -1.0f}; -inline constexpr math::vector global_up = {0.0f, 1.0f, 0.0f}; -inline constexpr math::vector global_right = {1.0f, 0.0f, 0.0f}; - -/// Duration of the menu fade in animation, in seconds. -inline constexpr float menu_fade_in_duration = 0.25f; - -/// Duration of the menu fade out animation, in seconds. -inline constexpr float menu_fade_out_duration = 0.125f; - -/// Padding of the a menu item mouseover bounds, as a percentage of the font size. -inline constexpr float menu_mouseover_padding = 0.1f; - -/// Opacity of the menu background. -inline constexpr float menu_bg_opacity = 2.0f / 4.0f; - -/// RGBA color of active menu items. -inline constexpr math::vector menu_active_color{1.0f, 1.0f, 1.0f, 1.0f}; - -/// RGBA color of inactive menu items. -inline constexpr math::vector menu_inactive_color{1.0f, 1.0f, 1.0f, 0.5f}; - -/// Duration of the title screen fade in, in seconds. -inline constexpr float title_fade_in_duration = 1.0f; - -/// Duration of the fade out when quitting the game or returning to the main menu, in seconds. -inline constexpr float quit_fade_out_duration = 0.5f; - -/// Duration of the fade out when a new colony is started, in seconds. -inline constexpr float new_colony_fade_out_duration = 1.0f; - -/// Duration of the nuptial flight fade in, in seconds. -inline constexpr float nuptial_flight_fade_in_duration = 5.0f; - -#define MATERIAL_PASS_MAX_AMBIENT_LIGHT_COUNT 1 -#define MATERIAL_PASS_MAX_POINT_LIGHT_COUNT 1 -#define MATERIAL_PASS_MAX_DIRECTIONAL_LIGHT_COUNT 3 -#define MATERIAL_PASS_MAX_SPOTLIGHT_COUNT 1 -#define MATERIAL_PASS_MAX_BONE_COUNT 64 -#define TERRAIN_PATCH_SIZE 200.0f -#define TERRAIN_PATCH_RESOLUTION 4 -#define VEGETATION_PATCH_RESOLUTION 1 - -} // namespace config - -#endif // ANTKEEPER_CONFIG_HPP diff --git a/src/debug/cli.cpp b/src/debug/cli.cpp deleted file mode 100644 index e7a9ee4..0000000 --- a/src/debug/cli.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "debug/cli.hpp" - -namespace debug { - -std::string cli::interpret(const std::string& line) const -{ - std::istringstream stream(line); - std::string command_name; - stream >> command_name; - - if (auto it = commands.find(command_name); it != commands.end()) - { - return it->second(line.substr(command_name.length() + 1)); - } - - return std::string(); -} - -void cli::unregister_command(const std::string& name) -{ - if (auto it = commands.find(name); it != commands.end()) - commands.erase(it); -} - -} // namespace debug diff --git a/src/debug/console.cpp b/src/debug/console.cpp deleted file mode 100644 index d5fef6d..0000000 --- a/src/debug/console.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "debug/console.hpp" - -#if defined(_WIN32) - #define WIN32_LEAN_AND_MEAN - #include -#endif - -namespace debug { -namespace console { - -void enable_vt100() -{ - #if defined(_WIN32) - DWORD mode = 0; - HANDLE std_output_handle = GetStdHandle(STD_OUTPUT_HANDLE); - GetConsoleMode(std_output_handle, &mode); - SetConsoleMode(std_output_handle, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING); - #endif -} - -void disable_vt100() -{ - #if defined(_WIN32) - DWORD mode = 0; - HANDLE std_output_handle = GetStdHandle(STD_OUTPUT_HANDLE); - GetConsoleMode(std_output_handle, &mode); - SetConsoleMode(std_output_handle, mode & (~ENABLE_VIRTUAL_TERMINAL_PROCESSING)); - #endif -} - -void enable_utf8() -{ - #if defined(_WIN32) - SetConsoleOutputCP(CP_UTF8); - #endif -} - -} // namespace console -} // namespace debug diff --git a/src/debug/log.cpp b/src/debug/log.cpp deleted file mode 100644 index ee87942..0000000 --- a/src/debug/log.cpp +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "debug/log.hpp" - -namespace debug { -namespace log { - -logger& default_logger() noexcept -{ - static logger instance; - return instance; -} - -} // namespace log -} // namespace debug diff --git a/src/debug/log.hpp b/src/debug/log.hpp deleted file mode 100644 index c2d34c1..0000000 --- a/src/debug/log.hpp +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_DEBUG_LOG_HPP -#define ANTKEEPER_DEBUG_LOG_HPP - -#include "config.hpp" -#include "debug/log/message-severity.hpp" -#include "debug/log/logger.hpp" -#include -#include -#include - -// Enable logging of messages of all severities by default. -#if !defined(ANTKEEPER_DEBUG_LOG_MIN_MESSAGE_SEVERITY) - #define ANTKEEPER_DEBUG_LOG_MIN_MESSAGE_SEVERITY (ANTKEEPER_DEBUG_LOG_MESSAGE_SEVERITY_TRACE) -#endif - -namespace debug { - -/** - * Debug message logging. - */ -namespace log { - -/** - * Returns the default logger. - */ -[[nodiscard]] logger& default_logger() noexcept; - -/** - * Self-formatting message that logs itself to the default logger on construction. - * - * @tparam Severity Message severity. A message will not log itself if @p Severity is less than the user-defined macro `ANTKEEPER_DEBUG_LOG_MIN_MESSAGE_SEVERITY`. - * @tparam Args Types of arguments to be formatted. - */ -template -struct message -{ - /** - * Formats and logs a message. - * - * Class template argument deduction (CTAD) is utilized to capture source location as a default argument following variadic format arguments. - * - * @param format Message format string. - * @param args Arguments to be formatted. - * @param location Source location from which the message was sent. - */ - message - ( - [[maybe_unused]] std::string_view format, - [[maybe_unused]] Args&&... args, - [[maybe_unused]] std::source_location&& location = std::source_location::current() - ) - { - if constexpr (ANTKEEPER_DEBUG_LOG_MIN_MESSAGE_SEVERITY <= static_cast>(Severity)) - { - default_logger().log(std::vformat(format, std::make_format_args(std::forward(args)...)), Severity, std::forward(location)); - } - } -}; - -// Use class template argument deduction (CTAD) to capture source location as a default argument following variadic format arguments. -template -message(std::string_view, Args&&...) -> message; - -#if (ANTKEEPER_DEBUG_LOG_MIN_MESSAGE_SEVERITY <= ANTKEEPER_DEBUG_LOG_MESSAGE_SEVERITY_TRACE) - /** - * Formats and logs a trace message. - * - * @tparam Args Types of arguments to be formatted. - */ - template - using trace = message; -#else - // Disable trace message logging. - template - inline void trace([[maybe_unused]] Args&&...) noexcept {}; -#endif - -#if (ANTKEEPER_DEBUG_LOG_MIN_MESSAGE_SEVERITY <= ANTKEEPER_DEBUG_LOG_MESSAGE_SEVERITY_DEBUG) - /** - * Formats and logs a debug message. - * - * @tparam Args Types of arguments to be formatted. - */ - template - using debug = message; -#else - // Disable debug message logging. - template - inline void debug([[maybe_unused]] Args&&...) noexcept {}; -#endif - -#if (ANTKEEPER_DEBUG_LOG_MIN_MESSAGE_SEVERITY <= ANTKEEPER_DEBUG_LOG_MESSAGE_SEVERITY_INFO) - /** - * Formats and logs an info message. - * - * @tparam Args Types of arguments to be formatted. - */ - template - using info = message; -#else - // Disable info message logging. - template - inline void info([[maybe_unused]] Args&&...) noexcept {}; -#endif - -#if (ANTKEEPER_DEBUG_LOG_MIN_MESSAGE_SEVERITY <= ANTKEEPER_DEBUG_LOG_MESSAGE_SEVERITY_WARNING) - /** - * Formats and logs a warning message. - * - * @tparam Args Types of arguments to be formatted. - */ - template - using warning = message; -#else - // Disable warning message logging. - template - inline void warning([[maybe_unused]] Args&&...) noexcept {}; -#endif - -#if (ANTKEEPER_DEBUG_LOG_MIN_MESSAGE_SEVERITY <= ANTKEEPER_DEBUG_LOG_MESSAGE_SEVERITY_ERROR) - /** - * Formats and logs an error message. - * - * @tparam Args Types of arguments to be formatted. - */ - template - using error = message; -#else - // Disable error message logging. - template - inline void error([[maybe_unused]] Args&&...) noexcept {}; -#endif - -#if (ANTKEEPER_DEBUG_LOG_MIN_MESSAGE_SEVERITY <= ANTKEEPER_DEBUG_LOG_MESSAGE_SEVERITY_FATAL) - /** - * Formats and logs a fatal error message. - * - * @tparam Args Types of arguments to be formatted. - */ - template - using fatal = message; -#else - // Disable fatal error message logging. - template - inline void fatal([[maybe_unused]] Args&&...) noexcept {}; -#endif - -} // namespace log -} // namespace debug - -#endif // ANTKEEPER_DEBUG_LOG_HPP diff --git a/src/debug/log/event.hpp b/src/debug/log/event.hpp deleted file mode 100644 index c486e50..0000000 --- a/src/debug/log/event.hpp +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_DEBUG_LOG_EVENT_HPP -#define ANTKEEPER_DEBUG_LOG_EVENT_HPP - -#include "debug/log/message-severity.hpp" -#include -#include -#include -#include - -namespace debug { -namespace log { - -class logger; - -/** - * Debug logging events. - */ -namespace event { - -/** - * Event generated when a message has been logged. - */ -struct message_logged -{ - /// Logger which received the message. - log::logger* logger; - - /// Time at which the message was sent. - std::chrono::time_point time; - - /// ID of the thread from which the message was sent. - std::thread::id thread_id; - - /// Source location from which the message was sent. - std::source_location location; - - /// Severity of the message. - message_severity severity; - - /// Message contents. - std::string message; -}; - -} // namespace event -} // namespace log -} // namespace debug - -#endif // ANTKEEPER_DEBUG_LOG_EVENT_HPP diff --git a/src/debug/log/logger.cpp b/src/debug/log/logger.cpp deleted file mode 100644 index 8b20bb4..0000000 --- a/src/debug/log/logger.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "debug/log/logger.hpp" -#include -#include - -namespace debug { -namespace log { - -void logger::log(std::string&& message, message_severity severity, std::source_location&& location) -{ - // Generate message logged event - message_logged_publisher.publish - ( - { - this, - std::chrono::system_clock::now(), - std::this_thread::get_id(), - std::move(location), - severity, - std::move(message) - } - ); -} - -} // namespace log -} // namespace debug diff --git a/src/debug/log/logger.hpp b/src/debug/log/logger.hpp deleted file mode 100644 index 344cc9d..0000000 --- a/src/debug/log/logger.hpp +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_DEBUG_LOG_LOGGER_HPP -#define ANTKEEPER_DEBUG_LOG_LOGGER_HPP - -#include "debug/log/message-severity.hpp" -#include "debug/log/event.hpp" -#include "event/publisher.hpp" -#include -#include - -namespace debug { -namespace log { - -/** - * Generates an event each time a message logged. - */ -class logger -{ -public: - /** - * Logs a message. - * - * @param message Message contents. - * @param severity Message severity. - * @param location Source location from which the message was sent. - */ - void log - ( - std::string&& message, - message_severity severity = message_severity::info, - std::source_location&& location = std::source_location::current() - ); - - /// Returns the channel through which message logged events are published. - [[nodiscard]] inline ::event::channel& get_message_logged_channel() noexcept - { - return message_logged_publisher.channel(); - } - -private: - ::event::publisher message_logged_publisher; -}; - -} // namespace log -} // namespace debug - -#endif // ANTKEEPER_DEBUG_LOG_LOGGER_HPP diff --git a/src/engine/ai/ai.hpp b/src/engine/ai/ai.hpp new file mode 100644 index 0000000..600591e --- /dev/null +++ b/src/engine/ai/ai.hpp @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_AI_HPP +#define ANTKEEPER_AI_HPP + +/// Artificial intelligence (AI) +namespace ai {} + +#include +#include + +#endif // ANTKEEPER_AI_HPP diff --git a/src/engine/ai/bt/bt.hpp b/src/engine/ai/bt/bt.hpp new file mode 100644 index 0000000..c98e7f7 --- /dev/null +++ b/src/engine/ai/bt/bt.hpp @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_AI_BT_HPP +#define ANTKEEPER_AI_BT_HPP + +#include +#include + +namespace ai { + +/// Behavior tree (BT) +namespace bt {} + +} // namespace ai + +#include +#include + +#endif // ANTKEEPER_AI_BT_HPP diff --git a/src/engine/ai/bt/node.hpp b/src/engine/ai/bt/node.hpp new file mode 100644 index 0000000..45f19d1 --- /dev/null +++ b/src/engine/ai/bt/node.hpp @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_AI_BT_NODE_HPP +#define ANTKEEPER_AI_BT_NODE_HPP + +#include + +namespace ai { +namespace bt { + +/** + * Abstract base class for behavior tree nodes. + * + * @tparam T Data type on which nodes operate. + */ +template +struct node +{ + /// Data type on which nodes operate. + typedef T context_type; + + /** + * Executes a node's function and returns its status. + * + * @param context Context data on which the node will operate. + */ + virtual status execute(context_type& context) const = 0; +}; + +/// Behavior tree node with no children. +template +using leaf_node = node; + + +/// A node with exactly one child. +template +struct decorator_node: public node +{ + node* child; +}; + +/// A node that can have one or more children. +template +struct composite_node: public node +{ + std::list*> children; +}; + +/// Executes a function on a context and returns the status. +template +struct action: public leaf_node +{ + virtual status execute(node::context_type& context) const final; + typedef std::function::context_type&)> function_type; + function_type function; +}; + +/// Evaluates a boolean condition (predicate) and returns either `status::success` or `status::failure`. +template +struct condition: public leaf_node +{ + virtual status execute(node::context_type& context) const final; + typedef std::function::context_type&)> predicate_type; + predicate_type predicate; +}; + +/// Executes a child node and returns its inverted status. If the child returns `status::success`, then `status::failure` will be returned. Otherwise if the child returns `status::failure`, then `status::success` will be returned. +template +struct inverter: public decorator_node +{ + virtual status execute(node::context_type& context) const final; +}; + +/// Attempts to execute a child node `n` times or until the child fails. +template +struct repeater: public decorator_node +{ + virtual status execute(node::context_type& context) const final; + int n; +}; + +/// Executes a child node and returns `status::success` regardless of the child node status. +template +struct succeeder: public decorator_node +{ + virtual status execute(node::context_type& context) const final; +}; + +/// Attempts to execute each child node sequentially until one fails. If all children are executed successfully, `status::success` will be returned. Otherwise if any children fail, `status::failure` will be returned. +template +struct sequence: public composite_node +{ + virtual status execute(node::context_type& context) const final; +}; + +/// Attempts to execute each child node sequentially until one succeeds. If a child succeeds, `status::success` will be returned. Otherwise if all children fail, `status::failure` will be returned. +template +struct selector: public composite_node +{ + virtual status execute(node::context_type& context) const final; +}; + +template +status action::execute(node::context_type& context) const +{ + return function(context); +} + +template +status condition::execute(node::context_type& context) const +{ + return (predicate(context)) ? status::success : status::failure; +} + +template +status inverter::execute(node::context_type& context) const +{ + status child_status = decorator_node::child->execute(context); + return (child_status == status::success) ? status::failure : (child_status == status::failure) ? status::success : child_status; +} + +template +status repeater::execute(node::context_type& context) const +{ + status child_status; + for (int i = 0; i < n; ++i) + { + child_status = decorator_node::child->execute(context); + if (child_status == status::failure) + break; + } + return child_status; +} + +template +status succeeder::execute(node::context_type& context) const +{ + decorator_node::child->execute(context); + return status::success; +} + +template +status sequence::execute(node::context_type& context) const +{ + for (const node* child: composite_node::children) + { + status child_status = child->execute(context); + if (child_status != status::success) + return child_status; + } + return status::success; +} + +template +status selector::execute(node::context_type& context) const +{ + for (const node* child: composite_node::children) + { + status child_status = child->execute(context); + if (child_status != status::failure) + return child_status; + } + return status::failure; +} + +} // namespace bt +} // namespace ai + +#endif // ANTKEEPER_AI_BT_NODE_HPP diff --git a/src/ai/bt/status.hpp b/src/engine/ai/bt/status.hpp similarity index 100% rename from src/ai/bt/status.hpp rename to src/engine/ai/bt/status.hpp diff --git a/src/engine/ai/steering/agent.hpp b/src/engine/ai/steering/agent.hpp new file mode 100644 index 0000000..32a649e --- /dev/null +++ b/src/engine/ai/steering/agent.hpp @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_AI_STEERING_AGENT_HPP +#define ANTKEEPER_AI_STEERING_AGENT_HPP + +#include +#include + +namespace ai { +namespace steering { + +/** + * Autonomous agent governed by steering behaviors. + */ +struct agent +{ + /// Mass of the agent. + float mass; + + /// Cartesian position vector. + float3 position; + + /// Velocity vector. + float3 velocity; + + /// Acceleration vector. + float3 acceleration; + + /// Maximum force. + float max_force; + + /// Maximum speed. + float max_speed; + + /// Maximum speed squared. + float max_speed_squared; + + /// Orientation quaternion. + math::quaternion orientation; + + /// Orthonormal basis forward direction vector. + float3 forward; + + /// Orthonormal basis up direction vector. + float3 up; +}; + +} // namespace steering +} // namespace ai + +#endif // ANTKEEPER_AI_STEERING_AGENT_HPP diff --git a/src/engine/ai/steering/behavior/flee.cpp b/src/engine/ai/steering/behavior/flee.cpp new file mode 100644 index 0000000..9e00cb3 --- /dev/null +++ b/src/engine/ai/steering/behavior/flee.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include + +namespace ai { +namespace steering { +namespace behavior { + +float3 flee(const agent& agent, const float3& target) +{ + float3 force = {0, 0, 0}; + const float3 difference = target - agent.position; + const float sqr_distance = math::dot(difference, difference); + + if (sqr_distance) + { + const float inverse_distance = 1.0f / std::sqrt(sqr_distance); + force = difference * inverse_distance * agent.max_force; + force = agent.velocity - force; + } + + return force; +} + +} // namespace behavior +} // namespace steering +} // namespace ai diff --git a/src/engine/ai/steering/behavior/flee.hpp b/src/engine/ai/steering/behavior/flee.hpp new file mode 100644 index 0000000..91490f3 --- /dev/null +++ b/src/engine/ai/steering/behavior/flee.hpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_AI_STEERING_BEHAVIOR_FLEE_HPP +#define ANTKEEPER_AI_STEERING_BEHAVIOR_FLEE_HPP + +#include +#include + +namespace ai { +namespace steering { +namespace behavior { + +/** + * Attempts to steer an agent so that it moves away from a target. + * + * @param agent Autonomous agent to steer. + * @param target Target position. + * @return Flee force. + */ +float3 flee(const agent& agent, const float3& target); + +} // namespace behavior +} // namespace steering +} // namespace ai + +#endif // ANTKEEPER_AI_STEERING_BEHAVIOR_FLEE_HPP diff --git a/src/engine/ai/steering/behavior/seek.cpp b/src/engine/ai/steering/behavior/seek.cpp new file mode 100644 index 0000000..9adba2c --- /dev/null +++ b/src/engine/ai/steering/behavior/seek.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include + +namespace ai { +namespace steering { +namespace behavior { + +float3 seek(const agent& agent, const float3& target) +{ + float3 force = {0, 0, 0}; + const float3 difference = target - agent.position; + const float sqr_distance = math::dot(difference, difference); + + if (sqr_distance) + { + const float inverse_distance = 1.0f / std::sqrt(sqr_distance); + force = difference * inverse_distance * agent.max_force; + force -= agent.velocity; + } + + return force; +} + +} // namespace behavior +} // namespace steering +} // namespace ai diff --git a/src/engine/ai/steering/behavior/seek.hpp b/src/engine/ai/steering/behavior/seek.hpp new file mode 100644 index 0000000..03b8621 --- /dev/null +++ b/src/engine/ai/steering/behavior/seek.hpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_AI_STEERING_BEHAVIOR_SEEK_HPP +#define ANTKEEPER_AI_STEERING_BEHAVIOR_SEEK_HPP + +#include +#include + +namespace ai { +namespace steering { +namespace behavior { + +/** + * Attempts to steer an agent so that it moves toward a target. + * + * @param agent Autonomous agent to steer. + * @param target Target position. + * @return Seek force. + */ +float3 seek(const agent& agent, const float3& target); + +} // namespace behavior +} // namespace steering +} // namespace ai + +#endif // ANTKEEPER_AI_STEERING_BEHAVIOR_SEEK_HPP diff --git a/src/engine/ai/steering/behavior/wander.cpp b/src/engine/ai/steering/behavior/wander.cpp new file mode 100644 index 0000000..4c86f6f --- /dev/null +++ b/src/engine/ai/steering/behavior/wander.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include +#include + +namespace ai { +namespace steering { +namespace behavior { + +float3 wander_2d(const agent& agent, float noise, float distance, float radius, float& angle) +{ + // Shift wander angle + angle += math::random(-noise, noise); + + // Calculate center of wander circle + const float3 center = agent.position + agent.forward * distance; + + // Decompose orientation into swing and twist rotations + math::quaternion swing, twist; + math::swing_twist(agent.orientation, agent.up, swing, twist); + + // Calculate offset to point on wander circle + const float3 offset = math::conjugate(twist) * (math::angle_axis(angle, agent.up) * agent.forward * radius); + + // Seek toward point on wander circle + return seek(agent, center + offset); +} + +float3 wander_3d(const agent& agent, float noise, float distance, float radius, float& theta, float& phi) +{ + // Shift wander angles + theta += math::random(-noise, noise); + phi += math::random(-noise, noise); + + // Calculate center of wander sphere + const float3 center = agent.position + agent.forward * distance; + + // Convert spherical coordinates to Cartesian point on wander sphere + const float r_cos_theta = radius * std::cos(theta); + const float3 offset = + { + r_cos_theta * std::cos(phi), + r_cos_theta * std::sin(phi), + radius * std::sin(theta) + }; + + // Seek toward point on wander sphere + return seek(agent, center + offset); +} + +} // namespace behavior +} // namespace steering +} // namespace ai diff --git a/src/engine/ai/steering/behavior/wander.hpp b/src/engine/ai/steering/behavior/wander.hpp new file mode 100644 index 0000000..eab4ea7 --- /dev/null +++ b/src/engine/ai/steering/behavior/wander.hpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_AI_STEERING_BEHAVIOR_WANDER_HPP +#define ANTKEEPER_AI_STEERING_BEHAVIOR_WANDER_HPP + +#include +#include + +namespace ai { +namespace steering { +namespace behavior { + +/** + * Steers an agent in a continuously shifting random direction on the yaw plane. + * + * @param agent Autonomous agent to steer. + * @param distance Distance to the center of the wander circle. + * @param noise Maximum wander angle shift, in radians. + * @param radius Radius of the wander circle. + * @param[in,out] angle Angular coordinate on the wander circle, in radians. + * + * @return Wander force. + */ +float3 wander_2d(const agent& agent, float noise, float distance, float radius, float& angle); + +/** + * Steers an agent in a continuously shifting random direction. + * + * @param agent Autonomous agent to steer. + * @param distance Distance to the wander sphere. + * @param radius Radius of the wander sphere. + * @param delta Maximum angle offset. + * @param[in,out] theta Polar wander angle, in radians. + * @param[in,out] phi Azimuthal wander angle, in radians. + * + * @return Wander force. + */ +float3 wander_3d(const agent& agent, float noise, float distance, float radius, float& theta, float& phi); + +} // namespace behavior +} // namespace steering +} // namespace ai + +#endif // ANTKEEPER_AI_STEERING_BEHAVIOR_WANDER_HPP diff --git a/src/engine/ai/steering/steering.hpp b/src/engine/ai/steering/steering.hpp new file mode 100644 index 0000000..d538b44 --- /dev/null +++ b/src/engine/ai/steering/steering.hpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_AI_STEERING_HPP +#define ANTKEEPER_AI_STEERING_HPP + +namespace ai { + +/** + * Autonomous agent steering. + * + * @see Reynolds, Craig. (2002). Steering Behaviors For Autonomous Characters. + */ +namespace steering {} + +#include +#include +#include +#include + +} // namespace ai + +#endif // ANTKEEPER_AI_STEERING_HPP diff --git a/src/animation/animation-channel.hpp b/src/engine/animation/animation-channel.hpp similarity index 100% rename from src/animation/animation-channel.hpp rename to src/engine/animation/animation-channel.hpp diff --git a/src/engine/animation/animation.cpp b/src/engine/animation/animation.cpp new file mode 100644 index 0000000..3900189 --- /dev/null +++ b/src/engine/animation/animation.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include + +animation_base::animation_base(): + looped(false), + loop_count(0), + paused(false), + stopped(true), + position(0.0), + speed(1.0), + start_callback(nullptr), + end_callback(nullptr), + loop_callback(nullptr) +{} + +void animation_base::seek(double t) +{ + position = t; +} + +void animation_base::rewind() +{ + seek(0.0); +} + +void animation_base::loop(bool enabled) +{ + looped = enabled; +} + +void animation_base::pause() +{ + paused = true; +} + +void animation_base::play() +{ + if (stopped) + { + stopped = false; + + if (start_callback) + { + start_callback(); + } + } + + paused = false; +} + +void animation_base::stop() +{ + rewind(); + stopped = true; + paused = false; + loop_count = 0; +} + +void animation_base::set_speed(double speed) +{ + this->speed = speed; +} + +void animation_base::set_start_callback(std::function callback) +{ + start_callback = callback; +} + +void animation_base::set_end_callback(std::function callback) +{ + end_callback = callback; +} + +void animation_base::set_loop_callback(std::function callback) +{ + loop_callback = callback; +} diff --git a/src/engine/animation/animation.hpp b/src/engine/animation/animation.hpp new file mode 100644 index 0000000..be81afd --- /dev/null +++ b/src/engine/animation/animation.hpp @@ -0,0 +1,387 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_ANIMATION_HPP +#define ANTKEEPER_ANIMATION_HPP + +#include +#include +#include +#include +#include + +/** + * Abstract base class for keyframe animations. + */ +class animation_base +{ +public: + animation_base(); + + /** + * Advances the animation position (t) by @p dt. + * + * @param dt Delta time by which the animation position will be advanced. + */ + virtual void advance(double dt) = 0; + + /** + * Sets the animation position to @p t. + * + * @param t Position in time to which the animation position will be set. + */ + void seek(double t); + + /// Sets the animation position to `0.0`. + void rewind(); + + /// Enables or disables looping of the animation. + void loop(bool enabled); + + /// Pauses the animation. + void pause(); + + /// Plays the animation. + void play(); + + /// Stops the animation, rewinds it, and resets the loop count. + void stop(); + + /** + * Sets the speed of the animation. + * + * @param speed Speed multiplier. + */ + void set_speed(double speed); + + /// Returns `true` if looping of the animation is enabled, `false` otherwise. + bool is_looped() const; + + /// Returns `true` if the animation is paused, `false` otherwise. + bool is_paused() const; + + /// Returns `true` if the animation is stopped, `false` otherwise. + bool is_stopped() const; + + /// Returns the current position in time of the animation. + double get_position() const; + + /// Returns the current loop count of the animation. + int get_loop_count() const; + + /// Returns the duration of the animation. + virtual double get_duration() const = 0; + + /// Sets the callback that's executed when the animation is started from a stopped state. + void set_start_callback(std::function callback); + + /// Sets the callback that's executed when a non-looped animation has finished. + void set_end_callback(std::function callback); + + /** + * Sets the callback that's executed when the animation loops. + * + * @param callback Loop callback function which is passed the current loop count. + */ + void set_loop_callback(std::function callback); + +protected: + bool looped; + int loop_count; + bool paused; + bool stopped; + double position; + double speed; + + std::function start_callback; + std::function end_callback; + std::function loop_callback; +}; + +inline bool animation_base::is_looped() const +{ + return looped; +} + +inline bool animation_base::is_paused() const +{ + return paused; +} + +inline bool animation_base::is_stopped() const +{ + return stopped; +} + +inline double animation_base::get_position() const +{ + return position; +} + +inline int animation_base::get_loop_count() const +{ + return loop_count; +} + +/** + * Keyframe animation. + * + * @tparam T Animated data type. + */ +template +class animation: public animation_base +{ +public: + /// Channel for this animation type. + typedef animation_channel channel; + + // Keyframe type for this animation. + typedef typename channel::keyframe keyframe; + + /// Interpolator function type. + typedef typename std::decay>::type interpolator_type; + + /// Creates an animation. + animation(); + + /// @copydoc animation_base::advance() + virtual void advance(double dt); + + /** + * Adds a channel to the animation. + * + * @param id ID of the channel. + * @return Added or pre-existing channel. + */ + channel* add_channel(int id); + + /** + * Removes a channel from the animation. + * + * @param id ID of the channel to remove. + */ + void remove_channel(int id); + + /// Removes all channels from the animation. + void remove_channels(); + + /** + * Sets the frame interpolator function object. + * + * @param interpolator Frame interpolator function object. + */ + void set_interpolator(interpolator_type interpolator); + + /** + * Sets the callback that's executed on each frame of animation. + * + * @param callback Frame callback which receives the index of an animation channel and value of an interpolated frame. + */ + void set_frame_callback(std::function callback); + + /** + * Returns the channel with the specified ID. + * + * @param id ID of the channel to get. + */ + const channel* get_channel(int id) const; + + /// @copydoc animation::get_channel(int) const + channel* get_channel(int id); + + /// @copydoc animation_base::get_duration() const + virtual double get_duration() const; + +private: + std::unordered_map channels; + interpolator_type interpolator; + std::function frame_callback; +}; + +template +animation::animation(): + interpolator(nullptr), + frame_callback(nullptr) +{} + +template +void animation::advance(double dt) +{ + if (paused || stopped) + { + return; + } + + // Advance position by dt + position += dt * speed; + + // Determine duration of the animation + double duration = get_duration(); + + if (position < duration) + { + if (frame_callback != nullptr && interpolator != nullptr) + { + for (std::size_t i = 0; i < channels.size(); ++i) + { + auto frames = channels[i].find_keyframes(position); + + if (frames[0] != nullptr && frames[1] != nullptr) + { + // Calculate interpolated frame + double t0 = std::get<0>(*frames[0]); + double t1 = std::get<0>(*frames[1]); + double alpha = (position - t0) / (t1 - t0); + T frame = interpolator(std::get<1>(*frames[0]), std::get<1>(*frames[1]), alpha); + + // Pass frame to frame callback + frame_callback(static_cast(i), frame); + } + else if (frames[0] != nullptr) + { + // Pass frame to frame callback + frame_callback(static_cast(i), std::get<1>(*frames[0])); + } + else if (frames[1] != nullptr) + { + // Pass frame to frame callback + frame_callback(static_cast(i), std::get<1>(*frames[1])); + } + } + } + } + else + { + if (looped) + { + ++loop_count; + + // Subtract duration of animation from position + position -= duration; + + // Execute loop callback + if (loop_callback) + { + loop_callback(loop_count); + } + + // Call frame callback on looped frame + if (frame_callback) + { + advance(0.0); + } + } + else + { + // Call frame callback for end frame + if (frame_callback != nullptr) + { + for (std::size_t i = 0; i < channels.size(); ++i) + { + auto frames = channels[i].find_keyframes(channels[i].get_duration()); + if (frames[0] != nullptr) + { + frame_callback(static_cast(i), std::get<1>(*frames[0])); + } + } + } + + stopped = true; + + // Call end callback + if (end_callback) + { + end_callback(); + } + } + } +} + +template +typename animation::channel* animation::add_channel(int id) +{ + return &(*channels.emplace(id, id).first).second; +} + +template +void animation::remove_channel(int id) +{ + auto it = channels.find(id); + if (it != channels.end()) + { + channels.erase(it); + } +} + +template +void animation::remove_channels() +{ + channels.clear(); +} + +template +void animation::set_interpolator(interpolator_type interpolator) +{ + this->interpolator = interpolator; +} + +template +void animation::set_frame_callback(std::function callback) +{ + this->frame_callback = callback; +} + +template +const typename animation::channel* animation::get_channel(int id) const +{ + auto it = channels.find(id); + if (it != channels.end()) + { + return &it->second; + } + + return nullptr; +} + +template +typename animation::channel* animation::get_channel(int id) +{ + auto it = channels.find(id); + if (it != channels.end()) + { + return &it->second; + } + + return nullptr; +} + +template +double animation::get_duration() const +{ + double duration = 0.0; + + for (auto it = channels.begin(); it != channels.end(); ++it) + { + duration = std::max(duration, it->second.get_duration()); + } + + return duration; +} + +#endif // ANTKEEPER_ANIMATION_HPP diff --git a/src/engine/animation/animator.cpp b/src/engine/animation/animator.cpp new file mode 100644 index 0000000..932fdb4 --- /dev/null +++ b/src/engine/animation/animator.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include + +animator::animator(): + animating(0) +{} + +void animator::animate(double dt) +{ + // Advance animations + ++animating; + for (animation_base* animation: animations) + { + animation->advance(dt); + } + --animating; +} + +void animator::add_animation(animation_base* animation) +{ + if (animating) + throw std::runtime_error("Attempting to add animation to animator while animating"); + + animations.emplace(animation); +} + +void animator::remove_animation(animation_base* animation) +{ + if (animating) + throw std::runtime_error("Attempting to remove animation from animator while animating"); + + auto it = animations.find(animation); + if (it != animations.end()) + { + animations.erase(it); + } +} + +void animator::remove_animations() +{ + if (animating) + throw std::runtime_error("Attempting to remove animations from animator while animating"); + + animations.clear(); +} diff --git a/src/animation/animator.hpp b/src/engine/animation/animator.hpp similarity index 100% rename from src/animation/animator.hpp rename to src/engine/animation/animator.hpp diff --git a/src/animation/bone.hpp b/src/engine/animation/bone.hpp similarity index 100% rename from src/animation/bone.hpp rename to src/engine/animation/bone.hpp diff --git a/src/engine/animation/ease.hpp b/src/engine/animation/ease.hpp new file mode 100644 index 0000000..e9bb555 --- /dev/null +++ b/src/engine/animation/ease.hpp @@ -0,0 +1,397 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +/* + * Easing Functions (Equations) + * + * Copyright (C) 2001 Robert Penner + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of the author nor the names of contributors may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ANTKEEPER_EASE_HPP +#define ANTKEEPER_EASE_HPP + +#include +#include + +/** + * Container for templated easing functions. + * + * All easing functions require the following operators to be defined: + * + * value_type operator+(const value_type&, const value_type&); + * value_type operator-(const value_type&, const value_type&); + * value_type operator*(const value_type&, scalar_type); + * + * @tparam T Value type. + * @tparam S Scalar type. + */ +template +struct ease +{ + typedef T value_type; + typedef S scalar_type; + + static T in_sine(const T& x, const T& y, S a); + static T out_sine(const T& x, const T& y, S a); + static T in_out_sine(const T& x, const T& y, S a); + + static T in_quad(const T& x, const T& y, S a); + static T out_quad(const T& x, const T& y, S a); + static T in_out_quad(const T& x, const T& y, S a); + + static T in_cubic(const T& x, const T& y, S a); + static T out_cubic(const T& x, const T& y, S a); + static T in_out_cubic(const T& x, const T& y, S a); + + static T in_quart(const T& x, const T& y, S a); + static T out_quart(const T& x, const T& y, S a); + static T in_out_quart(const T& x, const T& y, S a); + + static T in_quint(const T& x, const T& y, S a); + static T out_quint(const T& x, const T& y, S a); + static T in_out_quint(const T& x, const T& y, S a); + + static T in_expo(const T& x, const T& y, S a); + static T out_expo(const T& x, const T& y, S a); + static T in_out_expo(const T& x, const T& y, S a); + + static T in_circ(const T& x, const T& y, S a); + static T out_circ(const T& x, const T& y, S a); + static T in_out_circ(const T& x, const T& y, S a); + + static T in_back(const T& x, const T& y, S a); + static T out_back(const T& x, const T& y, S a); + static T in_out_back(const T& x, const T& y, S a); + + static T in_elastic(const T& x, const T& y, S a); + static T out_elastic(const T& x, const T& y, S a); + static T in_out_elastic(const T& x, const T& y, S a); + + static T in_bounce(const T& x, const T& y, S a); + static T out_bounce(const T& x, const T& y, S a); + static T in_out_bounce(const T& x, const T& y, S a); +}; + +template +T ease::in_sine(const T& x, const T& y, S a) +{ + return math::lerp(y, x, std::cos(a * math::half_pi)); +} + +template +T ease::out_sine(const T& x, const T& y, S a) +{ + return math::lerp(x, y, std::sin(a * math::half_pi)); +} + +template +T ease::in_out_sine(const T& x, const T& y, S a) +{ + return math::lerp(x, y, -(std::cos(a * math::pi) - S(1)) * S(0.5)); +} + +template +T ease::in_quad(const T& x, const T& y, S a) +{ + return math::lerp(x, y, a * a); +} + +template +T ease::out_quad(const T& x, const T& y, S a) +{ + return math::lerp(x, y, (S(2) - a) * a); +} + +template +T ease::in_out_quad(const T& x, const T& y, S a) +{ + return math::lerp(x, y, (a < S(0.5)) ? S(2) * a * a : -(S(2) * a * a - S(4) * a + S(1))); +} + +template +T ease::in_cubic(const T& x, const T& y, S a) +{ + return math::lerp(x, y, a * a * a); +} + +template +T ease::out_cubic(const T& x, const T& y, S a) +{ + return math::lerp(x, y, a * ((a - S(3)) * a + S(3))); +} + +template +T ease::in_out_cubic(const T& x, const T& y, S a) +{ + return math::lerp(x, y, (a < S(0.5)) ? S(4) * a * a * a : S(4) * a * a * a - S(12) * a * a + S(12) * a - 3); +} + +template +T ease::in_quart(const T& x, const T& y, S a) +{ + return math::lerp(x, y, a * a * a * a); +} + +template +T ease::out_quart(const T& x, const T& y, S a) +{ + return math::lerp(x, y, a * (a * ((S(4) - a) * a - S(6)) + S(4))); +} + +template +T ease::in_out_quart(const T& x, const T& y, S a) +{ + return math::lerp(x, y, (a < S(0.5)) ? S(8) * a * a * a * a : a * (a * ((S(32) - S(8) * a) * a - S(48)) + S(32)) - S(7)); +} + +template +T ease::in_quint(const T& x, const T& y, S a) +{ + return math::lerp(x, y, a * a * a * a * a); +} + +template +T ease::out_quint(const T& x, const T& y, S a) +{ + return math::lerp(x, y, a * (a * (a * ((a - S(5)) * a + S(10)) - S(10)) + S(5))); +} + +template +T ease::in_out_quint(const T& x, const T& y, S a) +{ + if (a < S(0.5)) + { + return math::lerp(x, y, S(16) * a * a * a * a * a); + } + else + { + a = S(2) * (S(1) - a); + return math::lerp(x, y, S(0.5) * (S(2) - a * a * a * a * a)); + } +} + +template +T ease::in_expo(const T& x, const T& y, S a) +{ + return (a == S(0)) ? x : math::lerp(x, y, std::pow(S(1024), a - S(1))); +} + +template +T ease::out_expo(const T& x, const T& y, S a) +{ + return (a == S(1)) ? y : math::lerp(y, x, std::pow(S(2), S(-10) * a)); +} + +template +T ease::in_out_expo(const T& x, const T& y, S a) +{ + if (a == S(0)) + { + return x; + } + else if (a == S(1)) + { + return y; + } + + return math::lerp(x, y, (a < S(0.5)) ? std::pow(S(2), S(20) * a - S(11)) : S(1) - std::pow(S(2), S(9) - S(20) * a)); +} + +template +T ease::in_circ(const T& x, const T& y, S a) +{ + return math::lerp(y, x, std::sqrt(S(1) - a * a)); +} + +template +T ease::out_circ(const T& x, const T& y, S a) +{ + return math::lerp(x, y, std::sqrt(-(a - S(2)) * a)); +} + +template +T ease::in_out_circ(const T& x, const T& y, S a) +{ + if (a < S(0.5)) + { + return math::lerp(x, y, S(0.5) - S(0.5) * std::sqrt(S(1) - S(4) * a * a)); + } + else + { + return math::lerp(x, y, S(0.5) * (std::sqrt(S(-4) * (a - S(2)) * a - S(3)) + S(1))); + } +} + +template +T ease::in_back(const T& x, const T& y, S a) +{ + const S c = S(1.70158); + return math::lerp(x, y, a * a * (a * c + a - c)); +} + +template +T ease::out_back(const T& x, const T& y, S a) +{ + const S c = S(1.70158); + a -= S(1); + return math::lerp(x, y, a * a * (a * c + a + c) + S(1)); +} + +template +T ease::in_out_back(const T& x, const T& y, S a) +{ + const S c = S(1.70158) * S(1.525f); + + if (a < S(0.5)) + { + return math::lerp(x, y, a * a * (a * (S(4) * c + S(4)) - S(2) * c)); + } + else + { + S b = S(1) - S(2) * a; + return math::lerp(x, y, b * b * (a * c + a - c * S(0.5) - S(1)) + S(1)); + } +} + +template +T ease::in_elastic(const T& x, const T& y, S a) +{ + if (a == S(0)) + { + return x; + } + else if (a == S(1)) + { + return y; + } + + return math::lerp(x, y, -std::pow(S(1024), a - S(1)) * std::sin(S(20.944) * (a - S(1.075)))); +} + +template +T ease::out_elastic(const T& x, const T& y, S a) +{ + if (a == S(0)) + { + return x; + } + else if (a == S(1)) + { + return y; + } + + return math::lerp(x, y, std::pow(S(2), S(-10) * a) * std::sin(S(20.944) * (a - S(0.075))) + S(1)); +} + +template +T ease::in_out_elastic(const T& x, const T& y, S a) +{ + if (a == S(0)) + { + return x; + } + else if (a == S(1)) + { + return y; + } + + if (a < S(0.5)) + { + return math::lerp(x, y, std::pow(S(2), S(20) * a - S(11)) * std::sin(S(15.5334) - S(27.5293) * a)); + } + else + { + return math::lerp(y, x, std::pow(2, S(9) - S(20) * a) * std::sin(S(15.5334) - S(27.5293) * a)); + } +} + +template +T ease::in_bounce(const T& x, const T& y, S a) +{ + return math::lerp(x, y, S(1) - ease::out_bounce(S(0), S(1), S(1) - a)); +} + +template +T ease::out_bounce(const T& x, const T& y, S a) +{ + const S n = S(7.5625); + const S d = S(2.75); + + if (a < S(1) / d) + { + a = n * a * a; + } + else if (a < S(2) / d) + { + a -= S(1.5) / d; + a = n * a * a + S(0.75); + } + else if (a < S(2.5) / d) + { + a -= S(2.25) / d; + a = n * a * a + S(0.9375); + } + else + { + a -= S(2.625) / d; + a = n * a * a + S(0.984375); + } + + return math::lerp(x, y, a); +} + +template +T ease::in_out_bounce(const T& x, const T& y, S a) +{ + if (a < S(0.5)) + { + return math::lerp(x, y, (S(1) - ease::out_bounce(S(0), S(1), S(1) - S(2) * a)) * S(0.5)); + } + else + { + return math::lerp(x, y, (S(1) + ease::out_bounce(S(0), S(1), S(2) * a - S(1))) * S(0.5)); + } +} + +#endif // ANTKEEPER_EASE_HPP diff --git a/src/engine/animation/pose.cpp b/src/engine/animation/pose.cpp new file mode 100644 index 0000000..f7f6399 --- /dev/null +++ b/src/engine/animation/pose.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include + +void concatenate(const pose& bone_space, pose& skeleton_space) +{ + for (auto&& [bone, transform]: bone_space) + { + auto parent_index = bone_parent_index(bone); + + if (parent_index != bone_index(bone)) + { + auto parent = skeleton_space.find(parent_index); + skeleton_space[bone] = (parent != skeleton_space.end()) ? parent->second * transform : transform; + } + else + { + skeleton_space[bone] = transform; + } + } +} + +void inverse(const pose& x, pose& y) +{ + for (auto&& [bone, transform]: x) + { + y[bone] = math::inverse(transform); + } +} + +void matrix_palette(const pose& inverse_bind_pose, const pose& pose, float4x4* palette) +{ + for (auto&& [bone, transform]: pose) + { + auto index = ::bone_index(bone); + palette[index] = math::matrix_cast(inverse_bind_pose.at(bone) * transform); + } +} diff --git a/src/engine/animation/pose.hpp b/src/engine/animation/pose.hpp new file mode 100644 index 0000000..f351621 --- /dev/null +++ b/src/engine/animation/pose.hpp @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_ANIMATION_POSE_HPP +#define ANTKEEPER_ANIMATION_POSE_HPP + +#include +#include +#include +#include + +/** + * Skeletal animation pose. + */ +typedef std::map, bone_index_compare> pose; + +/** + * Transforms a pose from bone-space into skeleton-space. + * + * @param[in] bone_space Bone-space pose. + * @param[out] skeleton_space Skeleton-space pose. + * + * @warning If the index of any child bone is greater than its parent index, the concatenated pose may be incorrect. + */ +void concatenate(const pose& bone_space, pose& skeleton_space); + +/** + * Inverses each transform in a pose. + * + * @param[in] x Input pose. + * @param[out] y Output pose. + */ +void inverse(const pose& x, pose& y); + +/** + * Generates a skinning matrix palette from a pose. + * + * @param inverse_bind_pose Inverse of the skeleton-space bind pose. + * @param pose Bone-space Skeleton-space pose. + */ +void matrix_palette(const pose& inverse_bind_pose, const pose& pose, float4x4* palette); + +#endif // ANTKEEPER_ANIMATION_POSE_HPP diff --git a/src/engine/animation/screen-transition.cpp b/src/engine/animation/screen-transition.cpp new file mode 100644 index 0000000..72ee1dd --- /dev/null +++ b/src/engine/animation/screen-transition.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include + +screen_transition::screen_transition() +{ + // Setup material + //material.set_flags(MATERIAL_FLAG_X_RAY); + material.set_blend_mode(render::blend_mode::translucent); + progress = material.add_property("progress"); + + // Setup billboard + billboard.set_material(&material); + billboard.set_active(false); + + // Add single channel to transition animation + channel = animation.add_channel(0); + + // Setup animation start callback to show transition billboard + animation.set_start_callback + ( + std::bind(&scene::object_base::set_active, &billboard, true) + ); + + // Setup animation end callback to hide transition billboard + animation.set_end_callback + ( + std::bind(&scene::object_base::set_active, &billboard, false) + ); + + // Setup animation frame callback to update transition progress material property + animation.set_frame_callback + ( + [this](int channel, float progress) + { + this->progress->set_value(progress); + } + ); + + // Setup animation frame callback to update transition progress material property + animation.set_frame_callback + ( + [this](int channel, float progress) + { + this->progress->set_value(progress); + } + ); +} + +void screen_transition::set_visible(bool visible) +{ + billboard.set_active(visible); +} + +void screen_transition::transition(float duration, bool reverse, ::animation::interpolator_type interpolator, bool hide, const std::function& callback) +{ + float initial_state = (reverse) ? 1.0f : 0.0f; + float final_state = (reverse) ? 0.0f : 1.0f; + + // Build transition animation + channel->remove_keyframes(); + channel->insert_keyframe({0.0f, initial_state}); + channel->insert_keyframe({duration, final_state}); + + // Set transition animation interpolator + animation.set_interpolator(interpolator); + + this->callback = callback; + if (hide) + { + // Setup animation end callback to hide transition billboard + animation.set_end_callback + ( + [this]() + { + this->billboard.set_active(false); + if (this->callback) + this->callback(); + } + ); + } + else + { + animation.set_end_callback(callback); + } + + // Update tweens + progress->set_value(initial_state); + material.update_tweens(); + + // Reset and play transition animation + animation.stop(); + animation.play(); +} diff --git a/src/engine/animation/screen-transition.hpp b/src/engine/animation/screen-transition.hpp new file mode 100644 index 0000000..2aa647c --- /dev/null +++ b/src/engine/animation/screen-transition.hpp @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_SCREEN_TRANSITION_HPP +#define ANTKEEPER_SCREEN_TRANSITION_HPP + +#include +#include +#include +#include + +/** + * Encapsulates a shader-based animated screen transition. + */ +class screen_transition +{ +public: + screen_transition(); + + void set_visible(bool visible); + + void transition(float duration, bool reverse, animation::interpolator_type interpolator, bool hide = true, const std::function& callback = nullptr); + + scene::billboard* get_billboard(); + render::material* get_material(); + ::animation* get_animation(); + +private: + scene::billboard billboard; + render::material material; + render::material_property* progress; + ::animation animation; + ::animation::channel* channel; + std::function callback; +}; + +inline scene::billboard* screen_transition::get_billboard() +{ + return &billboard; +} + +inline render::material* screen_transition::get_material() +{ + return &material; +} + +inline animation* screen_transition::get_animation() +{ + return &animation; +} + +#endif // ANTKEEPER_SCREEN_TRANSITION_HPP diff --git a/src/engine/animation/skeleton.cpp b/src/engine/animation/skeleton.cpp new file mode 100644 index 0000000..543cbfe --- /dev/null +++ b/src/engine/animation/skeleton.cpp @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include + diff --git a/src/engine/animation/skeleton.hpp b/src/engine/animation/skeleton.hpp new file mode 100644 index 0000000..27cfc62 --- /dev/null +++ b/src/engine/animation/skeleton.hpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_ANIMATION_SKELETON_HPP +#define ANTKEEPER_ANIMATION_SKELETON_HPP + +#include +#include +#include +#include + +/** + * Skeletal animation skeleton. + */ +struct skeleton +{ + /// Bone-space bind pose of the skeleton. + pose bind_pose; + + /// Inverse skeleton-space bind pose of the skeleton. + pose inverse_bind_pose; + + /// Maps bone names to bone identifiers. + std::unordered_map bone_map; +}; + +#endif // ANTKEEPER_ANIMATION_SKELETON_HPP diff --git a/src/engine/animation/spring.hpp b/src/engine/animation/spring.hpp new file mode 100644 index 0000000..a11e437 --- /dev/null +++ b/src/engine/animation/spring.hpp @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_SPRING_HPP +#define ANTKEEPER_SPRING_HPP + +#include + +/** + * Contains the variables required for numeric springing. + * + * @tparam T Value type. + * @tparam S Scalar type. + * + * @see spring() + * @see solve_numeric_spring() + */ +template +struct numeric_spring +{ + T x0; ///< Start value + T x1; ///< End value + T v; ///< Velocity + S z; ///< Damping ratio, which can be undamped (z = 0), underdamped (z < 1), critically damped (z = 1), or overdamped (z > 1). + S w; ///< Angular frequency of the oscillation, in radians per second (2pi = 1Hz). +}; + +/** + * Solves a number spring using the implicit Euler method. + * + * @tparam T Value type. + * @tparam S Scalar type. + * + * @param[in,out] x0 Start value, which will be oscillated by this function. + * @param[in,out] v Velocity, which will be modified by this function. + * @param[in] x1 End value. + * @param[in] z Damping ratio, which can be undamped (z = 0), underdamped (z < 1), critically damped (z = 1), or overdamped (z > 1). + * @param[in] w Angular frequency of the oscillation, in radians per second (2pi = 1Hz). + * @param[in] dt Delta time, in seconds. + */ +template +void spring(T& x0, T& v, const T& x1, S z, S w, S dt); + +/** + * Solves a number spring using the implicit Euler method. + * + * @param[in,out] ns Numeric spring to be sovled. + * @param dt Delta time, in seconds. + * + * @see spring() + */ +template +void solve_numeric_spring(numeric_spring& ns, S dt); + +/** + * Converts a frequency from hertz to radians per second. + * + * @param hz Frequency in hertz. + * @return Frequency in radians per second. + */ +template +T hz_to_rads(T hz); + +/** + * Converts a frequency from radians per second to hertz. + * + * @param rads Frequency in radians per second. + * @return Frequency in hertz. + */ +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) +{ + const S ww_dt = w * w * dt; + const S ww_dtdt = ww_dt * dt; + const S f = z * w * dt * S{2} + S{1}; + const T det_x = x0 * f + v * dt + x1 * ww_dtdt; + const T det_v = v + (x1 - x0) * ww_dt; + const S inv_det = S{1} / (f + ww_dtdt); + + x0 = det_x * inv_det; + v = det_v * inv_det; +} + +template +void solve_numeric_spring(numeric_spring& ns, S dt) +{ + spring(ns.x0, ns.v, ns.x1, ns.z, ns.w, dt); +} + +template +inline T hz_to_rads(T hz) +{ + return hz * math::two_pi; +} + +template +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/engine/animation/timeline.cpp b/src/engine/animation/timeline.cpp new file mode 100644 index 0000000..3361734 --- /dev/null +++ b/src/engine/animation/timeline.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include + +auto cue_compare = [](const typename timeline::cue& a, const typename timeline::cue& b) +{ + return std::get<0>(a) < std::get<0>(b); +}; + +timeline::timeline(): + cues(cue_compare), + position(0.0f), + autoremove(false) +{} + +void timeline::advance(float dt) +{ + auto lower_bound = cues.lower_bound({position, nullptr}); + auto upper_bound = cues.upper_bound({position + dt, nullptr}); + + for (auto iterator = lower_bound; iterator != upper_bound; ++iterator) + { + std::get<1>(*iterator)(); + } + + if (autoremove && lower_bound != upper_bound) + { + cues.erase(lower_bound, upper_bound); + } + + position += dt; +} + +void timeline::seek(float t) +{ + position = t; +} + +void timeline::add_cue(const cue& c) +{ + cues.emplace(c); +} + +void timeline::remove_cue(const cue& c) +{ + cues.erase(c); +} + +void timeline::remove_cues(float start, float end) +{ + auto lower_bound = cues.lower_bound({start, nullptr}); + auto upper_bound = cues.upper_bound({end, nullptr}); + cues.erase(lower_bound, upper_bound); +} + +void timeline::add_sequence(const sequence& s) +{ + for (const cue& c: s) + { + add_cue(c); + } +} + +void timeline::remove_sequence(const sequence& s) +{ + for (const cue& c: s) + { + remove_cue(c); + } +} + +void timeline::clear() +{ + cues.clear(); +} + +void timeline::set_autoremove(bool enabled) +{ + autoremove = enabled; +} + +typename timeline::sequence timeline::get_cues(float start, float end) const +{ + sequence s; + + auto lower_bound = cues.lower_bound({start, nullptr}); + auto upper_bound = cues.upper_bound({end, nullptr}); + for (auto iterator = lower_bound; iterator != upper_bound; ++iterator) + { + s.push_back(*iterator); + } + + return s; +} + diff --git a/src/animation/timeline.hpp b/src/engine/animation/timeline.hpp similarity index 100% rename from src/animation/timeline.hpp rename to src/engine/animation/timeline.hpp diff --git a/src/animation/tween.hpp b/src/engine/animation/tween.hpp similarity index 100% rename from src/animation/tween.hpp rename to src/engine/animation/tween.hpp diff --git a/src/engine/app/display-events.hpp b/src/engine/app/display-events.hpp new file mode 100644 index 0000000..612ba8c --- /dev/null +++ b/src/engine/app/display-events.hpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_APP_DISPLAY_EVENTS_HPP +#define ANTKEEPER_APP_DISPLAY_EVENTS_HPP + +#include + +namespace app { + +class display; + +/** + * Event generated when a display has been connected. + */ +struct display_connected_event +{ + /// Pointer to the display that has been connected. + const display* display; +}; + +/** + * Event generated when a display has been disconnected. + */ +struct display_disconnected_event +{ + /// Pointer to the display that has been disconnected. + const display* display; +}; + +/** + * Event generated when the orientation of a display has changed. + */ +struct display_orientation_changed_event +{ + /// Pointer to the display that has had it's orientation changed. + const display* display; + + /// Orientation of the display. + display_orientation orientation; +}; + +} // namespace app + +#endif // ANTKEEPER_APP_DISPLAY_EVENTS_HPP diff --git a/src/app/display-orientation.hpp b/src/engine/app/display-orientation.hpp similarity index 100% rename from src/app/display-orientation.hpp rename to src/engine/app/display-orientation.hpp diff --git a/src/engine/app/display.hpp b/src/engine/app/display.hpp new file mode 100644 index 0000000..8cb1a2a --- /dev/null +++ b/src/engine/app/display.hpp @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_APP_DISPLAY_HPP +#define ANTKEEPER_APP_DISPLAY_HPP + +#include +#include +#include +#include +#include + +namespace app { + +/** + * Virtual display. + */ +class display +{ +public: + /** + * Sets the index of the display. + * + * @param index Index of the display. + */ + inline void set_index(int index) noexcept + { + this->index = index; + } + + /** + * Sets the name of the display. + * + * @param name Name of the display. + */ + inline void set_name(const std::string& name) noexcept + { + this->name = name; + } + + /** + * Sets the bounds of the display. + * + * @param bounds Bounds of the display, in display units. + */ + inline void set_bounds(const geom::primitive::rectangle& bounds) noexcept + { + this->bounds = bounds; + } + + /** + * Sets the usable bounds of the display, which excludes areas reserved by the OS for things like menus or docks. + */ + inline void set_usable_bounds(const geom::primitive::rectangle& bounds) noexcept + { + this->usable_bounds = bounds; + } + + /** + * Sets the refresh rate of the display. + * + * @param rate Refresh rate, in Hz. + */ + inline void set_refresh_rate(int rate) noexcept + { + this->refresh_rate = rate; + } + + /** + * Sets the DPI of the display. + * + * @param dpi DPI. + */ + inline void set_dpi(float dpi) noexcept + { + this->dpi = dpi; + } + + /** + * Sets the orientation of the display. + * + * @param orientation Display orientation. + */ + inline void set_orientation(display_orientation orientation) noexcept + { + this->orientation = orientation; + } + + /// Returns the index of the display. + [[nodiscard]] inline const int& get_index() const noexcept + { + return index; + } + + /// Returns the name of the display. + [[nodiscard]] inline const std::string& get_name() const noexcept + { + return name; + } + + /// Returns the bounds of the display, in display units. + [[nodiscard]] inline const geom::primitive::rectangle& get_bounds() const noexcept + { + return bounds; + } + + /// Returns the usable bounds of the display, which excludes areas reserved by the OS for things like menus or docks, in display units. + [[nodiscard]] inline const geom::primitive::rectangle& get_usable_bounds() const noexcept + { + return usable_bounds; + } + + /// Returns the refresh rate of the display, in Hz. + [[nodiscard]] inline const int& get_refresh_rate() const noexcept + { + return refresh_rate; + } + + /// Returns the DPI of the display. + [[nodiscard]] inline const float& get_dpi() const noexcept + { + return dpi; + } + + /// Returns the current orientation of the display. + [[nodiscard]] inline const display_orientation& get_orientation() const noexcept + { + return orientation; + } + + /// Returns `true` if the display is connected, `false` otherwise. + [[nodiscard]] inline const bool& is_connected() const noexcept + { + return connected; + } + + /// Returns the channel through which display connected events are published. + [[nodiscard]] inline event::channel& get_connected_channel() noexcept + { + return connected_publisher.channel(); + } + + /// Returns the channel through which display disconnected events are published. + [[nodiscard]] inline event::channel& get_disconnected_channel() noexcept + { + return disconnected_publisher.channel(); + } + + /// Returns the channel through which display orientation changed events are published. + [[nodiscard]] inline event::channel& get_orientation_changed_channel() noexcept + { + return orientation_changed_publisher.channel(); + } + +private: + friend class window_manager; + friend class sdl_window_manager; + + int index; + std::string name; + geom::primitive::rectangle bounds; + geom::primitive::rectangle usable_bounds; + int refresh_rate; + float dpi; + display_orientation orientation; + bool connected; + + event::publisher connected_publisher; + event::publisher disconnected_publisher; + event::publisher orientation_changed_publisher; +}; + +} // namespace app + +#endif // ANTKEEPER_APP_DISPLAY_HPP diff --git a/src/engine/app/input-manager.cpp b/src/engine/app/input-manager.cpp new file mode 100644 index 0000000..e41a835 --- /dev/null +++ b/src/engine/app/input-manager.cpp @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include + +namespace app { + +input_manager* input_manager::instance() +{ + return new sdl_input_manager(); +} + +void input_manager::register_device(input::device& device) +{ + switch (device.get_device_type()) + { + case input::device_type::gamepad: + register_gamepad(static_cast(device)); + break; + + case input::device_type::keyboard: + register_keyboard(static_cast(device)); + break; + + case input::device_type::mouse: + register_mouse(static_cast(device)); + break; + + default: + //std::unreachable(); + break; + } +} + +void input_manager::register_gamepad(input::gamepad& device) +{ + // Connect gamepad event signals to the event queue + subscriptions.emplace(&device, device.get_connected_channel().subscribe(event_queue)); + subscriptions.emplace(&device, device.get_disconnected_channel().subscribe(event_queue)); + subscriptions.emplace(&device, device.get_axis_moved_channel().subscribe(event_queue)); + subscriptions.emplace(&device, device.get_button_pressed_channel().subscribe(event_queue)); + subscriptions.emplace(&device, device.get_button_released_channel().subscribe(event_queue)); + + // Add gamepad to list of gamepads + gamepads.emplace(&device); +} + +void input_manager::register_keyboard(input::keyboard& device) +{ + // Connect keyboard event signals to the event queue + subscriptions.emplace(&device, device.get_connected_channel().subscribe(event_queue)); + subscriptions.emplace(&device, device.get_disconnected_channel().subscribe(event_queue)); + subscriptions.emplace(&device, device.get_key_pressed_channel().subscribe(event_queue)); + subscriptions.emplace(&device, device.get_key_released_channel().subscribe(event_queue)); + + // Add keyboard to list of keyboards + keyboards.emplace(&device); +} + +void input_manager::register_mouse(input::mouse& device) +{ + // Connect mouse event signals to the event queue + subscriptions.emplace(&device, device.get_connected_channel().subscribe(event_queue)); + subscriptions.emplace(&device, device.get_disconnected_channel().subscribe(event_queue)); + subscriptions.emplace(&device, device.get_button_pressed_channel().subscribe(event_queue)); + subscriptions.emplace(&device, device.get_button_released_channel().subscribe(event_queue)); + subscriptions.emplace(&device, device.get_moved_channel().subscribe(event_queue)); + subscriptions.emplace(&device, device.get_scrolled_channel().subscribe(event_queue)); + + // Add mouse to list of mice + mice.emplace(&device); +} + +void input_manager::unregister_device(input::device& device) +{ + subscriptions.erase(&device); + + switch (device.get_device_type()) + { + case input::device_type::gamepad: + unregister_gamepad(static_cast(device)); + break; + + case input::device_type::keyboard: + unregister_keyboard(static_cast(device)); + break; + + case input::device_type::mouse: + unregister_mouse(static_cast(device)); + break; + + default: + //std::unreachable(); + break; + } +} + +void input_manager::unregister_gamepad(input::gamepad& gamepad) +{ + gamepads.erase(&gamepad); +} + +void input_manager::unregister_keyboard(input::keyboard& keyboard) +{ + keyboards.erase(&keyboard); +} + +void input_manager::unregister_mouse(input::mouse& mouse) +{ + mice.erase(&mouse); +} + +} // namespace app diff --git a/src/engine/app/input-manager.hpp b/src/engine/app/input-manager.hpp new file mode 100644 index 0000000..6a7ea3f --- /dev/null +++ b/src/engine/app/input-manager.hpp @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_APP_INPUT_MANAGER_HPP +#define ANTKEEPER_APP_INPUT_MANAGER_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace app { + +/** + * Manages virtual input devices. + */ +class input_manager +{ +public: + /** + * Allocates and returns an input manager. + */ + static input_manager* instance(); + + /// Destructs an input manager. + virtual ~input_manager() = default; + + /** + * Processes input events. + */ + virtual void update() = 0; + + /** + * Makes the cursor visible. + */ + virtual void show_cursor() = 0; + + /** + * Makes the cursor invisible. + */ + virtual void hide_cursor() = 0; + + /** + * Returns the event queue associated with registered input devices. + */ + [[nodiscard]] inline const ::event::queue& get_event_queue() const noexcept + { + return event_queue; + } + + /** + * Returns the event queue associated with registered input devices. + */ + [[nodiscard]] inline ::event::queue& get_event_queue() noexcept + { + return event_queue; + } + + /// Returns the set of registered gamepads. + [[nodiscard]] inline const std::unordered_set& get_gamepads() noexcept + { + return gamepads; + } + + /// Returns the set of registered keyboards. + [[nodiscard]] inline const std::unordered_set& get_keyboards() noexcept + { + return keyboards; + } + + /// Returns the set of registered mice. + [[nodiscard]] inline const std::unordered_set& get_mice() noexcept + { + return mice; + } + +protected: + /** + * Registers an input device. + * + * @param device Input device to register. + */ + /// @{ + void register_device(input::device& device); + void register_gamepad(input::gamepad& device); + void register_keyboard(input::keyboard& device); + void register_mouse(input::mouse& device); + /// @} + + /** + * Unregisters an input device. + * + * @param device Input device to unregister. + */ + /// @{ + void unregister_device(input::device& device); + void unregister_gamepad(input::gamepad& device); + void unregister_keyboard(input::keyboard& device); + void unregister_mouse(input::mouse& device); + /// @} + + ::event::queue event_queue; + +private: + std::multimap> subscriptions; + std::unordered_set gamepads; + std::unordered_set keyboards; + std::unordered_set mice; +}; + +} // namespace app + +#endif // ANTKEEPER_APP_INPUT_MANAGER_HPP diff --git a/src/engine/app/sdl/sdl-input-manager.cpp b/src/engine/app/sdl/sdl-input-manager.cpp new file mode 100644 index 0000000..22fab13 --- /dev/null +++ b/src/engine/app/sdl/sdl-input-manager.cpp @@ -0,0 +1,340 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +namespace app { + +sdl_input_manager::sdl_input_manager() +{ + // Init SDL joystick and controller subsystems + debug::log::trace("Initializing SDL joystick and controller subsystems..."); + if (SDL_InitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) != 0) + { + debug::log::error("Failed to initialize SDL joystick and controller subsytems: {}", SDL_GetError()); + throw std::runtime_error("Failed to initialize SDL joystick and controller subsytems"); + } + else + { + debug::log::trace("Initialized SDL joystick and controller subsystems"); + } + + // Register keyboard and mouse + register_keyboard(keyboard); + register_mouse(mouse); + + // Generate keyboard and mouse device connected events + keyboard.connect(); + mouse.connect(); +} + +sdl_input_manager::~sdl_input_manager() +{ + // Quit SDL joystick and controller subsystems + debug::log::trace("Quitting SDL joystick and controller subsystems..."); + SDL_QuitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER); + debug::log::trace("Quit SDL joystick and controller subsystems..."); +} + +void sdl_input_manager::update() +{ + // Active modifier keys + std::uint16_t sdl_key_mod = KMOD_NONE; + std::uint16_t modifier_keys = input::modifier_key::none; + + // Gather SDL events from event queue + SDL_PumpEvents(); + + // Handle OS events + for (;;) + { + // Get next display or window event + SDL_Event event; + int status = SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_FIRSTEVENT, SDL_LOCALECHANGED); + + if (!status) + { + break; + } + else if (status < 0) + { + debug::log::error("Failed to peep SDL events: {}", SDL_GetError()); + throw std::runtime_error("Failed to peep SDL events"); + } + + switch (event.type) + { + case SDL_QUIT: + debug::log::debug("Application quit requested"); + this->event_queue.enqueue({}); + break; + + default: + break; + } + } + + // Handle keyboard, mouse, and gamepad events + for (;;) + { + // Get next display or window event + SDL_Event event; + int status = SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_KEYDOWN, SDL_LASTEVENT); + + if (!status) + { + break; + } + else if (status < 0) + { + debug::log::error("Failed to peep SDL events: {}", SDL_GetError()); + throw std::runtime_error("Failed to peep SDL events"); + } + + switch (event.type) + { + [[likely]] case SDL_MOUSEMOTION: + { + mouse.move({event.motion.x, event.motion.y}, {event.motion.xrel, event.motion.yrel}); + break; + } + + [[likely]] case SDL_KEYDOWN: + case SDL_KEYUP: + { + // Get scancode of key + const input::scancode scancode = static_cast(event.key.keysym.scancode); + + // Rebuild modifier keys bit mask + if (sdl_key_mod != event.key.keysym.mod) + { + sdl_key_mod = event.key.keysym.mod; + + modifier_keys = input::modifier_key::none; + if (sdl_key_mod & KMOD_LSHIFT) + modifier_keys |= input::modifier_key::left_shift; + if (sdl_key_mod & KMOD_RSHIFT) + modifier_keys |= input::modifier_key::right_shift; + if (sdl_key_mod & KMOD_LCTRL) + modifier_keys |= input::modifier_key::left_ctrl; + if (sdl_key_mod & KMOD_RCTRL) + modifier_keys |= input::modifier_key::right_ctrl; + if (sdl_key_mod & KMOD_LALT) + modifier_keys |= input::modifier_key::left_alt; + if (sdl_key_mod & KMOD_RALT) + modifier_keys |= input::modifier_key::right_alt; + if (sdl_key_mod & KMOD_LGUI) + modifier_keys |= input::modifier_key::left_gui; + if (sdl_key_mod & KMOD_RGUI) + modifier_keys |= input::modifier_key::right_gui; + if (sdl_key_mod & KMOD_NUM) + modifier_keys |= input::modifier_key::num_lock; + if (sdl_key_mod & KMOD_CAPS) + modifier_keys |= input::modifier_key::caps_lock; + if (sdl_key_mod & KMOD_SCROLL) + modifier_keys |= input::modifier_key::scroll_lock; + if (sdl_key_mod & KMOD_MODE) + modifier_keys |= input::modifier_key::alt_gr; + } + + if (event.type == SDL_KEYDOWN) + { + keyboard.press(scancode, modifier_keys, (event.key.repeat > 0)); + } + else + { + keyboard.release(scancode, modifier_keys); + } + + break; + } + + case SDL_MOUSEWHEEL: + { + const float flip = (event.wheel.direction == SDL_MOUSEWHEEL_FLIPPED) ? -1.0f : 1.0f; + mouse.scroll({event.wheel.preciseX * flip, event.wheel.preciseY * flip}); + break; + } + + case SDL_MOUSEBUTTONDOWN: + { + mouse.press(static_cast(event.button.button)); + break; + } + + case SDL_MOUSEBUTTONUP: + { + mouse.release(static_cast(event.button.button)); + break; + } + + [[likely]] case SDL_CONTROLLERAXISMOTION: + { + if (event.caxis.axis != SDL_CONTROLLER_AXIS_INVALID) + { + if (auto it = gamepad_map.find(event.cdevice.which); it != gamepad_map.end()) + { + // Map axis position onto `[-1, 1]`. + const float position = math::map + ( + static_cast(event.caxis.value), + static_cast(std::numeric_limits::min()), + static_cast(std::numeric_limits::max()), + -1.0f, + 1.0f + ); + + // Generate gamepad axis moved event + it->second->move(static_cast(event.caxis.axis), position); + } + } + break; + } + + case SDL_CONTROLLERBUTTONDOWN: + { + if (event.cbutton.button != SDL_CONTROLLER_BUTTON_INVALID) + { + if (auto it = gamepad_map.find(event.cdevice.which); it != gamepad_map.end()) + { + it->second->press(static_cast(event.cbutton.button)); + } + } + break; + } + + case SDL_CONTROLLERBUTTONUP: + { + if (event.cbutton.button != SDL_CONTROLLER_BUTTON_INVALID) + { + if (auto it = gamepad_map.find(event.cdevice.which); it != gamepad_map.end()) + { + it->second->release(static_cast(event.cbutton.button)); + } + } + break; + } + + [[unlikely]] case SDL_CONTROLLERDEVICEADDED: + { + if (SDL_IsGameController(event.cdevice.which)) + { + SDL_GameController* sdl_controller = SDL_GameControllerOpen(event.cdevice.which); + if (sdl_controller) + { + // Get gamepad name + const char* controller_name = SDL_GameControllerNameForIndex(event.cdevice.which); + if (!controller_name) + { + controller_name = ""; + } + + if (auto it = gamepad_map.find(event.cdevice.which); it != gamepad_map.end()) + { + // Gamepad reconnected + debug::log::info("Reconnected gamepad {}", event.cdevice.which); + it->second->connect(); + } + else + { + // Get gamepad GUID + SDL_Joystick* sdl_joystick = SDL_GameControllerGetJoystick(sdl_controller); + SDL_JoystickGUID sdl_guid = SDL_JoystickGetGUID(sdl_joystick); + + // Copy into UUID struct + ::uuid gamepad_uuid; + std::memcpy(gamepad_uuid.data.data(), sdl_guid.data, gamepad_uuid.data.size()); + + debug::log::info("Connected gamepad {}; name: \"{}\"; UUID: {}", event.cdevice.which, controller_name, gamepad_uuid.string()); + + // Create new gamepad + input::gamepad* gamepad = new input::gamepad(); + gamepad->set_uuid(gamepad_uuid); + + // Add gamepad to gamepad map + gamepad_map[event.cdevice.which] = gamepad; + + // Register gamepad + register_device(*gamepad); + + // Generate gamepad connected event + gamepad->connect(); + } + } + else + { + debug::log::error("Failed to connect gamepad {}: {}", event.cdevice.which, SDL_GetError()); + SDL_ClearError(); + } + } + + break; + } + + [[unlikely]] case SDL_CONTROLLERDEVICEREMOVED: + { + SDL_GameController* sdl_controller = SDL_GameControllerFromInstanceID(event.cdevice.which); + + if (sdl_controller) + { + SDL_GameControllerClose(sdl_controller); + if (auto it = gamepad_map.find(event.cdevice.which); it != gamepad_map.end()) + { + it->second->disconnect(); + } + + debug::log::info("Disconnected gamepad {}", event.cdevice.which); + } + + break; + } + + default: + break; + } + } + + // Flush event queue + this->event_queue.flush(); +} + +void sdl_input_manager::show_cursor() +{ + if (SDL_ShowCursor(SDL_ENABLE) < 0) + { + debug::log::error("Failed to show cursor: \"{}\"", SDL_GetError()); + SDL_ClearError(); + } +} + +void sdl_input_manager::hide_cursor() +{ + if (SDL_ShowCursor(SDL_DISABLE) < 0) + { + debug::log::error("Failed to hide cursor: \"{}\"", SDL_GetError()); + SDL_ClearError(); + } +} + +} // namespace app diff --git a/src/engine/app/sdl/sdl-input-manager.hpp b/src/engine/app/sdl/sdl-input-manager.hpp new file mode 100644 index 0000000..1942fcf --- /dev/null +++ b/src/engine/app/sdl/sdl-input-manager.hpp @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_APP_SDL_INPUT_MANAGER_HPP +#define ANTKEEPER_APP_SDL_INPUT_MANAGER_HPP + +#include + +namespace app { + +class sdl_window; + +/** + * + */ +class sdl_input_manager: public input_manager +{ +public: + /** + * Constructs an SDL input manager. + */ + sdl_input_manager(); + + /** + * Destructs an SDL input manager. + */ + virtual ~sdl_input_manager(); + + virtual void update(); + virtual void show_cursor(); + virtual void hide_cursor(); + +private: + input::keyboard keyboard; + input::mouse mouse; + std::unordered_map gamepad_map; +}; + +} // namespace app + +#endif // ANTKEEPER_APP_SDL_INPUT_MANAGER_HPP diff --git a/src/engine/app/sdl/sdl-window-manager.cpp b/src/engine/app/sdl/sdl-window-manager.cpp new file mode 100644 index 0000000..2ea89f6 --- /dev/null +++ b/src/engine/app/sdl/sdl-window-manager.cpp @@ -0,0 +1,498 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include +#include +#include + +namespace app { + +sdl_window_manager::sdl_window_manager() +{ + // Init SDL events and video subsystems + debug::log::trace("Initializing SDL events and video subsystems..."); + if (SDL_InitSubSystem(SDL_INIT_EVENTS | SDL_INIT_VIDEO) != 0) + { + debug::log::fatal("Failed to initialize SDL events and video subsystems: {}", SDL_GetError()); + throw std::runtime_error("Failed to initialize SDL events and video subsystems"); + } + debug::log::trace("Initialized SDL events and video subsystems"); + + // Query displays + const int display_count = SDL_GetNumVideoDisplays(); + if (display_count < 1) + { + debug::log::warning("No displays detected: {}", SDL_GetError()); + } + else + { + // Allocate displays + displays.resize(display_count); + debug::log::info("Display count: {}", display_count); + + for (int i = 0; i < display_count; ++i) + { + // Update display state + update_display(i); + + // Log display information + display& display = displays[i]; + const auto display_resolution = display.get_bounds().size(); + debug::log::info("Display {} name: \"{}\"; resolution: {}x{}; refresh rate: {}Hz; DPI: {}", i, display.get_name(), display_resolution.x(), display_resolution.y(), display.get_refresh_rate(), display.get_dpi()); + } + } + + // Load OpenGL library + debug::log::trace("Loading OpenGL library..."); + if (SDL_GL_LoadLibrary(nullptr) != 0) + { + debug::log::fatal("Failed to load OpenGL library: {}", SDL_GetError()); + throw std::runtime_error("Failed to load OpenGL library"); + } + debug::log::trace("Loaded OpenGL library"); + + // Set OpenGL-related window creation hints + SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, config::opengl_version_major); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, config::opengl_version_minor); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + SDL_GL_SetAttribute(SDL_GL_RED_SIZE, config::opengl_min_red_size); + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, config::opengl_min_green_size); + SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, config::opengl_min_blue_size); + SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, config::opengl_min_alpha_size); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, config::opengl_min_depth_size); + SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, config::opengl_min_stencil_size); +} + +sdl_window_manager::~sdl_window_manager() +{ + // Quit SDL video subsystem + debug::log::trace("Quitting SDL video subsystem..."); + SDL_QuitSubSystem(SDL_INIT_VIDEO); + debug::log::trace("Quit SDL video subsystem"); +} + +window* sdl_window_manager::create_window +( + const std::string& title, + const math::vector& windowed_position, + const math::vector& windowed_size, + bool maximized, + bool fullscreen, + bool v_sync +) +{ + // Create new window + app::sdl_window* window = new app::sdl_window + ( + title, + windowed_position, + windowed_size, + maximized, + fullscreen, + v_sync + ); + + // Map internal SDL window to window + window_map[window->internal_window] = window; + + return window; +} + +void sdl_window_manager::update() +{ + // Gather SDL events from event queue + SDL_PumpEvents(); + + for (;;) + { + // Get next window or display event + SDL_Event event; + int status = SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_DISPLAYEVENT, SDL_SYSWMEVENT); + + if (!status) + { + break; + } + else if (status < 0) + { + debug::log::error("Failed to peep SDL events: {}", SDL_GetError()); + throw std::runtime_error("Failed to peep SDL events"); + } + + // Handle event + if (event.type == SDL_WINDOWEVENT) + { + switch (event.window.event) + { + case SDL_WINDOWEVENT_SIZE_CHANGED: + { + // Get window + SDL_Window* internal_window = SDL_GetWindowFromID(event.window.windowID); + app::sdl_window* window = get_window(internal_window); + + // Update window state + window->size = {event.window.data1, event.window.data2}; + const auto window_flags = SDL_GetWindowFlags(internal_window); + if (!(window_flags & SDL_WINDOW_MAXIMIZED) && !(window_flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_FULLSCREEN_DESKTOP))) + { + window->windowed_size = window->size; + } + SDL_GL_GetDrawableSize(internal_window, &window->viewport_size.x(), &window->viewport_size.y()); + window->rasterizer->context_resized(window->viewport_size.x(), window->viewport_size.y()); + + // Log window resized event + debug::log::debug("Window {} resized to {}x{}", event.window.windowID, event.window.data1, event.window.data2); + + // Publish window resized event + window->resized_publisher.publish({window, window->size}); + break; + } + + case SDL_WINDOWEVENT_MOVED: + { + // Get window + SDL_Window* internal_window = SDL_GetWindowFromID(event.window.windowID); + app::sdl_window* window = get_window(internal_window); + + // Update window state + window->position = {event.window.data1, event.window.data2}; + const auto window_flags = SDL_GetWindowFlags(internal_window); + if (!(window_flags & SDL_WINDOW_MAXIMIZED) && !(window_flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_FULLSCREEN_DESKTOP))) + { + window->windowed_position = window->position; + } + + // Log window moved event + debug::log::debug("Window {} moved to ({}, {})", event.window.windowID, event.window.data1, event.window.data2); + + // Publish window moved event + window->moved_publisher.publish({window, window->position}); + break; + } + + case SDL_WINDOWEVENT_FOCUS_GAINED: + { + // Get window + SDL_Window* internal_window = SDL_GetWindowFromID(event.window.windowID); + app::sdl_window* window = get_window(internal_window); + + // Log window focus gained event + debug::log::debug("Window {} gained focus", event.window.windowID); + + // Publish window focus gained event + window->focus_changed_publisher.publish({window, true}); + break; + } + + case SDL_WINDOWEVENT_FOCUS_LOST: + { + // Get window + SDL_Window* internal_window = SDL_GetWindowFromID(event.window.windowID); + app::sdl_window* window = get_window(internal_window); + + // Log window focus lost event + debug::log::debug("Window {} lost focus", event.window.windowID); + + // Publish window focus lost event + window->focus_changed_publisher.publish({window, false}); + break; + } + + case SDL_WINDOWEVENT_MAXIMIZED: + { + // Get window + SDL_Window* internal_window = SDL_GetWindowFromID(event.window.windowID); + app::sdl_window* window = get_window(internal_window); + + // Update window state + window->maximized = true; + + // Log window focus gained event + debug::log::debug("Window {} maximized", event.window.windowID); + + // Publish window maximized event + window->maximized_publisher.publish({window}); + break; + } + + case SDL_WINDOWEVENT_RESTORED: + { + // Get window + SDL_Window* internal_window = SDL_GetWindowFromID(event.window.windowID); + app::sdl_window* window = get_window(internal_window); + + // Update window state + window->maximized = false; + + // Log window restored event + debug::log::debug("Window {} restored", event.window.windowID); + + // Publish window restored event + window->restored_publisher.publish({window}); + break; + } + + case SDL_WINDOWEVENT_MINIMIZED: + { + // Get window + SDL_Window* internal_window = SDL_GetWindowFromID(event.window.windowID); + app::sdl_window* window = get_window(internal_window); + + // Log window focus gained event + debug::log::debug("Window {} minimized", event.window.windowID); + + // Publish window minimized event + window->minimized_publisher.publish({window}); + break; + } + + [[unlikely]] case SDL_WINDOWEVENT_CLOSE: + { + // Get window + SDL_Window* internal_window = SDL_GetWindowFromID(event.window.windowID); + app::sdl_window* window = get_window(internal_window); + + // Log window closed event + debug::log::debug("Window {} closed", event.window.windowID); + + // Publish window closed event + window->closed_publisher.publish({window}); + break; + } + + default: + break; + } + } + else if (event.type == SDL_DISPLAYEVENT) + { + switch (event.display.event) + { + case SDL_DISPLAYEVENT_CONNECTED: + if (event.display.display < displays.size()) + { + // Get previously connected display + display& display = displays[event.display.display]; + + // Update display state + display.connected = true; + + // Log display (re)connected event + debug::log::info("Reconnected display {}", event.display.display); + + // Publish display (re)connected event + display.connected_publisher.publish({&display}); + } + else if (event.display.display == displays.size()) + { + // Allocate new display + displays.resize(displays.size() + 1); + display& display = displays[event.display.display]; + + // Update display state + update_display(event.display.display); + + // Log display info + const auto display_resolution = display.get_bounds().size(); + debug::log::info("Connected display {}; name: \"{}\"; resolution: {}x{}; refresh rate: {}Hz; DPI: {}", event.display.display, display.get_name(), display_resolution.x(), display_resolution.y(), display.get_refresh_rate(), display.get_dpi()); + + // Publish display connected event + display.connected_publisher.publish({&display}); + } + else + { + debug::log::error("Index of connected display ({}) out of range", event.display.display); + } + break; + + case SDL_DISPLAYEVENT_DISCONNECTED: + if (event.display.display < displays.size()) + { + // Get display + display& display = displays[event.display.display]; + + // Update display state + display.connected = false; + + // Log display disconnected event + debug::log::info("Disconnected display {}", event.display.display); + + // Publish display disconnected event + display.disconnected_publisher.publish({&display}); + } + else + { + debug::log::error("Index of disconnected display ({}) out of range", event.display.display); + } + break; + + case SDL_DISPLAYEVENT_ORIENTATION: + if (event.display.display < displays.size()) + { + // Get display + display& display = displays[event.display.display]; + + // Update display state + switch (event.display.data1) + { + case SDL_ORIENTATION_LANDSCAPE: + display.set_orientation(display_orientation::landscape); + break; + case SDL_ORIENTATION_LANDSCAPE_FLIPPED: + display.set_orientation(display_orientation::landscape_flipped); + break; + case SDL_ORIENTATION_PORTRAIT: + display.set_orientation(display_orientation::portrait); + break; + case SDL_ORIENTATION_PORTRAIT_FLIPPED: + display.set_orientation(display_orientation::portrait_flipped); + break; + default: + display.set_orientation(display_orientation::unknown); + break; + } + + // Log display orientation changed event + debug::log::info("Display {} orientation changed", event.display.display); + + // Publish display orientation changed event + display.orientation_changed_publisher.publish({&display, display.get_orientation()}); + } + else + { + debug::log::error("Index of orientation-changed display ({}) out of range", event.display.display); + } + break; + + default: + break; + } + } + } +} + +sdl_window* sdl_window_manager::get_window(SDL_Window* internal_window) +{ + sdl_window* window = nullptr; + if (auto i = window_map.find(internal_window); i != window_map.end()) + { + window = i->second; + } + else + { + throw std::runtime_error("SDL window unrecognized by SDL window manager"); + } + return window; +} + +std::size_t sdl_window_manager::get_display_count() const +{ + return displays.size(); +} + +const display& sdl_window_manager::get_display(std::size_t index) const +{ + return displays[index]; +} + +void sdl_window_manager::update_display(int sdl_display_index) +{ + // Query display mode + SDL_DisplayMode sdl_display_mode; + if (SDL_GetDesktopDisplayMode(sdl_display_index, &sdl_display_mode) != 0) + { + debug::log::error("Failed to get mode of display {}: {}", sdl_display_index, SDL_GetError()); + SDL_ClearError(); + sdl_display_mode = {0, 0, 0, 0, nullptr}; + } + + // Query display name + const char* sdl_display_name = SDL_GetDisplayName(sdl_display_index); + if (!sdl_display_name) + { + debug::log::warning("Failed to get name of display {}: {}", sdl_display_index, SDL_GetError()); + SDL_ClearError(); + sdl_display_name = nullptr; + } + + // Query display bounds + SDL_Rect sdl_display_bounds; + if (SDL_GetDisplayBounds(sdl_display_index, &sdl_display_bounds) != 0) + { + debug::log::warning("Failed to get bounds of display {}: {}", sdl_display_index, SDL_GetError()); + SDL_ClearError(); + sdl_display_bounds = {0, 0, sdl_display_mode.w, sdl_display_mode.h}; + } + + // Query display usable bounds + SDL_Rect sdl_display_usable_bounds; + if (SDL_GetDisplayUsableBounds(sdl_display_index, &sdl_display_usable_bounds) != 0) + { + debug::log::warning("Failed to get usable bounds of display {}: {}", sdl_display_index, SDL_GetError()); + SDL_ClearError(); + sdl_display_usable_bounds = sdl_display_bounds; + } + + // Query display DPI + float sdl_display_dpi; + if (SDL_GetDisplayDPI(sdl_display_index, &sdl_display_dpi, nullptr, nullptr) != 0) + { + debug::log::warning("Failed to get DPI of display {}: {}", sdl_display_index, SDL_GetError()); + SDL_ClearError(); + sdl_display_dpi = 0.0f; + } + + // Query display orientation + SDL_DisplayOrientation sdl_display_orientation = SDL_GetDisplayOrientation(sdl_display_index); + + // Update display properties + display& display = displays[sdl_display_index]; + display.set_index(static_cast(sdl_display_index)); + display.set_name(sdl_display_name ? sdl_display_name : std::string()); + display.set_bounds({{sdl_display_bounds.x, sdl_display_bounds.y}, {sdl_display_bounds.x + sdl_display_bounds.w, sdl_display_bounds.y + sdl_display_bounds.h}}); + display.set_usable_bounds({{sdl_display_usable_bounds.x, sdl_display_usable_bounds.y}, {sdl_display_usable_bounds.x + sdl_display_usable_bounds.w, sdl_display_usable_bounds.y + sdl_display_usable_bounds.h}}); + display.set_refresh_rate(sdl_display_mode.refresh_rate); + display.set_dpi(sdl_display_dpi); + switch (sdl_display_orientation) + { + case SDL_ORIENTATION_LANDSCAPE: + display.set_orientation(display_orientation::landscape); + break; + case SDL_ORIENTATION_LANDSCAPE_FLIPPED: + display.set_orientation(display_orientation::landscape_flipped); + break; + case SDL_ORIENTATION_PORTRAIT: + display.set_orientation(display_orientation::portrait); + break; + case SDL_ORIENTATION_PORTRAIT_FLIPPED: + display.set_orientation(display_orientation::portrait_flipped); + break; + default: + display.set_orientation(display_orientation::unknown); + break; + } + display.connected = true; +} + +} // namespace app diff --git a/src/engine/app/sdl/sdl-window-manager.hpp b/src/engine/app/sdl/sdl-window-manager.hpp new file mode 100644 index 0000000..bea10f4 --- /dev/null +++ b/src/engine/app/sdl/sdl-window-manager.hpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_APP_SDL_WINDOW_MANAGER_HPP +#define ANTKEEPER_APP_SDL_WINDOW_MANAGER_HPP + +#include +#include +#include +#include +#include + +namespace app { + +class sdl_window; + +/** + * + */ +class sdl_window_manager: public window_manager +{ +public: + /** + * Constructs an SDL window manager. + */ + sdl_window_manager(); + + /** + * Destructs an SDL window manager. + */ + virtual ~sdl_window_manager(); + + virtual void update(); + + /// @copydoc window::window() + [[nodiscard]] virtual window* create_window + ( + const std::string& title, + const math::vector& windowed_position, + const math::vector& windowed_size, + bool maximized, + bool fullscreen, + bool v_sync + ); + + [[nodiscard]] virtual std::size_t get_display_count() const; + [[nodiscard]] virtual const display& get_display(std::size_t index) const; + +private: + sdl_window* get_window(SDL_Window* internal_window); + void update_display(int sdl_display_index); + + std::vector displays; + std::unordered_map window_map; +}; + +} // namespace app + +#endif // ANTKEEPER_APP_SDL_WINDOW_MANAGER_HPP diff --git a/src/engine/app/sdl/sdl-window.cpp b/src/engine/app/sdl/sdl-window.cpp new file mode 100644 index 0000000..57e4cbe --- /dev/null +++ b/src/engine/app/sdl/sdl-window.cpp @@ -0,0 +1,296 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include +#include +#include + +namespace app { + +sdl_window::sdl_window +( + const std::string& title, + const math::vector& windowed_position, + const math::vector& windowed_size, + bool maximized, + bool fullscreen, + bool v_sync +) +{ + // Determine SDL window creation flags + Uint32 window_flags = SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE; + if (maximized) + { + window_flags |= SDL_WINDOW_MAXIMIZED; + } + if (fullscreen) + { + window_flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; + } + + // Create SDL window + debug::log::trace("Creating SDL window..."); + internal_window = SDL_CreateWindow + ( + title.c_str(), + windowed_position.x(), + windowed_position.y(), + windowed_size.x(), + windowed_size.y(), + window_flags + ); + if (!internal_window) + { + debug::log::fatal("Failed to create SDL window: {}", SDL_GetError()); + throw std::runtime_error("Failed to create SDL window"); + } + debug::log::trace("Created SDL window"); + + // Create OpenGL context + debug::log::trace("Creating OpenGL context..."); + internal_context = SDL_GL_CreateContext(internal_window); + if (!internal_context) + { + debug::log::fatal("Failed to create OpenGL context: {}", SDL_GetError()); + throw std::runtime_error("Failed to create OpenGL context"); + } + debug::log::trace("Created OpenGL context"); + + // Query OpenGL context info + int opengl_context_version_major = -1; + int opengl_context_version_minor = -1; + int opengl_context_red_size = -1; + int opengl_context_green_size = -1; + int opengl_context_blue_size = -1; + int opengl_context_alpha_size = -1; + int opengl_context_depth_size = -1; + int opengl_context_stencil_size = -1; + SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &opengl_context_version_major); + SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &opengl_context_version_minor); + SDL_GL_GetAttribute(SDL_GL_RED_SIZE, &opengl_context_red_size); + SDL_GL_GetAttribute(SDL_GL_GREEN_SIZE, &opengl_context_green_size); + SDL_GL_GetAttribute(SDL_GL_BLUE_SIZE, &opengl_context_blue_size); + SDL_GL_GetAttribute(SDL_GL_ALPHA_SIZE, &opengl_context_alpha_size); + SDL_GL_GetAttribute(SDL_GL_DEPTH_SIZE, &opengl_context_depth_size); + SDL_GL_GetAttribute(SDL_GL_STENCIL_SIZE, &opengl_context_stencil_size); + + // Log OpenGL context info + debug::log::info + ( + "OpenGL context version: {}.{}; format: R{}G{}B{}A{}D{}S{}", + opengl_context_version_major, + opengl_context_version_minor, + opengl_context_red_size, + opengl_context_green_size, + opengl_context_blue_size, + opengl_context_alpha_size, + opengl_context_depth_size, + opengl_context_stencil_size + ); + + // Compare OpenGL context version with requested version + if (opengl_context_version_major != config::opengl_version_major || + opengl_context_version_minor != config::opengl_version_minor) + { + debug::log::warning("Requested OpenGL context version {}.{} but got version {}.{}", config::opengl_version_major, config::opengl_version_minor, opengl_context_version_major, opengl_context_version_minor); + } + + // Compare OpenGL context format with requested format + if (opengl_context_red_size < config::opengl_min_red_size || + opengl_context_green_size < config::opengl_min_green_size || + opengl_context_blue_size < config::opengl_min_blue_size || + opengl_context_alpha_size < config::opengl_min_alpha_size || + opengl_context_depth_size < config::opengl_min_depth_size || + opengl_context_stencil_size < config::opengl_min_stencil_size) + { + debug::log::warning + ( + "OpenGL context format (R{}G{}B{}A{}D{}S{}) does not meet minimum requested format (R{}G{}B{}A{}D{}S{})", + opengl_context_red_size, + opengl_context_green_size, + opengl_context_blue_size, + opengl_context_alpha_size, + opengl_context_depth_size, + opengl_context_stencil_size, + config::opengl_min_red_size, + config::opengl_min_green_size, + config::opengl_min_blue_size, + config::opengl_min_alpha_size, + config::opengl_min_depth_size, + config::opengl_min_stencil_size + ); + } + + // Load OpenGL functions via GLAD + debug::log::trace("Loading OpenGL functions..."); + if (!gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress)) + { + debug::log::fatal("Failed to load OpenGL functions", SDL_GetError()); + throw std::runtime_error("Failed to load OpenGL functions"); + } + debug::log::trace("Loaded OpenGL functions"); + + // Log OpenGL information + debug::log::info + ( + "OpenGL vendor: {}; renderer: {}; version: {}; shading language version: {}", + reinterpret_cast(glGetString(GL_VENDOR)), + reinterpret_cast(glGetString(GL_RENDERER)), + reinterpret_cast(glGetString(GL_VERSION)), + reinterpret_cast(glGetString(GL_SHADING_LANGUAGE_VERSION)) + ); + + // Fill window with color + //glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + swap_buffers(); + + // Enable or disable v-sync + set_v_sync(v_sync); + + // Update window state + this->title = title; + this->windowed_position = windowed_position; + this->windowed_size = windowed_size; + this->maximized = maximized; + this->fullscreen = fullscreen; + SDL_GetWindowPosition(internal_window, &this->position.x(), &this->position.y()); + SDL_GetWindowSize(internal_window, &this->size.x(), &this->size.y()); + SDL_GetWindowMinimumSize(internal_window, &this->minimum_size.x(), &this->minimum_size.y()); + SDL_GetWindowMaximumSize(internal_window, &this->maximum_size.x(), &this->maximum_size.y()); + SDL_GL_GetDrawableSize(internal_window, &this->viewport_size.x(), &this->viewport_size.y()); + + // Allocate rasterizer + this->rasterizer = new gl::rasterizer(); +} + +sdl_window::~sdl_window() +{ + // Destruct rasterizer + delete rasterizer; + + // Destruct the OpenGL context + SDL_GL_DeleteContext(internal_context); + + // Destruct the SDL window + SDL_DestroyWindow(internal_window); +} + +void sdl_window::set_title(const std::string& title) +{ + SDL_SetWindowTitle(internal_window, title.c_str()); + this->title = title; +} + +void sdl_window::set_position(const math::vector& position) +{ + SDL_SetWindowPosition(internal_window, position.x(), position.y()); +} + +void sdl_window::set_size(const math::vector& size) +{ + SDL_SetWindowSize(internal_window, size.x(), size.y()); +} + +void sdl_window::set_minimum_size(const math::vector& size) +{ + SDL_SetWindowMinimumSize(internal_window, size.x(), size.y()); + this->minimum_size = size; +} + +void sdl_window::set_maximum_size(const math::vector& size) +{ + SDL_SetWindowMaximumSize(internal_window, size.x(), size.y()); + this->maximum_size = size; +} + +void sdl_window::set_maximized(bool maximized) +{ + if (maximized) + { + SDL_MaximizeWindow(internal_window); + } + else + { + SDL_RestoreWindow(internal_window); + } +} + +void sdl_window::set_fullscreen(bool fullscreen) +{ + //SDL_HideWindow(internal_window); + SDL_SetWindowFullscreen(internal_window, (fullscreen) ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0); + //SDL_ShowWindow(internal_window); + this->fullscreen = fullscreen; +} + +void sdl_window::set_v_sync(bool v_sync) +{ + if (v_sync) + { + debug::log::trace("Enabling adaptive v-sync..."); + if (SDL_GL_SetSwapInterval(-1) != 0) + { + debug::log::error("Failed to enable adaptive v-sync: {}", SDL_GetError()); + debug::log::trace("Enabling synchronized v-sync..."); + if (SDL_GL_SetSwapInterval(1) != 0) + { + debug::log::error("Failed to enable synchronized v-sync: {}", SDL_GetError()); + v_sync = false; + } + else + { + debug::log::debug("Enabled synchronized v-sync"); + } + } + else + { + debug::log::debug("Enabled adaptive v-sync"); + } + } + else + { + debug::log::trace("Disabling v-sync..."); + if (SDL_GL_SetSwapInterval(0) != 0) + { + debug::log::error("Failed to disable v-sync: {}", SDL_GetError()); + v_sync = true; + } + else + { + debug::log::debug("Disabled v-sync"); + } + } + + this->v_sync = v_sync; +} + +void sdl_window::make_current() +{ + SDL_GL_MakeCurrent(internal_window, internal_context); +} + +void sdl_window::swap_buffers() +{ + SDL_GL_SwapWindow(internal_window); +} + +} // namespace app diff --git a/src/engine/app/sdl/sdl-window.hpp b/src/engine/app/sdl/sdl-window.hpp new file mode 100644 index 0000000..4ff342a --- /dev/null +++ b/src/engine/app/sdl/sdl-window.hpp @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_APP_SDL_WINDOW_HPP +#define ANTKEEPER_APP_SDL_WINDOW_HPP + +#include +#include + +namespace app { + +/** + * + */ +class sdl_window: public window +{ +public: + virtual ~sdl_window(); + virtual void set_title(const std::string& title); + virtual void set_position(const math::vector& position); + virtual void set_size(const math::vector& size); + virtual void set_minimum_size(const math::vector& size); + virtual void set_maximum_size(const math::vector& size); + virtual void set_maximized(bool maximized); + virtual void set_fullscreen(bool fullscreen); + virtual void set_v_sync(bool v_sync); + virtual void make_current(); + virtual void swap_buffers(); + + [[nodiscard]] inline virtual gl::rasterizer* get_rasterizer() noexcept + { + return rasterizer; + } + +private: + friend class sdl_window_manager; + + sdl_window + ( + const std::string& title, + const math::vector& windowed_position, + const math::vector& windowed_size, + bool maximized, + bool fullscreen, + bool v_sync + ); + + sdl_window(const sdl_window&) = delete; + sdl_window(sdl_window&&) = delete; + sdl_window& operator=(const sdl_window&) = delete; + sdl_window& operator=(sdl_window&&) = delete; + + SDL_Window* internal_window; + SDL_GLContext internal_context; + gl::rasterizer* rasterizer; +}; + +} // namespace app + +#endif // ANTKEEPER_APP_SDL_WINDOW_HPP diff --git a/src/engine/app/window-events.hpp b/src/engine/app/window-events.hpp new file mode 100644 index 0000000..be5af71 --- /dev/null +++ b/src/engine/app/window-events.hpp @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_APP_WINDOW_EVENTS_HPP +#define ANTKEEPER_APP_WINDOW_EVENTS_HPP + +#include + +namespace app { + +class window; + +/** + * Event generated when a window has been requested to close. + */ +struct window_closed_event +{ + /// Pointer to the window that has been requested to close. + window* window; +}; + +/** + * Event generated when a window has gained or lost focus. + */ +struct window_focus_changed_event +{ + /// Pointer to the window that has gained or lost focus. + window* window; + + /// `true` if the window is in focus, `false` otherwise. + bool in_focus; +}; + +/** + * Event generated when a window has been moved. + */ +struct window_moved_event +{ + /// Pointer to the window that has been moved. + window* window; + + /// Position of the window, in display units. + math::vector position; +}; + +/** + * Event generated when a window has been maximized. + */ +struct window_maximized_event +{ + /// Pointer to the window that has been maximized. + window* window; +}; + +/** + * Event generated when a window has been minimized. + */ +struct window_minimized_event +{ + /// Pointer to the window that has been minimized. + window* window; +}; + +/** + * Event generated when a window has been restored. + */ +struct window_restored_event +{ + /// Pointer to the window that has been restored. + window* window; +}; + +/** + * Event generated when a window has been resized. + */ +struct window_resized_event +{ + /// Pointer to the window that has been resized. + window* window; + + /// Window size, in display units. + math::vector size; +}; + +} // namespace app + +#endif // ANTKEEPER_APP_WINDOW_EVENTS_HPP diff --git a/src/engine/app/window-manager.cpp b/src/engine/app/window-manager.cpp new file mode 100644 index 0000000..694dc36 --- /dev/null +++ b/src/engine/app/window-manager.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include + +namespace app { + +window_manager* window_manager::instance() +{ + return new sdl_window_manager(); +} + +} // namespace app diff --git a/src/engine/app/window-manager.hpp b/src/engine/app/window-manager.hpp new file mode 100644 index 0000000..c6bbb46 --- /dev/null +++ b/src/engine/app/window-manager.hpp @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_APP_WINDOW_MANAGER_HPP +#define ANTKEEPER_APP_WINDOW_MANAGER_HPP + +#include +#include +#include +#include + +namespace app { + +/** + * + */ +class window_manager +{ +public: + /** + * Allocates and returns a window manager. + */ + static window_manager* instance(); + + /// Destructs a window manager. + virtual ~window_manager() = default; + + /** + * Updates all managed windows. This should be called once per frame. + */ + virtual void update() = 0; + + /** + * Constructs a window. + * + * @param title Title of the window. + * @param windowed_position Windowed (non-maximized, non-fullscreen) position of the window, in display units. + * @param windowed_size Windowed (non-maximized, non-fullscreen) size of the window, in display units. + * @param maximized `true` if the window should start maximized, `false` otherwise. + * @param fullscreen `true` if the window should start fullscreen, `false` otherwise. + * @param v_sync `true` if v-sync should be enabled, `false` otherwise. + */ + [[nodiscard]] virtual window* create_window + ( + const std::string& title, + const math::vector& windowed_position, + const math::vector& windowed_size, + bool maximized, + bool fullscreen, + bool v_sync + ) = 0; + + /// Returns the number of available displays. + [[nodiscard]] virtual std::size_t get_display_count() const = 0; + + /** + * Returns the display with the given index. + * + * @param index Index of a display. + * + * @return Display with the given index. + */ + [[nodiscard]] virtual const display& get_display(std::size_t index) const = 0; +}; + +} // namespace app + +#endif // ANTKEEPER_APP_WINDOW_MANAGER_HPP diff --git a/src/engine/app/window.cpp b/src/engine/app/window.cpp new file mode 100644 index 0000000..43d3c7f --- /dev/null +++ b/src/engine/app/window.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include + +namespace app { + + + +} // namespace app diff --git a/src/engine/app/window.hpp b/src/engine/app/window.hpp new file mode 100644 index 0000000..da5c49f --- /dev/null +++ b/src/engine/app/window.hpp @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_APP_WINDOW_HPP +#define ANTKEEPER_APP_WINDOW_HPP + +#include +#include +#include +#include +#include + +namespace app { + +class window_manager; + +/** + * + */ +class window +{ +public: + /** + * Constructs a window. + */ + window() = default; + + /** + * Destructs a window. + */ + virtual ~window() = default; + + /** + * Changes the title of the window. + * + * @param title Window title. + */ + virtual void set_title(const std::string& title) = 0; + + /** + * Changes the position of the window. + * + * @param position Position of the window, in display units. + */ + virtual void set_position(const math::vector& position) = 0; + + /** + * Changes the size of the window. + * + * @param size Size of the window, in display units. + */ + virtual void set_size(const math::vector& size) = 0; + + /** + * Sets the minimum size of the window. + * + * @param size Minimum size of the window, in display units. + */ + virtual void set_minimum_size(const math::vector& size) = 0; + + /** + * Sets the maximum size of the window. + * + * @param size Maximum size of the window, in display units. + */ + virtual void set_maximum_size(const math::vector& size) = 0; + + /** + * Maximizes or unmaximizes the window. + * + * @param maximized `true` if the window should be maximized, `false` otherwise. + */ + virtual void set_maximized(bool maximized) = 0; + + /** + * Enables or disables fullscreen mode. + * + * @param fullscreen `true` if the window should be in fullscreen mode, `false` otherwise. + */ + virtual void set_fullscreen(bool fullscreen) = 0; + + /** + * Enables or disables v-sync. + * + * @param v_sync `true` if the v-sync should be enabled, `false` otherwise. + */ + virtual void set_v_sync(bool v_sync) = 0; + + /** + * Makes the window's graphics context current. + */ + virtual void make_current() = 0; + + /** + * Swaps the front and back buffers of the window's graphics context. + */ + virtual void swap_buffers() = 0; + + /// Returns the title of the window. + [[nodiscard]] inline const std::string& get_title() const noexcept + { + return title; + } + + /// Returns the windowed (non-maximized, non-fullscreen) position of the window, in display units. + [[nodiscard]] inline const math::vector& get_windowed_position() const noexcept + { + return windowed_position; + } + + /// Returns the current position of the window, in display units. + [[nodiscard]] inline const math::vector& get_position() const noexcept + { + return position; + } + + /// Returns the windowed (non-maximized, non-fullscreen) size of the window, in display units. + [[nodiscard]] inline const math::vector& get_windowed_size() const noexcept + { + return windowed_size; + } + + /// Returns the current size of the window, in display units. + [[nodiscard]] inline const math::vector& get_size() const noexcept + { + return size; + } + + /// Returns the minimum size of the window, in display units. + [[nodiscard]] inline const math::vector& get_minimum_size() const noexcept + { + return minimum_size; + } + + /// Returns the maximum size of the window, in display units. + [[nodiscard]] inline const math::vector& get_maximum_size() const noexcept + { + return minimum_size; + } + + /// Returns the current size of the window's drawable viewport, in pixels. + [[nodiscard]] inline const math::vector& get_viewport_size() const noexcept + { + return viewport_size; + } + + /// Returns `true` if the window is maximized, `false` otherwise. + [[nodiscard]] inline bool is_maximized() const noexcept + { + return maximized; + } + + /// Returns `true` if the window is in fullscreen mode, `false` otherwise. + [[nodiscard]] inline bool is_fullscreen() const noexcept + { + return fullscreen; + } + + /// Returns `true` if the v-sync is enabled, `false` otherwise. + [[nodiscard]] inline bool get_v_sync() const noexcept + { + return v_sync; + } + + /// Returns the rasterizer associated with this window. + [[nodiscard]] virtual gl::rasterizer* get_rasterizer() noexcept = 0; + + /// Returns the channel through which window closed events are published. + [[nodiscard]] inline event::channel& get_closed_channel() noexcept + { + return closed_publisher.channel(); + } + + /// Returns the channel through which window focus changed events are published. + [[nodiscard]] inline event::channel& get_focus_changed_channel() noexcept + { + return focus_changed_publisher.channel(); + } + + /// Returns the channel through which window maximized events are published. + [[nodiscard]] inline event::channel& get_maximized_channel() noexcept + { + return maximized_publisher.channel(); + } + + /// Returns the channel through which window minimized events are published. + [[nodiscard]] inline event::channel& get_minimized_channel() noexcept + { + return minimized_publisher.channel(); + } + + /// Returns the channel through which window moved events are published. + [[nodiscard]] inline event::channel& get_moved_channel() noexcept + { + return moved_publisher.channel(); + } + + /// Returns the channel through which window resized events are published. + [[nodiscard]] inline event::channel& get_resized_channel() noexcept + { + return resized_publisher.channel(); + } + + /// Returns the channel through which window restored events are published. + [[nodiscard]] inline event::channel& get_restored_channel() noexcept + { + return restored_publisher.channel(); + } + +protected: + friend class window_manager; + + std::string title; + math::vector windowed_position; + math::vector position; + math::vector windowed_size; + math::vector size; + math::vector minimum_size; + math::vector maximum_size; + math::vector viewport_size; + bool maximized; + bool fullscreen; + bool v_sync; + + event::publisher closed_publisher; + event::publisher focus_changed_publisher; + event::publisher maximized_publisher; + event::publisher minimized_publisher; + event::publisher moved_publisher; + event::publisher resized_publisher; + event::publisher restored_publisher; +}; + +} // namespace app + +#endif // ANTKEEPER_APP_WINDOW_HPP diff --git a/src/astro/apparent-size.hpp b/src/engine/astro/apparent-size.hpp similarity index 100% rename from src/astro/apparent-size.hpp rename to src/engine/astro/apparent-size.hpp diff --git a/src/engine/color/aces.hpp b/src/engine/color/aces.hpp new file mode 100644 index 0000000..56eed3c --- /dev/null +++ b/src/engine/color/aces.hpp @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_COLOR_ACES_HPP +#define ANTKEEPER_COLOR_ACES_HPP + +#include +#include +#include + +namespace color { + +/// ACES color spaces. +namespace aces { + +/// CIE xy chromaticity coordinates of the ACES white point (~D60). +template +constexpr math::vector2 white_point = {T{0.32168}, T{0.33767}}; + +/// ACES AP0 color space. +template +constexpr rgb::color_space ap0 +( + {T{0.7347}, T{ 0.2653}}, + {T{0.0000}, T{ 1.0000}}, + {T{0.0001}, T{-0.0770}}, + aces::white_point, + nullptr, + nullptr +); + +/// ACES AP1 color space. +template +constexpr rgb::color_space ap1 +( + {T{0.713}, T{0.293}}, + {T{0.165}, T{0.830}}, + {T{0.128}, T{0.044}}, + aces::white_point, + nullptr, + nullptr +); + +/** + * Constructs a saturation adjustment matrix. + * + * @param s Saturation adjustment factor. + * @param to_y Color space to CIE XYZ luminance vector. + * + * @return Saturation adjustment matrix. + */ +template +constexpr math::matrix adjust_saturation(T s, const math::vector3& to_y) +{ + const math::vector3 v = to_y * (T{1} - s); + return math::matrix + { + v[0] + s, v[0], v[0], + v[1], v[1] + s, v[1], + v[2], v[2], v[2] + s + }; +} + +/// ACES AP1 RRT saturation adjustment matrix. +template +constexpr math::matrix ap1_rrt_sat = aces::adjust_saturation(T{0.96}, ap1.to_y); + +/// ACES AP1 ODT saturation adjustment matrix. +template +constexpr math::matrix ap1_odt_sat = aces::adjust_saturation(T{0.93}, ap1.to_y); + +} // namespace aces + +} // namespace color + +#endif // ANTKEEPER_COLOR_ACES_HPP diff --git a/src/engine/color/cat.hpp b/src/engine/color/cat.hpp new file mode 100644 index 0000000..989ac90 --- /dev/null +++ b/src/engine/color/cat.hpp @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_COLOR_CAT_HPP +#define ANTKEEPER_COLOR_CAT_HPP + +#include +#include + +namespace color { + +/// Chromatic adaption transforms (CAT). +namespace cat { + +/** + * Bradford cone response matrix. + * + * @see Specification ICC.1:2010 (Profile version 4.3.0.0). Image technology colour management — Architecture, profile format, and data structure, Annex E.3, pp. 102. + * @see http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html + */ +template +constexpr math::matrix bradford = +{ + 0.8951, -0.7502, 0.0389, + 0.2664, 1.7135, -0.0685, + -0.1614, 0.0367, 1.0296 +}; + +/** + * von Kries cone response matrix. + * + * @see http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html + */ +template +constexpr math::matrix von_kries = +{ + 0.40024, -0.22630, 0.00000, + 0.70760, 1.16532, 0.00000, + -0.08081, 0.04570, 0.91822 +}; + +/** + * XYZ scaling cone response matrix. + * + * @see http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html + */ +template +constexpr math::matrix xyz_scaling = +{ + T{1}, T{0}, T{0}, + T{0}, T{1}, T{0}, + T{0}, T{0}, T{1} +}; + +/** + * Constructs a chromatic adaptation transform (CAT) matrix. + * + * @param w0 CIE xy chromaticity coordinates of the source illuminant. + * @param w1 CIE xy chromaticity coordinates of the destination illuminant. + * @param cone_response Cone response matrix. + * + * @return CAT matrix. + * + * @see Specification ICC.1:2010 (Profile version 4.3.0.0). Image technology colour management — Architecture, profile format, and data structure, Annex E.3, pp. 102. + * @see http://www.brucelindbloom.com/index.html?Eqn_ChromAdapt.html + */ +template +constexpr math::matrix matrix(const math::vector2& w0, const math::vector2& w1, const math::matrix& cone_response = bradford) +{ + // Convert CIE xy chromaticity coordinates to CIE XYZ colors + const math::vector3 w0_xyz = {w0[0] / w0[1], T{1}, (T{1} - w0[0] - w0[1]) / w0[1]}; + const math::vector3 w1_xyz = {w1[0] / w1[1], T{1}, (T{1} - w1[0] - w1[1]) / w1[1]}; + + // Calculate cone response of CIE XYZ colors + const math::vector3 w0_cone_response = cone_response * w0_xyz; + const math::vector3 w1_cone_response = cone_response * w1_xyz; + + const math::matrix scale = + { + w1_cone_response[0] / w0_cone_response[0], T{0}, T{0}, + T{0}, w1_cone_response[1] / w0_cone_response[1], T{0}, + T{0}, T{0}, w1_cone_response[2] / w0_cone_response[2], + }; + + return math::inverse(cone_response) * scale * cone_response; +} + +} // namespace cat +} // namespace color + +#endif // ANTKEEPER_COLOR_CAT_HPP diff --git a/src/engine/color/cct.hpp b/src/engine/color/cct.hpp new file mode 100644 index 0000000..ea734e7 --- /dev/null +++ b/src/engine/color/cct.hpp @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_COLOR_CCT_HPP +#define ANTKEEPER_COLOR_CCT_HPP + +#include +#include +#include + +namespace color { + +/// Correlated color temperature (CCT). +namespace cct { + +/** + * Calculates CIE 1960 UCS colorspace chromaticity coordinates given a correlated color temperature using Krystek's algorithm. + * + * @param t Correlated color temperature, in Kelvin. + * @return CIE 1960 UCS colorspace chromaticity coordinates. + * + * @see Krystek, M. (1985), An algorithm to calculate correlated colour temperature. Color Res. Appl., 10: 38-40. + */ +template +math::vector2 to_ucs(T t) +{ + const T tt = t * t; + return math::vector2 + { + (T{0.860117757} + T{1.54118254e-4} * t + T{1.28641212e-7} * tt) / (T{1} + T{8.42420235e-4} * t + T{7.08145163e-7} * tt), + (T{0.317398726} + T{4.22806245e-5} * t + T{4.20481691e-8} * tt) / (T{1} - T{2.89741816e-5} * t + T{1.61456053e-7} * tt) + }; +} + +/** + * Calculates CIE xyY colorspace chromaticity coordinates given a correlated color temperature using Krystek's algorithm. + * + * @param t Correlated color temperature, in Kelvin. + * @return CIE xyY color with `Y = 1`. + */ +template +math::vector3 to_xyy(T t) +{ + return ucs::to_xyy(to_ucs(t), T{1}); +} + +/** + * Calculates CIE XYZ colorspace chromaticity coordinates given a correlated color temperature using Krystek's algorithm. + * + * @param t Correlated color temperature, in Kelvin. + * @return CIE XYZ color with `Y = 1`. + */ +template +math::vector3 to_xyz(T t) +{ + return xyy::to_xyz(to_xyy(t)); +} + +} // namespace cct +} // namespace color + +#endif // ANTKEEPER_COLOR_CCT_HPP diff --git a/src/engine/color/color.hpp b/src/engine/color/color.hpp new file mode 100644 index 0000000..8bb3283 --- /dev/null +++ b/src/engine/color/color.hpp @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_COLOR_HPP +#define ANTKEEPER_COLOR_HPP + +/// Color manipulation. +namespace color {} + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif // ANTKEEPER_COLOR_HPP diff --git a/src/engine/color/illuminant.hpp b/src/engine/color/illuminant.hpp new file mode 100644 index 0000000..b0c1558 --- /dev/null +++ b/src/engine/color/illuminant.hpp @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_COLOR_ILLUMINANT_HPP +#define ANTKEEPER_COLOR_ILLUMINANT_HPP + +#include + +namespace color { + +/** + * CIE standard illuminants. + * + * @see https://en.wikipedia.org/wiki/Standard_illuminant + */ +namespace illuminant { + +/// CIE 1931 2 Degree Standard Observer illuminants. +namespace deg2 { + + template + constexpr math::vector2 a = {T{0.44757}, T{0.40745}}; + template + constexpr math::vector2 b = {T{0.34842}, T{0.35161}}; + template + constexpr math::vector2 c = {T{0.31006}, T{0.31616}}; + template + constexpr math::vector2 d50 = {T{0.34567}, T{0.35850}}; + template + constexpr math::vector2 d55 = {T{0.33242}, T{0.34743}}; + template + constexpr math::vector2 d65 = {T{0.31271}, T{0.32902}}; + template + constexpr math::vector2 d75 = {T{0.29902}, T{0.31485}}; + template + constexpr math::vector2 d93 = {T{0.28315}, T{0.29711}}; + template + constexpr math::vector2 e = {T{0.33333}, T{0.33333}}; + template + constexpr math::vector2 f1 = {T{0.31310}, T{0.33727}}; + template + constexpr math::vector2 f2 = {T{0.37208}, T{0.37529}}; + template + constexpr math::vector2 f3 = {T{0.40910}, T{0.39430}}; + template + constexpr math::vector2 f4 = {T{0.44018}, T{0.40329}}; + template + constexpr math::vector2 f5 = {T{0.31379}, T{0.34531}}; + template + constexpr math::vector2 f6 = {T{0.37790}, T{0.38835}}; + template + constexpr math::vector2 f7 = {T{0.31292}, T{0.32933}}; + template + constexpr math::vector2 f8 = {T{0.34588}, T{0.35875}}; + template + constexpr math::vector2 f9 = {T{0.37417}, T{0.37281}}; + template + constexpr math::vector2 f10 = {T{0.34609}, T{0.35986}}; + template + constexpr math::vector2 f11 = {T{0.38052}, T{0.37713}}; + template + constexpr math::vector2 f12 = {T{0.43695}, T{0.40441}}; + template + constexpr math::vector2 led_b1 = {T{0.4560}, T{0.4078}}; + template + constexpr math::vector2 led_b2 = {T{0.4357}, T{0.4012}}; + template + constexpr math::vector2 led_b3 = {T{0.3756}, T{0.3723}}; + template + constexpr math::vector2 led_b4 = {T{0.3422}, T{0.3502}}; + template + constexpr math::vector2 led_b5 = {T{0.3118}, T{0.3236}}; + template + constexpr math::vector2 led_bh1 = {T{0.4474}, T{0.4066}}; + template + constexpr math::vector2 led_rgb1 = {T{0.4557}, T{0.4211}}; + template + constexpr math::vector2 led_v1 = {T{0.4560}, T{0.4548}}; + template + constexpr math::vector2 led_v2 = {T{0.3781}, T{0.3775}}; + +} // deg2 + +/// CIE 1964 10 Degree Standard Observer illuminants. +namespace deg10 { + + template + constexpr math::vector2 a = {T{0.45117}, T{0.40594}}; + template + constexpr math::vector2 b = {T{0.34980}, T{0.35270}}; + template + constexpr math::vector2 c = {T{0.31039}, T{0.31905}}; + template + constexpr math::vector2 d50 = {T{0.34773}, T{0.35952}}; + template + constexpr math::vector2 d55 = {T{0.33411}, T{0.34877}}; + template + constexpr math::vector2 d65 = {T{0.31382}, T{0.33100}}; + template + constexpr math::vector2 d75 = {T{0.29968}, T{0.31740}}; + template + constexpr math::vector2 d93 = {T{0.28327}, T{0.30043}}; + template + constexpr math::vector2 e = {T{0.33333}, T{0.33333}}; + template + constexpr math::vector2 f1 = {T{0.31811}, T{0.33559}}; + template + constexpr math::vector2 f2 = {T{0.37925}, T{0.36733}}; + template + constexpr math::vector2 f3 = {T{0.41761}, T{0.38324}}; + template + constexpr math::vector2 f4 = {T{0.44920}, T{0.39074}}; + template + constexpr math::vector2 f5 = {T{0.31975}, T{0.34246}}; + template + constexpr math::vector2 f6 = {T{0.38660}, T{0.37847}}; + template + constexpr math::vector2 f7 = {T{0.31569}, T{0.32960}}; + template + constexpr math::vector2 f8 = {T{0.34902}, T{0.35939}}; + template + constexpr math::vector2 f9 = {T{0.37829}, T{0.37045}}; + template + constexpr math::vector2 f10 = {T{0.35090}, T{0.35444}}; + template + constexpr math::vector2 f11 = {T{0.38541}, T{0.37123}}; + template + constexpr math::vector2 f12 = {T{0.44256}, T{0.39717}}; + +} // namespace deg10 + +} // namespace illuminant + +} // namespace color + +#endif // ANTKEEPER_COLOR_ILLUMINANT_HPP diff --git a/src/color/index.hpp b/src/engine/color/index.hpp similarity index 100% rename from src/color/index.hpp rename to src/engine/color/index.hpp diff --git a/src/engine/color/rgb.hpp b/src/engine/color/rgb.hpp new file mode 100644 index 0000000..0874cf4 --- /dev/null +++ b/src/engine/color/rgb.hpp @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_COLOR_RGB_HPP +#define ANTKEEPER_COLOR_RGB_HPP + +#include +#include +#include + +namespace color { + +/// RGB color spaces. +namespace rgb { + +/** + * Constructs a matrix which transforms an RGB color into a CIE XYZ color. + * + * @param r CIE xy chromaticity coordinates of the red primary. + * @param g CIE xy chromaticity coordinates of the green primary. + * @param b CIE xy chromaticity coordinates of the blue primary. + * @param w CIE xy chromaticity coordinates of the white point. + * + * @return Matrix which transforms an RGB color into a CIE XYZ color. + * + * @see https://www.ryanjuckett.com/rgb-color-space-conversion/ + * @see https://mina86.com/2019/srgb-xyz-matrix/ + */ +template +constexpr math::matrix to_xyz(const math::vector2& r, const math::vector2& g, const math::vector2& b, const math::vector2& w) +{ + const math::matrix m = + { + r[0], r[1], T{1} - (r[0] + r[1]), + g[0], g[1], T{1} - (g[0] + g[1]), + b[0], b[1], T{1} - (b[0] + b[1]) + }; + + const math::vector3 scale = math::inverse(m) * math::vector3{w[0] / w[1], T{1}, (T{1} - (w[0] + w[1])) / w[1]}; + + return math::matrix + { + m[0][0] * scale[0], m[0][1] * scale[0], m[0][2] * scale[0], + m[1][0] * scale[1], m[1][1] * scale[1], m[1][2] * scale[1], + m[2][0] * scale[2], m[2][1] * scale[2], m[2][2] * scale[2], + }; +} + +/** + * RGB color space. + */ +template +struct color_space +{ + /// Transfer function function pointer type. + typedef math::vector3 (*transfer_function_type)(const math::vector3&); + + /// CIE xy chromaticity coordinates of the red primary. + const math::vector2 r; + + /// CIE xy chromaticity coordinates of the green primary. + const math::vector2 g; + + /// CIE xy chromaticity coordinates of the blue primary. + const math::vector2 b; + + /// CIE xy chromaticity coordinates of the white point. + const math::vector2 w; + + /// Function pointer to the electro-optical transfer function. + const transfer_function_type eotf; + + /// Function pointer to the inverse electro-optical transfer function. + const transfer_function_type inverse_eotf; + + /// Matrix which transforms an RGB color to a CIE XYZ color. + const math::matrix3x3 to_xyz; + + /// Matrix which transforms a CIE XYZ color to an RGB color. + const math::matrix3x3 from_xyz; + + /// Vector which gives the luminance of an RGB color via dot product. + const math::vector3 to_y; + + /** + * Constructs an RGB color space. + * + * @param r CIE xy chromaticity coordinates of the red primary. + * @param g CIE xy chromaticity coordinates of the green primary. + * @param b CIE xy chromaticity coordinates of the blue primary. + * @param w CIE xy chromaticity coordinates of the white point. + */ + constexpr color_space(const math::vector2& r, const math::vector2& g, const math::vector2& b, const math::vector2& w, transfer_function_type eotf, transfer_function_type inverse_eotf); + + /** + * Measures the luminance of a linear RGB color. + * + * @param x Linear RGB color. + * @return return Luminance of @p x. + */ + constexpr T luminance(const math::vector3& x) const; +}; + +template +constexpr color_space::color_space(const math::vector2& r, const math::vector2& g, const math::vector2& b, const math::vector2& w, transfer_function_type eotf, transfer_function_type inverse_eotf): + r(r), + g(g), + b(b), + w(w), + eotf(eotf), + inverse_eotf(inverse_eotf), + to_xyz(color::rgb::to_xyz(r, g, b, w)), + from_xyz(math::inverse(to_xyz)), + to_y{to_xyz[0][1], to_xyz[1][1], to_xyz[2][1]} +{} + +template +constexpr T color_space::luminance(const math::vector3& x) const +{ + return math::dot(x, to_y); +} + +/** + * Constructs a matrix which transforms a color from one RGB color space to another RGB color space. + * + * @param s0 Source color space. + * @param s1 Destination color space. + * @param cone_response Chromatic adaptation transform cone response matrix. + * + * @return Color space transformation matrix. + */ +template +constexpr math::matrix3x3 to_rgb(const color_space& s0, const color_space& s1, const math::matrix3x3& cone_response = color::cat::bradford) +{ + return s1.from_xyz * color::cat::matrix(s0.w, s1.w, cone_response) * s0.to_xyz; +} + +} // namespace rgb +} // namespace color + +#endif // ANTKEEPER_COLOR_RGB_HPP diff --git a/src/engine/color/srgb.hpp b/src/engine/color/srgb.hpp new file mode 100644 index 0000000..806bff7 --- /dev/null +++ b/src/engine/color/srgb.hpp @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_COLOR_SRGB_HPP +#define ANTKEEPER_COLOR_SRGB_HPP + +#include +#include +#include +#include + +namespace color { + +/** + * sRGB electro-optical transfer function (EOTF), also known as the sRGB decoding function. + * + * @param v sRGB electrical signal (gamma-encoded sRGB). + * + * @return Corresponding luminance of the signal (linear sRGB). + */ +template +math::vector3 srgb_eotf(const math::vector3& v) +{ + auto f = [](T x) -> T + { + return x < T{0.04045} ? x / T{12.92} : std::pow((x + T{0.055}) / T{1.055}, T{2.4}); + }; + + return math::vector3 + { + f(v[0]), + f(v[1]), + f(v[2]) + }; +} + +/** + * sRGB inverse electro-optical transfer function (EOTF), also known as the sRGB encoding function. + * + * @param l sRGB luminance (linear sRGB). + * + * @return Corresponding electrical signal (gamma-encoded sRGB). + */ +template +math::vector3 srgb_inverse_eotf(const math::vector3& l) +{ + auto f = [](T x) -> T + { + return x <= T{0.0031308} ? x * T{12.92} : std::pow(x, T{1} / T{2.4}) * T{1.055} - T{0.055}; + }; + + return math::vector3 + { + f(l[0]), + f(l[1]), + f(l[2]) + }; +} + +/// sRGB color space. +template +constexpr rgb::color_space srgb +( + {T{0.64}, T{0.33}}, + {T{0.30}, T{0.60}}, + {T{0.15}, T{0.06}}, + color::illuminant::deg2::d65, + &srgb_eotf, + &srgb_inverse_eotf +); + +} // namespace color + +#endif // ANTKEEPER_COLOR_SRGB_HPP diff --git a/src/engine/color/ucs.hpp b/src/engine/color/ucs.hpp new file mode 100644 index 0000000..d6570ee --- /dev/null +++ b/src/engine/color/ucs.hpp @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_COLOR_UCS_HPP +#define ANTKEEPER_COLOR_UCS_HPP + +#include + +namespace color { + +/// CIE 1960 UCS color space. +namespace ucs { + +/** + * Transforms CIE 1960 UCS chromaticity coordinates into the CIE xyY colorspace. + * + * @param uv CIE 1960 UCS chromaticity coordinates. + * @param y Luminance or `Y` value of the resulting xyY color. + * @return CIE xyY color. + */ +template +constexpr math::vector3 to_xyy(const math::vector2& uv, T y = T{1}) +{ + const T d = T{1} / (T{2} * uv[0] - T{8} * uv[1] + T{4}); + return math::vector3{(T{3} * uv[0]) * d, (T{2} * uv[1]) * d, y}; +} + +} // namespace ucs +} // namespace color + +#endif // ANTKEEPER_COLOR_UCS_HPP diff --git a/src/engine/color/xyy.hpp b/src/engine/color/xyy.hpp new file mode 100644 index 0000000..48ec0fe --- /dev/null +++ b/src/engine/color/xyy.hpp @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_COLOR_XYY_HPP +#define ANTKEEPER_COLOR_XYY_HPP + +#include + +namespace color { + +/// CIE xyY color space. +namespace xyy { + +/** + * Returns the luminance of a CIE xyY color. + * + * @param x CIE xyY color. + * @return return Luminance of @p x. + */ +template +inline constexpr T luminance(const math::vector3& x) +{ + return x[2]; +} + +/** + * Transforms a CIE xyY color into the CIE 1960 UCS colorspace. + * + * @param x CIE xyY color. + * @return CIE 1960 UCS color. + */ +template +constexpr math::vector2 to_ucs(const math::vector3& x) +{ + const T d = (T{1} / (T{-2} * x[0] + T{12} * x[1] + T{3})); + return math::vector2{(T{4} * x[0]) * d, (T{6} * x[1]) * d}; +} + +/** + * Transforms a CIE xyY color into the CIE XYZ colorspace. + * + * @param x CIE xyY color. + * @return CIE XYZ color. + */ +template +constexpr math::vector3 to_xyz(const math::vector3& x) +{ + return math::vector3{(x[0] * x[2]) / x[1], x[2], ((T{1} - x[0] - x[1]) * x[2]) / x[1]}; +} + +} // namespace xyy +} // namespace color + +#endif // ANTKEEPER_COLOR_XYY_HPP diff --git a/src/engine/color/xyz.hpp b/src/engine/color/xyz.hpp new file mode 100644 index 0000000..c3a8f23 --- /dev/null +++ b/src/engine/color/xyz.hpp @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_COLOR_XYZ_HPP +#define ANTKEEPER_COLOR_XYZ_HPP + +#include + +namespace color { + +/** + * CIE XYZ color space. + * + * @see https://en.wikipedia.org/wiki/CIE_1931_color_space + */ +namespace xyz { + +/** + * Returns the luminance of a CIE XYZ color. + * + * @param x CIE XYZ color. + * @return return Luminance of @p x. + */ +template +inline constexpr T luminance(const math::vector3& x) +{ + return x[1]; +} + +/** + * Transforms a CIE XYZ color into the CIE xyY color space. + * + * @param x CIE XYZ color. + * @return CIE xyY color. + */ +template +constexpr math::vector3 to_xyy(const math::vector3& x) +{ + const T sum = x[0] + x[1] + x[2]; + return math::vector3{x[0] / sum, x[1] / sum, x[1]}; +} + +/** + * CIE 1931 standard observer color matching function for the X tristimulus value. + * + * @param lambda Wavelength of light, in nanometers. + * @return Matching X tristimulus value. + * + * @see match(T) + */ +template +T match_x(T lambda) +{ + const T t0 = (lambda - T(442.0)) * ((lambda < T(442.0)) ? T(0.0624) : T(0.0374)); + const T t1 = (lambda - T(599.8)) * ((lambda < T(599.8)) ? T(0.0264) : T(0.0323)); + const T t2 = (lambda - T(501.1)) * ((lambda < T(501.1)) ? T(0.0490) : T(0.0382)); + + const T x0 = T( 0.362) * std::exp(T(-0.5) * t0 * t0); + const T x1 = T( 1.056) * std::exp(T(-0.5) * t1 * t1); + const T x2 = T(-0.065) * std::exp(T(-0.5) * t2 * t2); + + return x0 + x1 + x2; +} + +/** + * CIE 1931 standard observer color matching function for the Y tristimulus value. + * + * @param lambda Wavelength of light, in nanometers. + * @return Matching Y tristimulus value. + * + * @see match(T) + */ +template +T match_y(T lambda) +{ + const T t0 = (lambda - T(568.8)) * ((lambda < T(568.8)) ? T(0.0213) : T(0.0247)); + const T t1 = (lambda - T(530.9)) * ((lambda < T(530.9)) ? T(0.0613) : T(0.0322)); + + const T y0 = T(0.821) * std::exp(T(-0.5) * t0 * t0); + const T y1 = T(0.286) * std::exp(T(-0.5) * t1 * t1); + + return y0 + y1; +} + +/** + * CIE 1931 standard observer color matching function for the Z tristimulus value. + * + * @param lambda Wavelength of light, in nanometers. + * @return Matching Z tristimulus value. + * + * @see match(T) + */ +template +T match_z(T lambda) +{ + const T t0 = (lambda - T(437.0)) * ((lambda < T(437.0)) ? T(0.0845) : T(0.0278)); + const T t1 = (lambda - T(459.0)) * ((lambda < T(459.0)) ? T(0.0385) : T(0.0725)); + + const T z0 = T(1.217) * std::exp(T(-0.5) * t0 * t0); + const T z1 = T(0.681) * std::exp(T(-0.5) * t1 * t1); + + return z0 + z1; +} + +/** + * Fitted piecewise gaussian approximation to the CIE 1931 standard observer color matching function. + * + * @param lambda Wavelength of light, in nanometers. + * @return Matching CIE XYZ color. + * + * @see match_x(T) + * @see match_y(T) + * @see match_z(T) + * + * @see Wyman, C., Sloan, P.J., & Shirley, P. (2013). Simple Analytic Approximations to the CIE XYZ Color Matching Functions. + */ +template +math::vector3 match(T lambda) +{ + return math::vector3 + { + match_x(lambda), + match_y(lambda), + match_z(lambda) + }; +} + +} // namespace xyz +} // namespace color + +#endif // ANTKEEPER_COLOR_XYZ_HPP diff --git a/src/engine/config.hpp.in b/src/engine/config.hpp.in new file mode 100644 index 0000000..ebbcc1e --- /dev/null +++ b/src/engine/config.hpp.in @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_CONFIG_HPP +#define ANTKEEPER_CONFIG_HPP + +// Disable trace message logging on release builds +#if defined(NDEBUG) + #define ANTKEEPER_DEBUG_LOG_MIN_MESSAGE_SEVERITY 1 +#else + #define ANTKEEPER_DEBUG_LOG_MIN_MESSAGE_SEVERITY 0 +#endif + +#include + +/// Global configuration constants. +namespace config { + +/// @name Application config +/// @{ + +/// Application name string. +inline constexpr const char* application_name = "@APPLICATION_NAME@"; + +/// Application slug string. +inline constexpr const char* application_slug = "@APPLICATION_SLUG@"; + +/// Application major version number. +inline constexpr int application_version_major = @APPLICATION_VERSION_MAJOR@; + +/// Application minor version number. +inline constexpr int application_version_minor = @APPLICATION_VERSION_MINOR@; + +/// Application patch version number. +inline constexpr int application_version_patch = @APPLICATION_VERSION_PATCH@; + +/// Application version string ("`major.minor.patch`") +inline constexpr const char* application_version_string = "@APPLICATION_VERSION@"; + +/// @} + +/// @name Debug config +/// @{ + +/// Maximum number of debug logs to archive. +inline constexpr std::size_t debug_log_archive_capacity = 5; + +/// @} + +/// @name OpenGL config +/// @{ + +/// OpenGL major version number, used when creating OpenGL contexts. +inline constexpr int opengl_version_major = 3; + +/// OpenGL minor version number, used when creating OpenGL contexts. +inline constexpr int opengl_version_minor = 3; + +/// Minimum number of bits in the red channel of the color attachment of the OpenGL default framebuffer. +inline constexpr int opengl_min_red_size = 8; + +/// Minimum number of bits in the green channel of the color attachment of the OpenGL default framebuffer. +inline constexpr int opengl_min_green_size = 8; + +/// Minimum number of bits in the blue channel of the color attachment of the OpenGL default framebuffer. +inline constexpr int opengl_min_blue_size = 8; + +/// Minimum number of bits in the alpha channel of the color attachment of the OpenGL default framebuffer. +inline constexpr int opengl_min_alpha_size = 0; + +/// Minimum number of bits in the depth attachment, if any, of the OpenGL default framebuffer. +inline constexpr int opengl_min_depth_size = 0; + +/// Minimum number of bits in the stencil attachment, if any, of the OpenGL default framebuffer. +inline constexpr int opengl_min_stencil_size = 0; + +/// @} + +inline constexpr math::vector global_forward = {0.0f, 0.0f, -1.0f}; +inline constexpr math::vector global_up = {0.0f, 1.0f, 0.0f}; +inline constexpr math::vector global_right = {1.0f, 0.0f, 0.0f}; + +/// Duration of the menu fade in animation, in seconds. +inline constexpr float menu_fade_in_duration = 0.25f; + +/// Duration of the menu fade out animation, in seconds. +inline constexpr float menu_fade_out_duration = 0.125f; + +/// Padding of the a menu item mouseover bounds, as a percentage of the font size. +inline constexpr float menu_mouseover_padding = 0.1f; + +/// Opacity of the menu background. +inline constexpr float menu_bg_opacity = 2.0f / 4.0f; + +/// RGBA color of active menu items. +inline constexpr math::vector menu_active_color{1.0f, 1.0f, 1.0f, 1.0f}; + +/// RGBA color of inactive menu items. +inline constexpr math::vector menu_inactive_color{1.0f, 1.0f, 1.0f, 0.5f}; + +/// Duration of the title screen fade in, in seconds. +inline constexpr float title_fade_in_duration = 1.0f; + +/// Duration of the fade out when quitting the game or returning to the main menu, in seconds. +inline constexpr float quit_fade_out_duration = 0.5f; + +/// Duration of the fade out when a new colony is started, in seconds. +inline constexpr float new_colony_fade_out_duration = 1.0f; + +/// Duration of the nuptial flight fade in, in seconds. +inline constexpr float nuptial_flight_fade_in_duration = 5.0f; + +#define MATERIAL_PASS_MAX_AMBIENT_LIGHT_COUNT 1 +#define MATERIAL_PASS_MAX_POINT_LIGHT_COUNT 1 +#define MATERIAL_PASS_MAX_DIRECTIONAL_LIGHT_COUNT 3 +#define MATERIAL_PASS_MAX_SPOTLIGHT_COUNT 1 +#define MATERIAL_PASS_MAX_BONE_COUNT 64 +#define TERRAIN_PATCH_SIZE 200.0f +#define TERRAIN_PATCH_RESOLUTION 4 +#define VEGETATION_PATCH_RESOLUTION 1 + +} // namespace config + +#endif // ANTKEEPER_CONFIG_HPP diff --git a/src/engine/debug/cli.cpp b/src/engine/debug/cli.cpp new file mode 100644 index 0000000..65cf2b7 --- /dev/null +++ b/src/engine/debug/cli.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include + +namespace debug { + +std::string cli::interpret(const std::string& line) const +{ + std::istringstream stream(line); + std::string command_name; + stream >> command_name; + + if (auto it = commands.find(command_name); it != commands.end()) + { + return it->second(line.substr(command_name.length() + 1)); + } + + return std::string(); +} + +void cli::unregister_command(const std::string& name) +{ + if (auto it = commands.find(name); it != commands.end()) + commands.erase(it); +} + +} // namespace debug diff --git a/src/debug/cli.hpp b/src/engine/debug/cli.hpp similarity index 100% rename from src/debug/cli.hpp rename to src/engine/debug/cli.hpp diff --git a/src/engine/debug/console.cpp b/src/engine/debug/console.cpp new file mode 100644 index 0000000..5e8c865 --- /dev/null +++ b/src/engine/debug/console.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include + +#if defined(_WIN32) + #define WIN32_LEAN_AND_MEAN + #include +#endif + +namespace debug { +namespace console { + +void enable_vt100() +{ + #if defined(_WIN32) + DWORD mode = 0; + HANDLE std_output_handle = GetStdHandle(STD_OUTPUT_HANDLE); + GetConsoleMode(std_output_handle, &mode); + SetConsoleMode(std_output_handle, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING); + #endif +} + +void disable_vt100() +{ + #if defined(_WIN32) + DWORD mode = 0; + HANDLE std_output_handle = GetStdHandle(STD_OUTPUT_HANDLE); + GetConsoleMode(std_output_handle, &mode); + SetConsoleMode(std_output_handle, mode & (~ENABLE_VIRTUAL_TERMINAL_PROCESSING)); + #endif +} + +void enable_utf8() +{ + #if defined(_WIN32) + SetConsoleOutputCP(CP_UTF8); + #endif +} + +} // namespace console +} // namespace debug diff --git a/src/debug/console.hpp b/src/engine/debug/console.hpp similarity index 100% rename from src/debug/console.hpp rename to src/engine/debug/console.hpp diff --git a/src/debug/debug.hpp b/src/engine/debug/debug.hpp similarity index 100% rename from src/debug/debug.hpp rename to src/engine/debug/debug.hpp diff --git a/src/engine/debug/log.cpp b/src/engine/debug/log.cpp new file mode 100644 index 0000000..82cc21a --- /dev/null +++ b/src/engine/debug/log.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include + +namespace debug { +namespace log { + +logger& default_logger() noexcept +{ + static logger instance; + return instance; +} + +} // namespace log +} // namespace debug diff --git a/src/engine/debug/log.hpp b/src/engine/debug/log.hpp new file mode 100644 index 0000000..59df307 --- /dev/null +++ b/src/engine/debug/log.hpp @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_DEBUG_LOG_HPP +#define ANTKEEPER_DEBUG_LOG_HPP + +#include +#include +#include +#include +#include +#include + +// Enable logging of messages of all severities by default. +#if !defined(ANTKEEPER_DEBUG_LOG_MIN_MESSAGE_SEVERITY) + #define ANTKEEPER_DEBUG_LOG_MIN_MESSAGE_SEVERITY (ANTKEEPER_DEBUG_LOG_MESSAGE_SEVERITY_TRACE) +#endif + +namespace debug { + +/** + * Debug message logging. + */ +namespace log { + +/** + * Returns the default logger. + */ +[[nodiscard]] logger& default_logger() noexcept; + +/** + * Self-formatting message that logs itself to the default logger on construction. + * + * @tparam Severity Message severity. A message will not log itself if @p Severity is less than the user-defined macro `ANTKEEPER_DEBUG_LOG_MIN_MESSAGE_SEVERITY`. + * @tparam Args Types of arguments to be formatted. + */ +template +struct message +{ + /** + * Formats and logs a message. + * + * Class template argument deduction (CTAD) is utilized to capture source location as a default argument following variadic format arguments. + * + * @param format Message format string. + * @param args Arguments to be formatted. + * @param location Source location from which the message was sent. + */ + message + ( + [[maybe_unused]] std::string_view format, + [[maybe_unused]] Args&&... args, + [[maybe_unused]] std::source_location&& location = std::source_location::current() + ) + { + if constexpr (ANTKEEPER_DEBUG_LOG_MIN_MESSAGE_SEVERITY <= static_cast>(Severity)) + { + default_logger().log(std::vformat(format, std::make_format_args(std::forward(args)...)), Severity, std::forward(location)); + } + } +}; + +// Use class template argument deduction (CTAD) to capture source location as a default argument following variadic format arguments. +template +message(std::string_view, Args&&...) -> message; + +#if (ANTKEEPER_DEBUG_LOG_MIN_MESSAGE_SEVERITY <= ANTKEEPER_DEBUG_LOG_MESSAGE_SEVERITY_TRACE) + /** + * Formats and logs a trace message. + * + * @tparam Args Types of arguments to be formatted. + */ + template + using trace = message; +#else + // Disable trace message logging. + template + inline void trace([[maybe_unused]] Args&&...) noexcept {}; +#endif + +#if (ANTKEEPER_DEBUG_LOG_MIN_MESSAGE_SEVERITY <= ANTKEEPER_DEBUG_LOG_MESSAGE_SEVERITY_DEBUG) + /** + * Formats and logs a debug message. + * + * @tparam Args Types of arguments to be formatted. + */ + template + using debug = message; +#else + // Disable debug message logging. + template + inline void debug([[maybe_unused]] Args&&...) noexcept {}; +#endif + +#if (ANTKEEPER_DEBUG_LOG_MIN_MESSAGE_SEVERITY <= ANTKEEPER_DEBUG_LOG_MESSAGE_SEVERITY_INFO) + /** + * Formats and logs an info message. + * + * @tparam Args Types of arguments to be formatted. + */ + template + using info = message; +#else + // Disable info message logging. + template + inline void info([[maybe_unused]] Args&&...) noexcept {}; +#endif + +#if (ANTKEEPER_DEBUG_LOG_MIN_MESSAGE_SEVERITY <= ANTKEEPER_DEBUG_LOG_MESSAGE_SEVERITY_WARNING) + /** + * Formats and logs a warning message. + * + * @tparam Args Types of arguments to be formatted. + */ + template + using warning = message; +#else + // Disable warning message logging. + template + inline void warning([[maybe_unused]] Args&&...) noexcept {}; +#endif + +#if (ANTKEEPER_DEBUG_LOG_MIN_MESSAGE_SEVERITY <= ANTKEEPER_DEBUG_LOG_MESSAGE_SEVERITY_ERROR) + /** + * Formats and logs an error message. + * + * @tparam Args Types of arguments to be formatted. + */ + template + using error = message; +#else + // Disable error message logging. + template + inline void error([[maybe_unused]] Args&&...) noexcept {}; +#endif + +#if (ANTKEEPER_DEBUG_LOG_MIN_MESSAGE_SEVERITY <= ANTKEEPER_DEBUG_LOG_MESSAGE_SEVERITY_FATAL) + /** + * Formats and logs a fatal error message. + * + * @tparam Args Types of arguments to be formatted. + */ + template + using fatal = message; +#else + // Disable fatal error message logging. + template + inline void fatal([[maybe_unused]] Args&&...) noexcept {}; +#endif + +} // namespace log +} // namespace debug + +#endif // ANTKEEPER_DEBUG_LOG_HPP diff --git a/src/engine/debug/log/event.hpp b/src/engine/debug/log/event.hpp new file mode 100644 index 0000000..87abcff --- /dev/null +++ b/src/engine/debug/log/event.hpp @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_DEBUG_LOG_EVENT_HPP +#define ANTKEEPER_DEBUG_LOG_EVENT_HPP + +#include +#include +#include +#include +#include + +namespace debug { +namespace log { + +class logger; + +/** + * Debug logging events. + */ +namespace event { + +/** + * Event generated when a message has been logged. + */ +struct message_logged +{ + /// Logger which received the message. + log::logger* logger; + + /// Time at which the message was sent. + std::chrono::time_point time; + + /// ID of the thread from which the message was sent. + std::thread::id thread_id; + + /// Source location from which the message was sent. + std::source_location location; + + /// Severity of the message. + message_severity severity; + + /// Message contents. + std::string message; +}; + +} // namespace event +} // namespace log +} // namespace debug + +#endif // ANTKEEPER_DEBUG_LOG_EVENT_HPP diff --git a/src/engine/debug/log/logger.cpp b/src/engine/debug/log/logger.cpp new file mode 100644 index 0000000..5c02a12 --- /dev/null +++ b/src/engine/debug/log/logger.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include + +namespace debug { +namespace log { + +void logger::log(std::string&& message, message_severity severity, std::source_location&& location) +{ + // Generate message logged event + message_logged_publisher.publish + ( + { + this, + std::chrono::system_clock::now(), + std::this_thread::get_id(), + std::move(location), + severity, + std::move(message) + } + ); +} + +} // namespace log +} // namespace debug diff --git a/src/engine/debug/log/logger.hpp b/src/engine/debug/log/logger.hpp new file mode 100644 index 0000000..b029483 --- /dev/null +++ b/src/engine/debug/log/logger.hpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_DEBUG_LOG_LOGGER_HPP +#define ANTKEEPER_DEBUG_LOG_LOGGER_HPP + +#include +#include +#include +#include +#include + +namespace debug { +namespace log { + +/** + * Generates an event each time a message logged. + */ +class logger +{ +public: + /** + * Logs a message. + * + * @param message Message contents. + * @param severity Message severity. + * @param location Source location from which the message was sent. + */ + void log + ( + std::string&& message, + message_severity severity = message_severity::info, + std::source_location&& location = std::source_location::current() + ); + + /// Returns the channel through which message logged events are published. + [[nodiscard]] inline ::event::channel& get_message_logged_channel() noexcept + { + return message_logged_publisher.channel(); + } + +private: + ::event::publisher message_logged_publisher; +}; + +} // namespace log +} // namespace debug + +#endif // ANTKEEPER_DEBUG_LOG_LOGGER_HPP diff --git a/src/debug/log/message-severity.hpp b/src/engine/debug/log/message-severity.hpp similarity index 100% rename from src/debug/log/message-severity.hpp rename to src/engine/debug/log/message-severity.hpp diff --git a/src/engine/entity/archetype.cpp b/src/engine/entity/archetype.cpp new file mode 100644 index 0000000..eadde4c --- /dev/null +++ b/src/engine/entity/archetype.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include + +namespace entity { + +entity::id archetype::create(entity::registry& registry) const +{ + entt::handle instance_handle(registry, registry.create()); + + for (const auto& function: stamps) + function(instance_handle); + + return instance_handle.entity(); +} + +void archetype::stamp(entt::handle& handle) const +{ + for (const auto& function: stamps) + function(handle); +} + +} // namespace entity diff --git a/src/engine/entity/archetype.hpp b/src/engine/entity/archetype.hpp new file mode 100644 index 0000000..99bb0c3 --- /dev/null +++ b/src/engine/entity/archetype.hpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_ENTITY_ARCHETYPE_HPP +#define ANTKEEPER_ENTITY_ARCHETYPE_HPP + +#include +#include +#include +#include + +namespace entity { + +/** + * Entity type template. + */ +struct archetype +{ + /// List of stamp functions which construct instances of the archetype's components. + std::list> stamps; + + /** + * Creates an instance of this archetype. + * + * @param registry Registry in which to create an entity. + * + * @return Entity ID of the created instance. + */ + entity::id create(entity::registry& registry) const; + + void stamp(entt::handle& handle) const; +}; + +} // namespace entity + +#endif // ANTKEEPER_ENTITY_ARCHETYPE_HPP diff --git a/src/engine/entity/clone.cpp b/src/engine/entity/clone.cpp new file mode 100644 index 0000000..7ee4aef --- /dev/null +++ b/src/engine/entity/clone.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include + +namespace entity { + +void clone(entity::registry& registry, entity::id source, entity::id destination) +{ + for (auto&& it: registry.storage()) + { + if (auto& storage = it.second; storage.contains(source)) + { + storage.emplace(destination, storage.get(source)); + } + } +} + +} // namespace entity diff --git a/src/engine/entity/clone.hpp b/src/engine/entity/clone.hpp new file mode 100644 index 0000000..f125dee --- /dev/null +++ b/src/engine/entity/clone.hpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_ENTITY_CLONE_HPP +#define ANTKEEPER_ENTITY_CLONE_HPP + +#include +#include + +namespace entity { + +/** + * Clones all the components of an entity. + * + * @param registry Entity registry. + * @param source Source entity ID. + * @param destination Destination entity ID. + */ +void clone(entity::registry& registry, entity::id source, entity::id destination); + +} // namespace entity + +#endif // ANTKEEPER_ENTITY_CLONE_HPP diff --git a/src/entity/id.hpp b/src/engine/entity/id.hpp similarity index 100% rename from src/entity/id.hpp rename to src/engine/entity/id.hpp diff --git a/src/entity/registry.hpp b/src/engine/entity/registry.hpp similarity index 100% rename from src/entity/registry.hpp rename to src/engine/entity/registry.hpp diff --git a/src/engine/event/channel.hpp b/src/engine/event/channel.hpp new file mode 100644 index 0000000..35688b3 --- /dev/null +++ b/src/engine/event/channel.hpp @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_EVENT_CHANNEL_HPP +#define ANTKEEPER_EVENT_CHANNEL_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace event { + +template +class publisher; + +/** + * Channel through which messages are published. + * + * @tparam T Message type. + */ +template +class channel +{ +public: + /// Message type. + typedef T message_type; + + /// Subscriber function object type. + typedef subscriber subscriber_type; + + /** + * Subscribes a function object to messages published through this channel. + * + * @param subscriber Subscriber function object which will received published messages. + * + * @return Shared subscription object which will unsubscribe the subscriber on destruction. + */ + [[nodiscard]] std::shared_ptr subscribe(subscriber_type&& subscriber) + { + // Construct shared pointer to subscriber function + std::shared_ptr shared_subscriber = std::make_shared(std::move(subscriber)); + + // Append subscriber to subscriber list and store iterator + auto iterator = subscribers.insert(subscribers.end(), shared_subscriber); + + // Construct and return a shared subscription object which removes the subscriber from the subscriber list when unsubscribed or destructed + return std::make_shared + ( + std::static_pointer_cast(shared_subscriber), + [this, iterator = std::move(iterator)] + { + this->subscribers.erase(iterator); + } + ); + } + + /** + * Subscribes a message queue to messages published through this channel. + * + * @param queue Message queue which will received published messages. + * + * @return Shared subscription object which will unsubscribe the queue on destruction. + */ + [[nodiscard]] std::shared_ptr subscribe(event::queue& queue) + { + return subscribe + ( + [&queue](const message_type& message) + { + queue.enqueue(message); + } + ); + } + +private: + friend class publisher; + + /// List of subscribers. + std::list> subscribers; +}; + +} // namespace event + +#endif // ANTKEEPER_EVENT_CHANNEL_HPP diff --git a/src/engine/event/event.hpp b/src/engine/event/event.hpp new file mode 100644 index 0000000..b907543 --- /dev/null +++ b/src/engine/event/event.hpp @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_EVENT_HPP +#define ANTKEEPER_EVENT_HPP + +/// Publish-subscribe messaging. +namespace event {} + +#include +#include +#include +#include +#include + +#endif // ANTKEEPER_EVENT_HPP diff --git a/src/engine/event/publisher.hpp b/src/engine/event/publisher.hpp new file mode 100644 index 0000000..cce1fc9 --- /dev/null +++ b/src/engine/event/publisher.hpp @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_EVENT_PUBLISHER_HPP +#define ANTKEEPER_EVENT_PUBLISHER_HPP + +#include +#include +#include + +namespace event { + +/** + * Publishes messages to subscribers. + * + * @tparam T Message type. + */ +template +class publisher +{ +public: + /// Message type. + typedef T message_type; + + /// Channel type. + typedef channel channel_type; + + /** + * Publishes a message. + * + * @tparam ExecutionPolicy Execution policy type. + * + * @param policy Execution policy to use. + * @param message Message to publish. + */ + /// @{ + template + void publish(ExecutionPolicy&& policy, const message_type& message) const + { + std::for_each + ( + policy, + std::begin(m_channel.subscribers), + std::end(m_channel.subscribers), + [&](const auto& subscriber) + { + (*subscriber)(message); + } + ); + } + + void publish(const message_type& message) const + { + publish(std::execution::seq, message); + } + /// @} + + /** + * Returns the channel through which messages are published. + */ + /// @{ + [[nodiscard]] inline const channel_type& channel() const noexcept + { + return m_channel; + } + + [[nodiscard]] inline channel_type& channel() noexcept + { + return m_channel; + } + /// @} + +private: + channel_type m_channel; +}; + +} // namespace event + +#endif // ANTKEEPER_EVENT_PUBLISHER_HPP diff --git a/src/engine/event/queue.hpp b/src/engine/event/queue.hpp new file mode 100644 index 0000000..b1cb7cb --- /dev/null +++ b/src/engine/event/queue.hpp @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_EVENT_QUEUE_HPP +#define ANTKEEPER_EVENT_QUEUE_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace event { + +/** + * Collects messages from publishers to be forwarded to subscribers when desired. + */ +class queue +{ +public: + /** + * Subscribes a function object to messages published by this queue. + * + * @tparam T Message type. + * + * @param subscriber Function object to subscribe. + * + * @return Shared subscription object which will unsubscribe the subscriber on destruction. + * + * @TODO This function should be available through an interface class which does not expose the queue's message-sending functions, such as event::channel for publishers. + */ + template + [[nodiscard]] std::shared_ptr subscribe(subscriber&& subscriber) + { + // Allocate shared pointer to std::any object containing subscriber + std::shared_ptr shared_subscriber = std::make_shared(std::make_any>(std::move(subscriber))); + + // Append subscriber to subscriber list and store iterator + auto iterator = subscribers.emplace(std::type_index(typeid(T)), shared_subscriber); + + // Construct and return a shared subscription object which removes the subscriber from the subscriber list when unsubscribed or destructed + return std::make_shared + ( + std::static_pointer_cast(shared_subscriber), + [this, iterator = std::move(iterator)]() + { + this->subscribers.erase(iterator); + } + ); + } + + /** + * Adds a message to the queue, to be distributed later. + * + * @tparam T Message type. + * + * @param message Message to enqueue. + */ + template + void enqueue(const T& message) + { + messages.emplace_back + ( + [this, message]() + { + this->forward(message); + } + ); + } + + /** + * Forwards queued messages, in FIFO order, to subscribers. + */ + void flush() + { + while (!messages.empty()) + { + messages.front()(); + messages.pop_front(); + } + } + + /** + * Removes all messages from the queue. + */ + void clear() + { + messages.clear(); + } + + /** + * Returns `true` if there are no messages in the queue, `false` otherwise. + */ + [[nodiscard]] inline bool empty() const noexcept + { + return messages.empty(); + } + +private: + /** + * Forwards a message to subscribers of the message type. + * + * @tparam T Message type. + * + * @param message Message to forward. + */ + template + void forward(const T& message) const + { + // For each subscriber of the given message type + const auto range = subscribers.equal_range(std::type_index(typeid(T))); + for (auto i = range.first; i != range.second; ++i) + { + // Send message to subscriber + std::any_cast>(*(i->second))(message); + } + } + + std::multimap> subscribers; + std::list> messages; +}; + +} // namespace event + +#endif // ANTKEEPER_EVENT_QUEUE_HPP diff --git a/src/event/subscriber.hpp b/src/engine/event/subscriber.hpp similarity index 100% rename from src/event/subscriber.hpp rename to src/engine/event/subscriber.hpp diff --git a/src/engine/event/subscription.cpp b/src/engine/event/subscription.cpp new file mode 100644 index 0000000..b3d45cb --- /dev/null +++ b/src/engine/event/subscription.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include + +namespace event { + +subscription::subscription(std::weak_ptr&& subscriber, unsubscribe_type&& unsubscriber): + subscriber(std::move(subscriber)), + unsubscriber(std::move(unsubscriber)) +{} + +subscription::~subscription() +{ + unsubscribe(); +} + +bool subscription::expired() const noexcept +{ + return subscriber.expired(); +} + +void subscription::unsubscribe() +{ + if (!expired()) + { + unsubscriber(); + } +} + +} // namespace event diff --git a/src/engine/event/subscription.hpp b/src/engine/event/subscription.hpp new file mode 100644 index 0000000..6947c4f --- /dev/null +++ b/src/engine/event/subscription.hpp @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_EVENT_SUBSCRIPTION_HPP +#define ANTKEEPER_EVENT_SUBSCRIPTION_HPP + +#include +#include + +namespace event { + +/** + * Unsubscribes a subscriber on destruction. + */ +class subscription +{ +public: + /// Unsubscribe function object type. + typedef std::function unsubscribe_type; + + /** + * Constructs a subscription. + * + * @param subscriber Weak pointer to the subscriber. + * @param unsubscriber Unsubscribe function object. + */ + subscription(std::weak_ptr&& subscriber, unsubscribe_type&& unsubscriber); + + /** + * Unsubscribes the subscriber and destructs the subscription. + */ + ~subscription(); + + /** + * Returns `true` if the subscription is no longer active, `false` otherwise. + */ + [[nodiscard]] bool expired() const noexcept; + + /** + * Unsubscribes the subscriber. + */ + void unsubscribe(); + +private: + std::weak_ptr subscriber; + unsubscribe_type unsubscriber; +}; + +/** + * Shared pointer to a subscription. + */ +typedef std::shared_ptr shared_subscription; + +} // namespace event + +#endif // ANTKEEPER_EVENT_SUBSCRIPTION_HPP diff --git a/src/genetics/amino-acid.hpp b/src/engine/genetics/amino-acid.hpp similarity index 100% rename from src/genetics/amino-acid.hpp rename to src/engine/genetics/amino-acid.hpp diff --git a/src/engine/genetics/base.cpp b/src/engine/genetics/base.cpp new file mode 100644 index 0000000..97e50ee --- /dev/null +++ b/src/engine/genetics/base.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include + +namespace genetics { +namespace base { + +/** + * Decodes an IUPAC degenerate base symbol into a bit mask representing the possible bases represented by the symbol. + * + * @param symbol IUPAC degenerate base symbol. + * @return Bit mask representing the possible bases represented by the symbol. + */ +static inline unsigned char decode(char symbol) +{ + static constexpr unsigned char bases[25] = + { + 0b0001, // A + 0b1110, // B + 0b0010, // C + 0b1101, // D + 0, // E + 0, // F + 0b0100, // G + 0b1011, // H + 0, // I + 0, // J + 0b1100, // K + 0, // L + 0b0011, // M + 0b1111, // N + 0, // O + 0, // P + 0, // Q + 0b0101, // R + 0b0110, // S + 0b1000, // T + 0b1000, // U + 0b0111, // V + 0b1001, // W + 0, // X + 0b1010, // Y + }; + + return (symbol < 'A' || symbol >= 'Z') ? 0 : bases[symbol - 'A']; +} + +int compare(char a, char b) +{ + static constexpr int popcount[16] = + { + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 + }; + + return popcount[decode(a) & decode(b)]; +} + +char transcribe(char symbol) +{ + return (symbol == 'T') ? 'U' : (symbol == 'U') ? 'T' : symbol; +} + +namespace dna +{ + char complement(char symbol) + { + constexpr const char* complements = "TVGHZZCDZZMZKNZZZYSAABWZR"; + return (symbol < 'A' || symbol >= 'Z') ? 'Z' : complements[symbol - 'A']; + } +} + +namespace rna +{ + char complement(char symbol) + { + constexpr const char* complements = "UVGHZZCDZZMZKNZZZYSAABWZR"; + return (symbol < 'A' || symbol >= 'Z') ? 'Z' : complements[symbol - 'A']; + } +} + +} // namespace base +} // namespace genetics diff --git a/src/genetics/base.hpp b/src/engine/genetics/base.hpp similarity index 100% rename from src/genetics/base.hpp rename to src/engine/genetics/base.hpp diff --git a/src/engine/genetics/codon.cpp b/src/engine/genetics/codon.cpp new file mode 100644 index 0000000..f0eeea2 --- /dev/null +++ b/src/engine/genetics/codon.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include + +namespace genetics { +namespace codon { + +/** + * Returns the index of a nucleobase for use with a translation table. + * + * @param base IUPAC code of nucleobase, either `U`, `T`, `C`, `A`, or `G`. + * @return Index of the nucleobase, or a negative value if a non-standard nucleobase was supplied. + */ +static inline int base_index(char base) +{ + switch (base) + { + case 'U': + case 'T': + return 0; + case 'C': + return 1; + case 'A': + return 2; + case 'G': + return 3; + } + + return ~3; +} + +/** + * Returns the index of a codon for use with a translation table. + * + * @param base1 IUPAC code of first nucleobase, either `U`, `T`, `C`, `A`, or `G`. + * @param base2 IUPAC code of second nucleobase, either `U`, `T`, `C`, `A`, or `G`. + * @param base3 IUPAC code of third nucleobase, either `U`, `T`, `C`, `A`, or `G`. + * @return Index of codon, or a negative value if a non-standard nucleobase was supplied. + */ +static inline int codon_index(char base1, char base2, char base3) +{ + int i = base_index(base1); + int j = base_index(base2); + int k = base_index(base3); + return (i << 4) | (j << 2) | k; +} + +inline char translate(char base1, char base2, char base3, const char* aas) +{ + int index = codon_index(base1, base2, base3); + if (index < 0) + return '-'; + return aas[index]; +} + +bool is_start(char base1, char base2, char base3, const char* starts) +{ + char aa = translate(base1, base2, base3, starts); + return ((aa != '-') && (aa != '*')); +} + +bool is_stop(char base1, char base2, char base3, const char* aas) +{ + char aa = translate(base1, base2, base3, aas); + return (aa == '*'); +} + +} // namspace codon +} // namespace genetics diff --git a/src/genetics/codon.hpp b/src/engine/genetics/codon.hpp similarity index 100% rename from src/genetics/codon.hpp rename to src/engine/genetics/codon.hpp diff --git a/src/engine/genetics/genetics.hpp b/src/engine/genetics/genetics.hpp new file mode 100644 index 0000000..5a9b173 --- /dev/null +++ b/src/engine/genetics/genetics.hpp @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GENETICS_HPP +#define ANTKEEPER_GENETICS_HPP + +/// Genetic algorithms +namespace genetics {} + +#include +#include +#include +#include +#include +#include +#include + +#endif // ANTKEEPER_GENETICS_HPP diff --git a/src/genetics/matrix.hpp b/src/engine/genetics/matrix.hpp similarity index 100% rename from src/genetics/matrix.hpp rename to src/engine/genetics/matrix.hpp diff --git a/src/engine/genetics/protein.hpp b/src/engine/genetics/protein.hpp new file mode 100644 index 0000000..71f4ed8 --- /dev/null +++ b/src/engine/genetics/protein.hpp @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GENETICS_PROTEIN_HPP +#define ANTKEEPER_GENETICS_PROTEIN_HPP + +#include +#include + +namespace genetics { + +/// Functions which operate on sequences of IUPAC amino acid symbols. +namespace protein { + +/** + * Returns the percent identity between two proteins. + * + * @param first1,last1 Range of IUPAC amino acids which constitute the first protein. + * @param first2 Beginning of the range of IUPAC amino acids which constitute the second protein. + * @return Percent identity between the two proteins. + */ +template +T identity(ForwardIt1 first1, ForwardIt1 last1, ForwardIt2 first2); + +/** + * Scores two proteins using a substitution matrix. + * + * @param first1,last1 Range of IUPAC amino acid codes which constitute the first protein. + * @param first2 Beginning of the range of IUPAC amino acid codes which constitute the second protein. + * @param matrix Substitution matrix. + * @return Score of the two proteins. + */ +template +typename std::remove_all_extents::type score(ForwardIt1 first1, ForwardIt1 last1, ForwardIt2 first2, const Matrix& matrix); + +/** + * Returns the percent similarity between two proteins. + * + * @param first1,last1 Range of IUPAC amino acids which constitute the first protein. + * @param first2 Beginning of the range of IUPAC amino acids which constitute the second protein. + * @return Percent similarity between the two proteins. + */ +template +typename T similarity(ForwardIt1 first1, ForwardIt1 last1, ForwardIt2 first2, const Matrix& matrix); + +template +T identity(ForwardIt1 first1, ForwardIt1 last1, ForwardIt2 first2) +{ + auto length = std::distance(first1, last1); + + T sum = 0; + for (; first1 != last1; ++first1, ++first2) + if (*first1 == *first2) + ++sum; + + return sum / static_cast(length); +} + +template +typename std::remove_all_extents::type score(ForwardIt1 first1, ForwardIt1 last1, ForwardIt2 first2, const Matrix& matrix) +{ + typename std::remove_all_extents::type result = 0; + for (; first1 != last1; ++first1, ++first2) + result += amino_acid::score(*first1, *first2, matrix); + return result; +} + +template +typename T similarity(ForwardIt1 first1, ForwardIt1 last1, ForwardIt2 first2, const Matrix& matrix) +{ + T length = static_cast(std::distance(first1, last1)); + T positive_count = T(0); + + for (; first1 != last1; ++first1, ++first2) + if (amino_acid::score(*first1, *first2, matrix) > 0) + ++positive_count; + + return positive_count / length; +} + +} // namespace protein +} // namespace genetics + +#endif // ANTKEEPER_GENETICS_PROTEIN_HPP diff --git a/src/engine/genetics/sequence.hpp b/src/engine/genetics/sequence.hpp new file mode 100644 index 0000000..b16833b --- /dev/null +++ b/src/engine/genetics/sequence.hpp @@ -0,0 +1,360 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GENETICS_SEQUENCE_HPP +#define ANTKEEPER_GENETICS_SEQUENCE_HPP + +#include +#include +#include +#include +#include + +namespace genetics { + +/// Functions and structures related to sequences of IUPAC degenerate base symbols. +namespace sequence { + +/** + * Open reading frame (ORF), defined by a start codon and stop codon, with the distance between divisible by three. + * + * @tparam Iterator Sequence iterator type. + */ +template +struct orf +{ + /// Iterator to the first base of the start codon. + Iterator start; + + /// Iterator to the first base of the stop codon. + Iterator stop; +}; + +/** + * Exchanges elements between two ranges, starting at a random offset. + * + * @param first1,last1 First range of elements to crossover. + * @param first2 Beginning of the second range of elements to crossover. + * @param g Uniform random bit generator. + * @return Iterator to the start of the crossover in the second range. + */ +template +ForwardIt2 crossover(ForwardIt1 first1, ForwardIt1 last1, ForwardIt2 first2, URBG&& g); + +/** + * Exchanges elements between two ranges multiple times, starting at a random offset each time. + * + * @param first1,last1 First range of elements to crossover. + * @param first2 Beginning of the second range of elements to crossover. + * @param count Number of times to crossover. + * @param g Uniform random bit generator. + */ +template +void crossover_n(ForwardIt1 first1, ForwardIt1 last1, ForwardIt2 first2, Size count, URBG&& g); + +/** + * Searches a sequence for an open reading frame (ORF). + * + * @param first,last Range of elements to search. + * @param table Genetic code translation table. + * @return First ORF in the sequence, or `{last, last}` if no ORF was found. + */ +template +orf find_orf(ForwardIt first, ForwardIt last, const codon::table& table); + +/** + * Applies the given function to a randomly selected element in a range. + * + * @param first,last Range of elements to mutate. + * @param unary_op Unary operation function object that will be applied. + * @param g Uniform random bit generator. + * @return Iterator to the mutated element. + */ +template +ForwardIt mutate(ForwardIt first, ForwardIt last, UnaryOperation unary_op, URBG&& g); + +/** + * Applies the given function to a random selection of elements in a range. + * + * @param first,last Range of elements to mutate. + * @param count Number of elements to mutate. + * @param unary_op Unary operation function object that will be applied. + * @param g Uniform random bit generator. + */ +template +void mutate_n(ForwardIt first, ForwardIt last, Size count, UnaryOperation unary_op, URBG&& g); + +/** + * Searches a sequence of IUPAC base symbols for a pattern matching a search string of IUPAC degenerate base symbols. + * + * @param first,last Sequence of IUPAC base symbols to search. + * @param s_first,s_last Search string of IUPAC degenerate base symbols. + * @param stride Distance between consecutive searches. + * @return Iterator to the beginning of the first subsequence matching `[s_first, s_last)` in the sequence `[first, last)`. If no such occurrence is found, @p last is returned. + */ +template +ForwardIt1 search(ForwardIt1 first, ForwardIt1 last, ForwardIt2 s_first, ForwardIt2 s_last, typename std::iterator_traits::difference_type stride); + +/** + * Transcribes a sequence of IUPAC base symbols between DNA and RNA, swapping `T` for `U` or `U` for `T`. + * + * @param first,last Range of elements to transcribe. + * @param d_first Beginning of the destination range. + * @return Output iterator to the element past the last element transcribed. + */ +template +OutputIt transcribe(InputIt first, InputIt last, OutputIt d_first); + +/** + * Translates a sequence of codons into amino acids. + * + * @param first,last Open reading frame. + * @param d_first Beginning of destination range. + * @param table Genetic code translation table. + * @return Output iterator to the element past the last element translated. + */ +template +OutputIt translate(InputIt first, InputIt last, OutputIt d_first, const codon::table& table); + +/// Functions which operate on sequences of IUPAC degenerate **DNA** base symbols. +namespace dna +{ + /** + * Generates the complementary sequence for a sequence of IUPAC degenerate DNA base symbols. + * + * @param first,last Range of elements to complement. + * @param d_first Beginning of the destination range. + * @return Output iterator to the element past the last element complemented. + */ + template + OutputIt complement(InputIt first, InputIt last, OutputIt d_first); +} + +/// Functions which operate on sequences of IUPAC degenerate **RNA** base symbols. +namespace rna +{ + /** + * Generates the complementary sequence for a sequence of IUPAC degenerate RNA base symbols. + * + * @param first,last Range of elements to complement. + * @param d_first Beginning of the destination range. + * @return Output iterator to the element past the last element complemented. + */ + template + OutputIt complement(InputIt first, InputIt last, OutputIt d_first); +} + +template +ForwardIt2 crossover(ForwardIt1 first1, ForwardIt1 last1, ForwardIt2 first2, URBG&& g) +{ + typedef typename std::iterator_traits::difference_type difference_t; + std::uniform_int_distribution distribution(0, std::distance(first1, last1) - 1); + difference_t pos = distribution(g); + std::advance(first1, pos); + std::advance(first2, pos); + std::swap_ranges(first1, last1, first2); + return first2; +} + +template +void crossover_n(ForwardIt1 first1, ForwardIt1 last1, ForwardIt2 first2, Size count, URBG&& g) +{ + typedef typename std::iterator_traits::difference_type difference_t; + + std::uniform_int_distribution distribution(0, std::distance(first1, last1) - 1); + ForwardIt1 crossover1, crossover2; + + while (count) + { + crossover1 = first1; + crossover2 = first2; + + difference_t pos = distribution(g); + std::advance(crossover1, pos); + std::advance(crossover2, pos); + std::swap_ranges(crossover1, last1, crossover2); + + --count; + } +} + +template +orf find_orf(ForwardIt first, ForwardIt last, const codon::table& table) +{ + ForwardIt second; + ForwardIt third; + orf result; + + auto distance = std::distance(first, last); + + if (distance >= 3) + { + second = first; + ++second; + third = second; + ++third; + + do + { + if (codon::is_start(*first, *second, *third, table.starts)) + { + result.start = first; + distance -= 3; + break; + } + + first = second; + second = third; + ++third; + --distance; + } + while (third != last); + } + + for (; distance >= 3; distance -= 3) + { + first = ++third; + second = ++third; + ++third; + + if (codon::is_stop(*first, *second, *third, table.aas)) + { + result.stop = first; + return result; + } + } + + return {last, last}; +} + +template +ForwardIt mutate(ForwardIt first, ForwardIt last, UnaryOperation unary_op, URBG&& g) +{ + typedef typename std::iterator_traits::difference_type difference_t; + + if (first == last) + return first; + + std::uniform_int_distribution distribution(0, std::distance(first, last) - 1); + std::advance(first, distribution(g)); + *first = unary_op(*first); + + return first; +} + +template +void mutate_n(ForwardIt first, ForwardIt last, Size count, UnaryOperation unary_op, URBG&& g) +{ + typedef typename std::iterator_traits::difference_type difference_t; + + if (first == last) + return first; + + std::uniform_int_distribution distribution(0, std::distance(first, last) - 1); + ForwardIt mutation; + + while (count) + { + mutation = first; + std::advance(mutation, distribution(g)); + *mutation = unary_op(*mutation); + --count; + } +} + +template +ForwardIt1 search(ForwardIt1 first, ForwardIt1 last, ForwardIt2 s_first, ForwardIt2 s_last, typename std::iterator_traits::difference_type stride) +{ + for (auto distance = std::distance(first, last); distance > 0; distance -= stride) + { + ForwardIt1 it = first; + for (ForwardIt2 s_it = s_first; ; ++it, ++s_it) + { + if (s_it == s_last) + return first; + + if (it == last) + return last; + + if (!base::compare(*it, *s_it)) + break; + } + + if (distance > stride) + std::advance(first, stride); + } + + return last; +} + +template +inline OutputIt transcribe(InputIt first, InputIt last, OutputIt d_first) +{ + return std::transform(first, last, d_first, base::transcribe); +} + +template +OutputIt translate(InputIt first, InputIt last, OutputIt d_first, const codon::table& table) +{ + auto length = std::distance(first, last); + + if (length >= 3) + { + InputIt second = first; + ++second; + InputIt third = second; + ++third; + + *(d_first++) = codon::translate(*first, *second, *third, table.starts); + + for (length -= 3; length >= 3; length -= 3) + { + first = ++third; + second = ++third; + ++third; + + *(d_first++) = codon::translate(*first, *second, *third, table.aas); + } + } + + return d_first; +} + +namespace dna +{ + template + inline OutputIt complement(InputIt first, InputIt last, OutputIt d_first) + { + return std::transform(first, last, d_first, base::dna::complement); + } +} + +namespace rna +{ + template + inline OutputIt complement(InputIt first, InputIt last, OutputIt d_first) + { + return std::transform(first, last, d_first, base::rna::complement); + } +} + +} // namespace sequence +} // namespace genetics + +#endif // ANTKEEPER_GENETICS_SEQUENCE_HPP diff --git a/src/engine/genetics/standard-code.hpp b/src/engine/genetics/standard-code.hpp new file mode 100644 index 0000000..4dfc4f8 --- /dev/null +++ b/src/engine/genetics/standard-code.hpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GENETICS_STANDARD_CODE_HPP +#define ANTKEEPER_GENETICS_STANDARD_CODE_HPP + +#include + +namespace genetics { + +/** + * Codon table for standard genetic code. + * + * @see https://www.ncbi.nlm.nih.gov/Taxonomy/Utils/wprintgc.cgi#SG1 + */ +constexpr codon::table standard_code = +{ + "FFLLSSSSYY**CC*WLLLLPPPPHHQQRRRRIIIMTTTTNNKKSSRRVVVVAAAADDEEGGGG", + "---M------**--*----M---------------M----------------------------", +}; + +} // namespace genetics + +#endif // ANTKEEPER_GENETICS_STANDARD_CODE_HPP diff --git a/src/engine/geom/aabb.hpp b/src/engine/geom/aabb.hpp new file mode 100644 index 0000000..c850837 --- /dev/null +++ b/src/engine/geom/aabb.hpp @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GEOM_AABB_HPP +#define ANTKEEPER_GEOM_AABB_HPP + +#include +#include +#include +#include +#include +#include + +namespace geom { + +/** + * Axis-aligned bounding box. + */ +template +struct aabb: public bounding_volume +{ + typedef math::vector vector_type; + typedef math::matrix matrix_type; + typedef math::transform transform_type; + + vector_type min_point; + vector_type max_point; + + /** + * Transforms an AABB. + * + * @param a AABB to be transformed. + * @param t Transform by which the AABB should be transformed. + * @return Transformed AABB. + */ + static aabb transform(const aabb& a, const transform_type& t); + + /** + * Transforms an AABB. + * + * @param a AABB to be transformed. + * @param m Matrix by which the AABB should be transformed. + * @return Transformed AABB. + */ + static aabb transform(const aabb& a, const matrix_type& m); + + aabb(const vector_type& min_point, const vector_type& max_point); + aabb(); + + virtual bounding_volume_type get_bounding_volume_type() const; + virtual bool intersects(const sphere& sphere) const; + virtual bool intersects(const aabb& aabb) const; + virtual bool contains(const sphere& sphere) const; + virtual bool contains(const aabb& aabb) const; + virtual bool contains(const vector_type& point) const; + + /** + * Returns the position of the specified corner. + * + * @param index Index of a corner. + * @return Position of the specified corner. + */ + vector_type corner(int index) const noexcept; +}; + +template +aabb aabb::transform(const aabb& a, const transform_type& t) +{ + vector_type min_point = {std::numeric_limits::infinity(), std::numeric_limits::infinity(), std::numeric_limits::infinity()}; + vector_type max_point = {-std::numeric_limits::infinity(), -std::numeric_limits::infinity(), -std::numeric_limits::infinity()}; + + for (std::size_t i = 0; i < 8; ++i) + { + vector_type transformed_corner = math::mul(t, a.corner(i)); + + for (std::size_t j = 0; j < 3; ++j) + { + min_point[j] = std::min(min_point[j], transformed_corner[j]); + max_point[j] = std::max(max_point[j], transformed_corner[j]); + } + } + + return {min_point, max_point}; +} + +template +aabb aabb::transform(const aabb& a, const matrix_type& m) +{ + vector_type min_point = {std::numeric_limits::infinity(), std::numeric_limits::infinity(), std::numeric_limits::infinity()}; + vector_type max_point = {-std::numeric_limits::infinity(), -std::numeric_limits::infinity(), -std::numeric_limits::infinity()}; + + for (std::size_t i = 0; i < 8; ++i) + { + vector_type corner = a.corner(i); + math::vector transformed_corner = math::mul(m, math::vector{corner.x(), corner.y(), corner.z(), T(1)}); + + for (std::size_t j = 0; j < 3; ++j) + { + min_point[j] = std::min(min_point[j], transformed_corner[j]); + max_point[j] = std::max(max_point[j], transformed_corner[j]); + } + } + + return {min_point, max_point}; +} + +template +aabb::aabb(const vector_type& min_point, const vector_type& max_point): + min_point(min_point), + max_point(max_point) +{} + +template +aabb::aabb() +{} + +template +inline bounding_volume_type aabb::get_bounding_volume_type() const +{ + return bounding_volume_type::aabb; +} + +template +bool aabb::intersects(const sphere& sphere) const +{ + const vector_type radius_vector = {sphere.radius, sphere.radius, sphere.radius}; + return aabb(min_point - radius_vector, max_point + radius_vector).contains(sphere.center); +} + +template +bool aabb::intersects(const aabb& aabb) const +{ + if (max_point.x() < aabb.min_point.x() || min_point.x() > aabb.max_point.x()) + return false; + if (max_point.y() < aabb.min_point.y() || min_point.y() > aabb.max_point.y()) + return false; + if (max_point.z() < aabb.min_point.z() || min_point.z() > aabb.max_point.z()) + return false; + return true; +} + +template +bool aabb::contains(const sphere& sphere) const +{ + if (sphere.center.x() - sphere.radius < min_point.x() || sphere.center.x() + sphere.radius > max_point.x()) + return false; + if (sphere.center.y() - sphere.radius < min_point.y() || sphere.center.y() + sphere.radius > max_point.y()) + return false; + if (sphere.center.z() - sphere.radius < min_point.z() || sphere.center.z() + sphere.radius > max_point.z()) + return false; + return true; +} + +template +bool aabb::contains(const aabb& aabb) const +{ + if (aabb.min_point.x() < min_point.x() || aabb.max_point.x() > max_point.x()) + return false; + if (aabb.min_point.y() < min_point.y() || aabb.max_point.y() > max_point.y()) + return false; + if (aabb.min_point.z() < min_point.z() || aabb.max_point.z() > max_point.z()) + return false; + return true; +} + +template +bool aabb::contains(const vector_type& point) const +{ + if (point.x() < min_point.x() || point.x() > max_point.x()) + return false; + if (point.y() < min_point.y() || point.y() > max_point.y()) + return false; + if (point.z() < min_point.z() || point.z() > max_point.z()) + return false; + return true; +} + +template +typename aabb::vector_type aabb::corner(int index) const noexcept +{ + return + { + ((index >> 2) & 1) ? max_point[0] : min_point[0], + ((index >> 1) & 1) ? max_point[1] : min_point[1], + ((index >> 0) & 1) ? max_point[2] : min_point[2] + }; +} + +} // namespace geom + +#endif // ANTKEEPER_GEOM_AABB_HPP + diff --git a/src/engine/geom/bounding-volume.hpp b/src/engine/geom/bounding-volume.hpp new file mode 100644 index 0000000..ec53fa0 --- /dev/null +++ b/src/engine/geom/bounding-volume.hpp @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GEOM_BOUNDING_VOLUME_HPP +#define ANTKEEPER_GEOM_BOUNDING_VOLUME_HPP + +#include +#include + +namespace geom { + +template +struct sphere; +template +struct aabb; + +/// Enumerates bounding volume types. +enum class bounding_volume_type +{ + aabb, ///< Indicates the bounding volume is an axis-aligned bounding box. + sphere, ///< Indicates the bounding volume is a sphere. + convex_hull ///< Indicates the bounding volume is a convex hull. +}; + +/** + * Abstract base class for bounding volumes. + * + * @tparam T Scalar type. + */ +template +class bounding_volume +{ +public: + /// Returns the enumerated type of this bounding volume. + virtual bounding_volume_type get_bounding_volume_type() const = 0; + + /// Tests for intersection between this bounding volume and a bounding sphere. + virtual bool intersects(const sphere& sphere) const = 0; + + /// Tests for intersection between this bounding volume and an axis-aligned bounding box. + virtual bool intersects(const aabb& aabb) const = 0; + + /// Tests whether this bounding volume contains a sphere. + virtual bool contains(const sphere& sphere) const = 0; + + /// Tests whether this bounding volume contains an axis-aligned bounding box. + virtual bool contains(const aabb& aabb) const = 0; + + /// Tests whether this bounding volume contains a point. + virtual bool contains(const math::vector& point) const = 0; + + /// Tests for intersection between this bounding volume and another bounding volume. + bool intersects(const bounding_volume& volume) const; +}; + +/// Tests for intersection between this bounding volume and another bounding volume. +template +bool bounding_volume::intersects(const bounding_volume& volume) const +{ + const bounding_volume_type type = volume.get_bounding_volume_type(); + switch (type) + { + case bounding_volume_type::sphere: + return intersects(static_cast&>(volume)); + break; + + case bounding_volume_type::aabb: + return intersects(static_cast&>(volume)); + break; + + default: + throw std::runtime_error("unimplemented"); + break; + } +} + +} // namespace geom + +#endif // ANTKEEPER_GEOM_BOUNDING_VOLUME_HPP diff --git a/src/engine/geom/cartesian.hpp b/src/engine/geom/cartesian.hpp new file mode 100644 index 0000000..eab9df8 --- /dev/null +++ b/src/engine/geom/cartesian.hpp @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GEOM_CARTESIAN_HPP +#define ANTKEEPER_GEOM_CARTESIAN_HPP + +#include +#include + +namespace geom { + +/// Functions which operate on Cartesian (rectangular) coordinates. +namespace cartesian { + +/** + * Converts Cartesian (rectangular) coordinates to spherical coordinates. + * + * @param v Cartesian coordinates. + * @return Spherical coordinates, in the ISO order of radial distance, polar angle (radians), and azimuthal angle (radians). + * + * @see geom::coordinates::spherical + */ +template +math::vector3 to_spherical(const math::vector3& v); + +template +math::vector3 to_spherical(const math::vector3& v) +{ + const T xx_yy = v.x() * v.x() + v.y() * v.y(); + + return math::vector3 + { + std::sqrt(xx_yy + v.z() * v.z()), + std::atan2(v.z(), std::sqrt(xx_yy)), + std::atan2(v.y(), v.x()) + }; +} + +} // namespace cartesian +} // namespace geom + +#endif // ANTKEEPER_GEOM_CARTESIAN_HPP diff --git a/src/engine/geom/convex-hull.hpp b/src/engine/geom/convex-hull.hpp new file mode 100644 index 0000000..fe7ee51 --- /dev/null +++ b/src/engine/geom/convex-hull.hpp @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GEOM_CONVEX_HULL_HPP +#define ANTKEEPER_GEOM_CONVEX_HULL_HPP + +#include +#include +#include +#include +#include +#include + +namespace geom { + +/** + * A plane-bounded convex hull. + */ +template +struct convex_hull: public bounding_volume +{ + /// Vector of planes with descibe the bounds of the convex hull. + std::vector> planes; + + /** + * Creates a convex hull. + * + * @param size Number of planes the convex hull should accommodate. + */ + convex_hull(std::size_t size); + + /// Creates a convex hull. + convex_hull(); + + virtual bounding_volume_type get_bounding_volume_type() const; + virtual bool intersects(const sphere& sphere) const; + virtual bool intersects(const aabb& aabb) const; + virtual bool contains(const sphere& sphere) const; + virtual bool contains(const aabb& aabb) const; + virtual bool contains(const math::vector& point) const; +}; + +template +convex_hull::convex_hull(std::size_t size): + planes(size, plane()) +{} + +template +convex_hull::convex_hull() +{} + +template +inline bounding_volume_type convex_hull::get_bounding_volume_type() const +{ + return bounding_volume_type::convex_hull; +} + +template +bool convex_hull::intersects(const sphere& sphere) const +{ + for (const plane& plane: planes) + if (plane.signed_distance(sphere.center) < -sphere.radius) + return false; + return true; +} + +template +bool convex_hull::intersects(const aabb& aabb) const +{ + math::vector pv; + for (const plane& plane: planes) + { + pv.x() = (plane.normal.x() > T(0)) ? aabb.max_point.x() : aabb.min_point.x(); + pv.y() = (plane.normal.y() > T(0)) ? aabb.max_point.y() : aabb.min_point.y(); + pv.z() = (plane.normal.z() > T(0)) ? aabb.max_point.z() : aabb.min_point.z(); + if (plane.signed_distance(pv) < T(0)) + return false; + } + + return true; +} + +template +bool convex_hull::contains(const sphere& sphere) const +{ + for (const plane& plane: planes) + if (plane.signed_distance(sphere.center) < sphere.radius) + return false; + return true; +} + +template +bool convex_hull::contains(const aabb& aabb) const +{ + math::vector pv; + math::vector nv; + + for (const plane& plane: planes) + { + pv.x() = (plane.normal.x() > T(0)) ? aabb.max_point.x() : aabb.min_point.x(); + pv.y() = (plane.normal.y() > T(0)) ? aabb.max_point.y() : aabb.min_point.y(); + pv.z() = (plane.normal.z() > T(0)) ? aabb.max_point.z() : aabb.min_point.z(); + nv.x() = (plane.normal.x() < T(0)) ? aabb.max_point.x() : aabb.min_point.x(); + nv.y() = (plane.normal.y() < T(0)) ? aabb.max_point.y() : aabb.min_point.y(); + nv.z() = (plane.normal.z() < T(0)) ? aabb.max_point.z() : aabb.min_point.z(); + + if (plane.signed_distance(pv) < T(0) || plane.signed_distance(nv) < T(0)) + return false; + } + + return true; +} + +template +bool convex_hull::contains(const math::vector& point) const +{ + for (const plane& plane: planes) + if (plane.signed_distance(point) < T(0)) + return false; + + return true; +} + +} // namespace geom + +#endif // ANTKEEPER_GEOM_CONVEX_HULL_HPP + diff --git a/src/engine/geom/csg.cpp b/src/engine/geom/csg.cpp new file mode 100644 index 0000000..b7263ea --- /dev/null +++ b/src/engine/geom/csg.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include + +namespace geom { +namespace csg { + +enum class polygon_classification +{ + coplanar, + front, + back, + spanning +}; + +/** + * Classifies a polygon relative to a partitioning plane. + * + * @param partition Partitioning plane relative to which the polygon should be classified. + * @param poly Polygon to be classified. + * @return Classification of the polygon, relative to the plane. + */ +static polygon_classification classify_polygon(const plane& partition, const polygon& poly) +{ + for (const float3& vertex: poly.vertices) + { + + } + + return polygon_classification::coplanar; +} + +/** + * Splits a polygon along a partitioning plane. + * + * @param poly Polygon to split. + * @param partition Partitioning plane along which the polygon should be split. + * @return List of polygons which were formed by splitting the specified polygon along the partitioning plane, along with their respective classifications relative to the partition. + */ +std::list> split_polygon(const polygon& poly, const plane& partition) +{ + return {}; +} + + +bsp_tree::bsp_tree(const std::list& polygons): + front(nullptr), + back(nullptr) +{ + //partition = polygons.front(); + + std::list front_polygons; + std::list back_polygons; + + // Classify all polygons relative to this node's partitioning plane + for (const polygon& p: polygons) + { + polygon_classification classification = classify_polygon(partition, p); + switch (classification) + { + case polygon_classification::coplanar: + coplanar_polygons.push_back(p); + break; + + case polygon_classification::front: + front_polygons.push_back(p); + break; + + case polygon_classification::back: + back_polygons.push_back(p); + break; + + case polygon_classification::spanning: + break; + } + } + + if (!front_polygons.empty()) + { + // Make subtree containing all polygons in front of this node's plane + front = new bsp_tree(front_polygons); + } + + if (!back_polygons.empty()) + { + // Make subtree containing all polygons behind this node's plane + back = new bsp_tree(back_polygons); + } +} + +bsp_tree::~bsp_tree() +{ + delete front; + delete back; +} + +} // namespace csg +} // namespace geom diff --git a/src/engine/geom/csg.hpp b/src/engine/geom/csg.hpp new file mode 100644 index 0000000..d53f5da --- /dev/null +++ b/src/engine/geom/csg.hpp @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GEOM_CSG_HPP +#define ANTKEEPER_GEOM_CSG_HPP + +#include +#include + +namespace geom { + +/// Constructive solid geometry (CSG) +namespace csg { + +struct plane +{ + float3 normal; + float distance; +}; + +struct polygon +{ + std::list vertices; + void* shared; +}; + +/** + * 3D solid represented by a collection of polygons. + */ +typedef std::list solid; + +/** + * BSP tree node. + */ +class bsp_tree +{ +public: + /** + * Recursively constructs a BSP tree from a collection of polygons. + * + * @param polygons Collection of polygons from which to create the BSP tree. + */ + explicit bsp_tree(const std::list& polygons); + + /** + * Destroys a BSP tree. + */ + ~bsp_tree(); + +private: + /// Partition which separates the front and back polygons. + plane partition; + + /// Set of polygons which are coplanar with the partition. + std::list coplanar_polygons; + + /// Subtree containing all polygons in front of the partition. + bsp_tree* front; + + /// Subtree containing all polygons behind the partition. + bsp_tree* back; +}; + +solid op_union(const solid& a, const solid& b); +solid op_difference(const solid& a, const solid& b); +solid op_intersect(const solid& a, const solid& b); + +} // namespace csg +} // namespace geom + +#endif // ANTKEEPER_GEOM_CSG_HPP + diff --git a/src/engine/geom/geom.hpp b/src/engine/geom/geom.hpp new file mode 100644 index 0000000..a86abb4 --- /dev/null +++ b/src/engine/geom/geom.hpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GEOM_HPP +#define ANTKEEPER_GEOM_HPP + +/// Geometry +namespace geom {} + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif // ANTKEEPER_GEOM_HPP diff --git a/src/engine/geom/hyperoctree.hpp b/src/engine/geom/hyperoctree.hpp new file mode 100644 index 0000000..634c9e1 --- /dev/null +++ b/src/engine/geom/hyperoctree.hpp @@ -0,0 +1,577 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GEOM_HYPEROCTREE_HPP +#define ANTKEEPER_GEOM_HYPEROCTREE_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace geom { + +/// Orders in which hyperoctree nodes can be stored and traversed. +enum class hyperoctree_order +{ + /// Hyperoctree nodes are unordered, potentially resulting in faster insertions through the internal use of `std::unordered_set` rather than `std::set`. + unordered, + + /// Hyperoctree nodes are stored and traversed in depth-first preorder. + dfs_pre, + + /// Hyperoctree nodes are stored and traversed in breadth-first order. + bfs +}; + +/// @private +template +using hyperoctree_dfs_pre_compare = std::less; + +/// @private +template +struct hyperoctree_bfs_compare +{ + constexpr bool operator()(const T& lhs, const T& rhs) const noexcept + { + return std::rotr(lhs, DepthBits) < std::rotr(rhs, DepthBits); + } +}; + +/// @private +template +struct hyperoctree_container {}; + +/// @private +template +struct hyperoctree_container +{ + typedef std::unordered_set type; +}; + +/// @private +template +struct hyperoctree_container +{ + typedef std::set> type; +}; + +/// @private +template +struct hyperoctree_container +{ + typedef std::set> type; +}; + +/** + * Hashed linear hyperoctree. + * + * @tparam T Unsigned integral node identifier type. + * @tparam N Number of dimensions. + * @tparam Order Order in which nodes are stored and traversed. + * + * @see http://codervil.blogspot.com/2015/10/octree-node-identifiers.html + * @see https://geidav.wordpress.com/2014/08/18/advanced-octrees-2-node-representations/ + */ +template +class hyperoctree +{ +private: + /** + * Finds the maximum hyperoctree depth level from the size of the node type `T` and number of dimensions `N`. + * + * @return Maximum hyperoctree depth level. + * + * @note There is likely a more elegant formula for this. Information about the 2D and 3D cases is given below: + * + * 2D: + * 8 bit ( 1 byte) = max depth 1 ( 4 loc bits, 1 depth bits, 1 divider bit) = 6 bits + * 16 bit ( 2 byte) = max depth 5 ( 12 loc bits, 3 depth bits, 1 divider bit) = 16 bits + * 32 bit ( 4 byte) = max depth 12 ( 26 loc bits, 4 depth bits, 1 divider bit) = 31 bits + * 64 bit ( 8 byte) = max depth 28 ( 58 loc bits, 5 depth bits, 1 divider bit) = 64 bits + * 128 bit (16 byte) = max depth 59 (120 loc bits, 6 depth bits, 1 divider bit) = 127 bits + * 256 bit (32 byte) = max depth 123 (248 loc bits, 7 depth bits, 1 divider bit) = 256 bits + * + * @see https://oeis.org/A173009 + * + * 3D: + * 8 bit ( 1 byte) = max depth 1 ( 6 loc bits, 1 depth bits, 1 divider bit) = 8 bits + * 16 bit ( 2 byte) = max depth 3 ( 12 loc bits, 2 depth bits, 1 divider bit) = 15 bits + * 32 bit ( 4 byte) = max depth 8 ( 27 loc bits, 4 depth bits, 1 divider bit) = 32 bits + * 64 bit ( 8 byte) = max depth 18 ( 57 loc bits, 5 depth bits, 1 divider bit) = 63 bits + * 128 bit (16 byte) = max depth 39 (120 loc bits, 6 depth bits, 1 divider bit) = 127 bits + * 256 bit (32 byte) = max depth 81 (243 loc bits, 7 depth bits, 1 divider bit) = 251 bits + * + * @see https://oeis.org/A178420 + */ + static consteval std::size_t find_max_depth() noexcept + { + std::size_t max_depth = 0; + for (std::size_t i = 1; i <= sizeof(T) * 8; ++i) + { + const std::size_t location_bits = sizeof(T) * 8 - i; + max_depth = location_bits / N - 1; + const std::size_t depth_bits = static_cast(std::bit_width(max_depth)); + + if (depth_bits + location_bits < sizeof(T) * 8) + break; + } + return static_cast(max_depth); + } + +public: + /// Node identifier type. + typedef T node_type; + + /// Number of dimensions. + static constexpr std::size_t dimensions = N; + + /// Node storage and traversal order. + static constexpr hyperoctree_order order = Order; + + /// Maximum node depth level. + static constexpr node_type max_depth = find_max_depth(); + + /// Number of bits in the node type. + static constexpr node_type node_bits = sizeof(node_type) * 8; + + /// Number of bits required to encode the depth of a node. + static constexpr node_type depth_bits = std::bit_width(max_depth); + + /// Number of bits required to encode the Morton location code of a node. + static constexpr node_type location_bits = (max_depth + 1) * N; + + /// Number of bits separating the depth and Morton location code in a node identifier. + static constexpr node_type divider_bits = node_bits - (depth_bits + location_bits); + + /// Number of children per node. + static constexpr node_type children_per_node = math::compile::exp2(N); + + /// Number of siblings per node. + static constexpr node_type siblings_per_node = children_per_node - 1; + + /// Resolution in each dimension. + static constexpr node_type resolution = math::compile::exp2(max_depth); + + /// Number of nodes in a full hyperoctree. + static constexpr std::size_t max_node_count = (math::compile::pow(resolution * 2, N) - 1) / siblings_per_node; + + /// Node identifier of the persistent root node. + static constexpr node_type root = 0; + + /// Node container type. + typedef typename hyperoctree_container::type container_type; + + /// Iterator type. + typedef typename container_type::iterator iterator; + + /// Constant iterator type. + typedef typename container_type::const_iterator const_iterator; + + /// Reverse iterator type. + typedef std::conditional, iterator>::type reverse_iterator; + + /// Constant reverse iterator type. + typedef std::conditional, const_iterator>::type const_reverse_iterator; + + /// @name Nodes + /// @{ + /** + * Extracts the depth of a node from its identifier. + * + * @param node Node identifier. + * + * @return Depth of the node. + */ + static inline constexpr node_type depth(node_type node) noexcept + { + constexpr node_type mask = math::compile::exp2(depth_bits) - 1; + return node & mask; + } + + /** + * Extracts the Morton location code of a node from its identifier. + * + * @param node Node identifier. + * + * @return Morton location code of the node. + */ + static inline constexpr node_type location(node_type node) noexcept + { + return node >> ((node_bits - 1) - depth(node) * N); + } + + /** + * Extracts the depth and Morton location code of a node from its identifier. + * + * @param node Node identifier. + * + * @return Array containing the depth of the node, followed by the Morton location code of the node. + */ + static constexpr std::array split(node_type node) noexcept + { + const node_type depth = hyperoctree::depth(node); + const node_type location = node >> ((node_bits - 1) - depth * N); + return {depth, location}; + } + + /** + * Constructs an identifier for a node at the given depth and location. + * + * @param depth Depth level. + * @param location Morton location code. + * + * @return Identifier of a node at the given depth and location. + * + * @warning If @p depth exceeds `max_depth`, the returned node identifier is not valid. + */ + static inline constexpr node_type node(node_type depth, node_type location) noexcept + { + return (location << ((node_bits - 1) - depth * N)) | depth; + } + + /** + * Constructs an identifier for the ancestor of a node at a given depth. + * + * @param node Node identifier. + * @param depth Absolute depth of an ancestor node. + * + * @return Identifier of the ancestor of the node at the given depth. + * + * @warning If @p depth exceeds the depth of @p node, the returned node identifier is not valid. + */ + static inline constexpr node_type ancestor(node_type node, node_type depth) noexcept + { + const node_type mask = (~node_type(0)) << ((node_bits - 1) - depth * N); + return (node & mask) | depth; + } + + /** + * Constructs an identifier for the parent of a node. + * + * @param node Node identifier. + * + * @return Identifier of the parent node. + */ + static inline constexpr node_type parent(node_type node) noexcept + { + return ancestor(node, depth(node) - 1); + } + + /** + * Constructs an identifier for the nth sibling of a node. + * + * @param node Node identifier. + * @param n Offset to a sibling, automatically wrapped to `[0, siblings_per_node]`. + * + * @return Identifier of the nth sibling node. + */ + static constexpr node_type sibling(node_type node, node_type n) noexcept + { + constexpr node_type mask = (1 << N) - 1; + const auto [depth, location] = split(node); + const node_type sibling_location = (location & (~mask)) | ((location + n) & mask); + return hyperoctree::node(depth, sibling_location); + } + + /** + * Constructs an identifier for the nth child of a node. + * + * @param node Node identifier. + * @param n Offset to a sibling of the first child node, automatically wrapped to `[0, siblings_per_node]`. + * + * @return Identifier of the nth child node. + */ + static inline constexpr node_type child(node_type node, T n) noexcept + { + return sibling(node + 1, n); + } + + /** + * Constructs an identifier for the first common ancestor of two nodes + * + * @param a Identifier of the first node. + * @param b Identifier of the second node. + * + * @return Identifier of the first common ancestor of the two nodes. + */ + static constexpr node_type common_ancestor(node_type a, node_type b) noexcept + { + const node_type bits = std::min(depth(a), depth(b)) * N; + const node_type marker = (node_type(1) << (node_bits - 1)) >> bits; + const node_type depth = node_type(std::countl_zero((a ^ b) | marker) / N); + return ancestor(a, depth); + } + /// @} + + /// Constructs a hyperoctree with a single root node. + hyperoctree(): + nodes({root}) + {} + + /// @name Iterators + /// @{ + /** + * Returns an iterator to the first node, in the traversal order specified by hyperoctree::order. + * + * @note Node identifiers cannot be modified through iterators. + */ + /// @{ + inline iterator begin() noexcept + { + return nodes.begin(); + } + inline const_iterator begin() const noexcept + { + return nodes.begin(); + } + inline const_iterator cbegin() const noexcept + { + return nodes.cbegin(); + } + /// @} + + /** + * Returns an iterator to the node following the last node, in the traversal order specified by hyperoctree::order. + * + * @note Node identifiers cannot be modified through iterators. + */ + /// @{ + inline iterator end() noexcept + { + return nodes.end(); + } + inline const_iterator end() const noexcept + { + return nodes.end(); + } + inline const_iterator cend() const noexcept + { + return nodes.cend(); + } + /// @} + + /** + * Returns a reverse iterator to the first node of the revered hyperoctree, in the traversal order specified by hyperoctree::order. + * + * @note Node identifiers cannot be modified through iterators. + * @note If the hyperoctree is unordered, reverse iteration and forward iteration will be identical. + */ + /// @{ + inline reverse_iterator rbegin() noexcept + { + if constexpr (order != hyperoctree_order::unordered) + return nodes.rbegin(); + else + return nodes.begin(); + } + inline const_reverse_iterator rbegin() const noexcept + { + if constexpr (order != hyperoctree_order::unordered) + return nodes.rbegin(); + else + return nodes.begin(); + } + inline const_reverse_iterator crbegin() const noexcept + { + if constexpr (order != hyperoctree_order::unordered) + return nodes.crbegin(); + else + return nodes.cbegin(); + } + /// @} + + /** + * Returns a reverse iterator to the node following the last node of the reverse hyperoctree, in the traversal order specified by hyperoctree::order. + * + * @note Node identifiers cannot be modified through iterators. + * @note If the hyperoctree is unordered, reverse iteration and forward iteration will be identical. + */ + /// @{ + inline reverse_iterator rend() noexcept + { + if constexpr (order != hyperoctree_order::unordered) + return nodes.rend(); + else + return nodes.end(); + } + inline const_reverse_iterator rend() const noexcept + { + if constexpr (order != hyperoctree_order::unordered) + return nodes.rend(); + else + return nodes.end(); + } + inline const_reverse_iterator crend() const noexcept + { + if constexpr (order != hyperoctree_order::unordered) + return nodes.crend(); + else + return nodes.cend(); + } + /// @} + /// @} + + /// @name Capacity + /// @{ + /** + * Checks if the hyperoctree has no nodes. + * + * @return `true` if the hyperoctree is empty, `false` otherwise. + * + * @note This function should always return `false`, as the root node is persistent. + */ + inline bool empty() const noexcept + { + return nodes.empty(); + } + + /** + * Checks if the hyperoctree is full. + * + * @return `true` if the hyperoctree is full, `false` otherwise. + */ + inline bool full() const noexcept + { + return size() == max_size(); + } + + /** + * Returns the number of nodes in the hyperoctree. + * + * @return Number of nodes in the hyperoctree. + * + * @note Hyperoctree size will always be greater than or equal to one, as the root node is persistent. + */ + inline std::size_t size() const noexcept + { + return nodes.size(); + } + + /// Returns the total number of nodes the hyperoctree is capable of containing. + constexpr std::size_t max_size() const noexcept + { + return max_node_count; + } + /// @} + + /// @name Modifiers + /// @{ + /** + * Erases all nodes except the root node, which is persistent. + */ + inline void clear() + { + nodes = {root}; + } + + /** + * Inserts a node and its siblings into the hyperoctree, inserting ancestors as necessary. + * + * @param node Node to insert. + * + * @note The root node is persistent and does not need to be inserted. + */ + void insert(node_type node) + { + if (contains(node)) + return; + + // Insert node + nodes.emplace(node); + + // Insert node siblings + for (node_type i = 1; i < children_per_node; ++i) + nodes.emplace(sibling(node, i)); + + // Insert node ancestors + insert(parent(node)); + } + + /** + * Erases a node, along with its descendants, siblings, and descendants of siblings. + * + * @param node Identifier of the node to erase. + * + * @note The root node is persistent and cannot be erased. + */ + void erase(node_type node) + { + if (node == root || !contains(node)) + return; + + // Erase node and its descendants + nodes.erase(node); + erase(child(node, 0)); + + // Erase node siblings + for (node_type i = 0; i < siblings_per_node; ++i) + { + node = sibling(node, 1); + + // Erase sibling and its descendants + nodes.erase(node); + erase(child(node, 0)); + } + } + /// @} + + /// @name Lookup + /// @{ + /** + * Checks if a node is contained within the hyperoctree. + * + * @param node Identifier of the node to check for. + * + * @return `true` if the hyperoctree contains the node, `false` otherwise. + */ + inline bool contains(node_type node) const + { + return nodes.contains(node); + } + + /** + * Checks if a node has no children. + * + * @param node Node identififer. + * + * @return `true` if the node has no children, and `false` otherwise. + */ + inline bool is_leaf(node_type node) const + { + return !contains(child(node, 0)); + } + /// @} + +private: + container_type nodes; +}; + +/// Hyperoctree with unordered node storage and traversal. +template +using unordered_hyperoctree = hyperoctree; + +} // namespace geom + +#endif // ANTKEEPER_GEOM_HYPEROCTREE_HPP diff --git a/src/engine/geom/intersection.cpp b/src/engine/geom/intersection.cpp new file mode 100644 index 0000000..22810f4 --- /dev/null +++ b/src/engine/geom/intersection.cpp @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include + +namespace geom { + +std::tuple ray_plane_intersection(const ray& ray, const plane& plane) +{ + float denom = math::dot(ray.direction, plane.normal); + if (denom != 0.0f) + { + float t = -(math::dot(ray.origin, plane.normal) + plane.distance) / denom; + + if (t >= 0.0f) + { + return std::make_tuple(true, t); + } + } + + return std::make_tuple(false, std::numeric_limits::infinity()); +} + +std::tuple ray_triangle_intersection(const ray& ray, const float3& a, const float3& b, const float3& c) +{ + // Find edges + float3 edge10 = b - a; + float3 edge20 = c - a; + + // Calculate determinant + float3 pv = math::cross(ray.direction, edge20); + float det = math::dot(edge10, pv); + + if (!det) + { + return std::make_tuple(false, std::numeric_limits::infinity(), 0.0f, 0.0f); + } + + float inverse_det = 1.0f / det; + + // Calculate u + float3 tv = ray.origin - a; + float u = math::dot(tv, pv) * inverse_det; + + if (u < 0.0f || u > 1.0f) + { + return std::make_tuple(false, std::numeric_limits::infinity(), 0.0f, 0.0f); + } + + // Calculate v + float3 qv = math::cross(tv, edge10); + float v = math::dot(ray.direction, qv) * inverse_det; + + if (v < 0.0f || u + v > 1.0f) + { + return std::make_tuple(false, std::numeric_limits::infinity(), 0.0f, 0.0f); + } + + // Calculate t + float t = math::dot(edge20, qv) * inverse_det; + + if (t > 0.0f) + { + return std::make_tuple(true, t, u, v); + } + + return std::make_tuple(false, std::numeric_limits::infinity(), 0.0f, 0.0f); +} + +std::tuple ray_aabb_intersection(const ray& ray, const aabb& aabb) +{ + float t0 = -std::numeric_limits::infinity(); + float t1 = std::numeric_limits::infinity(); + + for (std::size_t i = 0; i < 3; ++i) + { + if (ray.direction[i] == 0.0f) + { + if (ray.origin[i] < aabb.min_point[i] || ray.origin[i] > aabb.max_point[i]) + { + return std::make_tuple(false, std::numeric_limits::infinity(), std::numeric_limits::infinity()); + } + } + else + { + float tmin = (aabb.min_point[i] - ray.origin[i]) / ray.direction[i]; + float tmax = (aabb.max_point[i] - ray.origin[i]) / ray.direction[i]; + + t0 = std::max(t0, std::min(tmin, tmax)); + t1 = std::min(t1, std::max(tmin, tmax)); + } + } + + if (t0 > t1 || t1 < 0.0f) + { + return std::make_tuple(false, std::numeric_limits::infinity(), std::numeric_limits::infinity()); + } + + return std::make_tuple(true, t0, t1); +} + +std::tuple ray_mesh_intersection(const ray& ray, const mesh& mesh) +{ + const std::vector& triangles = mesh.get_faces(); + + bool intersection = false; + float t0 = std::numeric_limits::infinity(); + float t1 = -std::numeric_limits::infinity(); + std::size_t index0 = triangles.size(); + std::size_t index1 = triangles.size(); + + for (std::size_t i = 0; i < triangles.size(); ++i) + { + const mesh::face* triangle = triangles[i]; + + const float3& a = reinterpret_cast(triangle->edge->vertex->position); + const float3& b = reinterpret_cast(triangle->edge->next->vertex->position); + const float3& c = reinterpret_cast(triangle->edge->previous->vertex->position); + + auto result = ray_triangle_intersection(ray, a, b, c); + + if (std::get<0>(result)) + { + intersection = true; + float t = std::get<1>(result); + + if (t < t0) + { + t0 = t; + index0 = i; + } + + if (t > t1) + { + t1 = t; + index1 = i; + } + } + } + + return std::make_tuple(intersection, t0, t1, index0, index1); +} + +bool aabb_aabb_intersection(const aabb& a, const aabb& b) +{ + if (a.max_point.x() < b.min_point.x() || a.min_point.x() > b.max_point.x()) + return false; + if (a.max_point.y() < b.min_point.y() || a.min_point.y() > b.max_point.y()) + return false; + if (a.max_point.z() < b.min_point.z() || a.min_point.z() > b.max_point.z()) + return false; + return true; +} + +bool aabb_sphere_intersection(const aabb& aabb, const float3& center, float radius) +{ + float sqr_distance = 0.0f; + for (int i = 0; i < 3; ++i) + { + float v = center[i]; + if (v < aabb.min_point[i]) + sqr_distance += (aabb.min_point[i] - v) * (aabb.min_point[i] - v); + if (v > aabb.max_point[i]) + sqr_distance += (v - aabb.max_point[i]) * (v - aabb.max_point[i]); + } + + return (sqr_distance <= (radius * radius)); +} + +} // namespace geom diff --git a/src/engine/geom/intersection.hpp b/src/engine/geom/intersection.hpp new file mode 100644 index 0000000..83bd0f5 --- /dev/null +++ b/src/engine/geom/intersection.hpp @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GEOM_INTERSECTION_HPP +#define ANTKEEPER_GEOM_INTERSECTION_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace geom { + +/** + * Tests for intersection between a ray and a plane. + * + * @param ray Ray to test for intersection. + * @param plane Plane to test for intersection. + * @return Tuple containing a boolean indicating whether intersection occurred, and a float indicating the signed distance along the ray to the point of intersection. + */ +std::tuple ray_plane_intersection(const ray& ray, const plane& plane); + +std::tuple ray_triangle_intersection(const ray& ray, const float3& a, const float3& b, const float3& c); + +std::tuple ray_aabb_intersection(const ray& ray, const aabb& aabb); + +std::tuple ray_mesh_intersection(const ray& ray, const mesh& mesh); + +/** + * Ray-sphere intersection test. + */ +template +std::tuple ray_sphere_intersection(const ray& ray, const sphere& sphere) +{ + const auto d = ray.origin - sphere.center; + const T b = math::dot(d, ray.direction); + const T c = math::dot(d, d) - sphere.radius * sphere.radius; + T h = b * b - c; + + if (h < T(0)) + return {false, T(0), T(0)}; + + h = std::sqrt(h); + + return {true, -b - h, -b + h}; +} + +bool aabb_sphere_intersection(const aabb& aabb, const float3& center, float radius); + +bool aabb_aabb_intersection(const aabb& a, const aabb& b); + +} // namespace geom + +#endif // ANTKEEPER_GEOM_INTERSECTION_HPP + diff --git a/src/engine/geom/marching-cubes.cpp b/src/engine/geom/marching-cubes.cpp new file mode 100644 index 0000000..ea3fdaf --- /dev/null +++ b/src/engine/geom/marching-cubes.cpp @@ -0,0 +1,418 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include + +namespace geom { +namespace mc { + +static constexpr std::uint_fast8_t vertex_table[12][2] = +{ + {0, 1}, + {1, 2}, + {2, 3}, + {3, 0}, + {4, 5}, + {5, 6}, + {6, 7}, + {7, 4}, + {0, 4}, + {1, 5}, + {2, 6}, + {3, 7} +}; + +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 +}; + +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}, + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1} +}; + +void polygonize(float* vertices, std::uint_fast8_t* vertex_count, std::int_fast8_t* triangles, std::uint_fast8_t* triangle_count, const float* corners, const float* distances) +{ + *vertex_count = 0; + *triangle_count = 0; + + // Calculate signed distances for each cube corner and form an edge table index. + std::uint_fast8_t edge_index = 0; + for (std::uint_fast8_t i = 0; i < 8; ++i) + edge_index |= (distances[i] < 0.0f) ? (1 << i) : 0; + + // Get edge flags from edge table + const std::uint_fast16_t edge_flags = edge_table[edge_index]; + if (!edge_flags) + return; + + // Get vertex indices of the case + const std::int_fast8_t* indices = triangle_table[edge_index]; + + // Calculate vertices and store in vertex buffer + float vertex_buffer[12 * 3]; + for (std::uint_fast16_t i = 0; i < 12; ++i) + { + // If this edge is intersected + if (edge_flags & (1 << i)) + { + // Find the two vertices which make up edge ab + std::uint_fast8_t a = vertex_table[i][0]; + std::uint_fast8_t b = vertex_table[i][1]; + const float* v_a = corners + a * 3; + const float* v_b = corners + b * 3; + float f_a = distances[a]; + float f_b = distances[b]; + + // Determine interpolation ratio + static const float epsilon = 0.00001f; + float t; + if (std::fabs(f_a) < epsilon) + t = 1.0f; + else if (std::fabs(f_b) < epsilon) + t = 0.0f; + else if (std::fabs(f_b - f_a) < epsilon) + t = 0.5f; // Paul Bourke suggested this be 1.0f? Why? + else + t = (-f_a) / (f_b - f_a); + + // Interpolate between vertices + float* v = vertex_buffer + i * 3; + v[0] = v_a[0] + t * (v_b[0] - v_a[0]); + v[1] = v_a[1] + t * (v_b[1] - v_a[1]); + v[2] = v_a[2] + t * (v_b[2] - v_a[2]); + } + } + + // Remap vertex buffer to be stored contiguously + std::int_fast8_t vertex_remap[12]; + for (std::uint_fast8_t i = 0; i < 12; ++i) + vertex_remap[i] = -1; + for (std::uint_fast8_t i = 0; indices[i] != -1; ++i) + { + if (vertex_remap[indices[i]] == -1) + { + std::int_fast8_t index = indices[i] * 3; + *(vertices++) = vertex_buffer[ index]; + *(vertices++) = vertex_buffer[++index]; + *(vertices++) = vertex_buffer[++index]; + vertex_remap[indices[i]] = (*vertex_count)++; + } + } + + // Form triangles + for (std::uint_fast8_t i = 0; indices[i] != -1;) + { + *(triangles++) = vertex_remap[indices[i++]]; + *(triangles++) = vertex_remap[indices[i++]]; + *(triangles++) = vertex_remap[indices[i++]]; + ++(*triangle_count); + } +} + +} // namespace mc +} // namespace geom diff --git a/src/geom/marching-cubes.hpp b/src/engine/geom/marching-cubes.hpp similarity index 100% rename from src/geom/marching-cubes.hpp rename to src/engine/geom/marching-cubes.hpp diff --git a/src/engine/geom/mesh-accelerator.cpp b/src/engine/geom/mesh-accelerator.cpp new file mode 100644 index 0000000..d59ebdf --- /dev/null +++ b/src/engine/geom/mesh-accelerator.cpp @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include +#include + +namespace geom { + +mesh_accelerator::mesh_accelerator() +{} + +void mesh_accelerator::build(const mesh& mesh) +{ + // Clear octree and face map + octree.clear(); + face_map.clear(); + + // Calculate mesh dimensions + aabb bounds = calculate_bounds(mesh); + float3 mesh_dimensions = bounds.max_point - bounds.min_point; + center_offset = mesh_dimensions * 0.5f - (bounds.min_point + bounds.max_point) * 0.5f; + + // Calculate node dimensions at each octree depth + for (auto i = 0; i <= octree_type::max_depth; ++i) + { + node_dimensions[i] = mesh_dimensions * static_cast((1.0f / std::pow(2, i))); + } + + // Add faces to octree + for (mesh::face* face: mesh.get_faces()) + { + // Calculate face bounds + float3 min_point = reinterpret_cast(face->edge->vertex->position); + float3 max_point = min_point; + mesh::edge* edge = face->edge; + do + { + const auto& position = edge->vertex->position; + for (int i = 0; i < 3; ++i) + { + min_point[i] = std::min(min_point[i], position[i]); + max_point[i] = std::max(max_point[i], position[i]); + } + + edge = edge->next; + } + while (edge != face->edge); + + // 1. Find max depth node of aabb min + // 2. Find max depth node of aabb max + // 3. Find common ancestor of the two nodes--that's the containing node. + typename octree_type::node_type min_node = find_node(min_point); + typename octree_type::node_type max_node = find_node(max_point); + typename octree_type::node_type containing_node = octree_type::common_ancestor(min_node, max_node); + + // Insert containing node into octree + octree.insert(containing_node); + + // Add face to face map + face_map[containing_node].push_back(face); + } +} + +std::optional mesh_accelerator::query_nearest(const ray& ray) const +{ + ray_query_result result; + result.t = std::numeric_limits::infinity(); + result.face = nullptr; + + query_nearest_recursive(result.t, result.face, octree.root, ray); + + if (result.face) + return std::optional{result}; + return std::nullopt; +} + +void mesh_accelerator::query_nearest_recursive(float& nearest_t, geom::mesh::face*& nearest_face, typename octree_type::node_type node, const ray& ray) const +{ + // Get node bounds + const aabb node_bounds = get_node_bounds(node); + + // Test for intersection with node bounds + auto aabb_intersection = ray_aabb_intersection(ray, node_bounds); + + // If ray passed through this node + if (std::get<0>(aabb_intersection)) + { + // Test all triangles in the node + if (auto it = face_map.find(node); it != face_map.end()) + { + const std::list& faces = it->second; + + for (mesh::face* face: faces) + { + // Get triangle coordinates + const float3& a = reinterpret_cast(face->edge->vertex->position); + const float3& b = reinterpret_cast(face->edge->next->vertex->position); + const float3& c = reinterpret_cast(face->edge->previous->vertex->position); + + // Test for intersection with triangle + auto triangle_intersection = ray_triangle_intersection(ray, a, b, c); + if (std::get<0>(triangle_intersection)) + { + float t = std::get<1>(triangle_intersection); + if (t < nearest_t) + { + nearest_t = t; + nearest_face = face; + } + } + } + } + + // Test all child nodes + if (!octree.is_leaf(node)) + { + for (int i = 0; i < 8; ++i) + query_nearest_recursive(nearest_t, nearest_face, octree.child(node, i), ray); + } + } +} + +aabb mesh_accelerator::get_node_bounds(typename octree_type::node_type node) const +{ + // Decode Morton location of node + std::uint32_t x, y, z; + morton::decode(octree_type::location(node), x, y, z); + float3 node_location = float3{static_cast(x), static_cast(y), static_cast(z)}; + + // Get node dimensions at node depth + const float3& dimensions = node_dimensions[octree_type::depth(node)]; + + // Calculate AABB + float3 min_point = (node_location * dimensions) - center_offset; + return aabb{min_point, min_point + dimensions}; +} + +typename mesh_accelerator::octree_type::node_type mesh_accelerator::find_node(const float3& point) const +{ + // Transform point to octree space + float3 transformed_point = (point + center_offset); + + // Account for floating-point tolerance + const float epsilon = 0.00001f; + transformed_point.x() = std::max(0.0f, std::min(node_dimensions[0].x() - epsilon, transformed_point.x())); + transformed_point.y() = std::max(0.0f, std::min(node_dimensions[0].y() - epsilon, transformed_point.y())); + transformed_point.z() = std::max(0.0f, std::min(node_dimensions[0].z() - epsilon, transformed_point.z())); + + // Transform point to max-depth node space + transformed_point = transformed_point / node_dimensions[octree_type::max_depth]; + + // Encode transformed point as a Morton location code + std::uint32_t location = morton::encode( + static_cast(transformed_point.x()), + static_cast(transformed_point.y()), + static_cast(transformed_point.z())); + + // Return max depth node at the determined location + return octree_type::node(octree_type::max_depth, location); +} + +} // namespace geom diff --git a/src/engine/geom/mesh-accelerator.hpp b/src/engine/geom/mesh-accelerator.hpp new file mode 100644 index 0000000..6ae344e --- /dev/null +++ b/src/engine/geom/mesh-accelerator.hpp @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GEOM_MESH_ACCELERATOR_HPP +#define ANTKEEPER_GEOM_MESH_ACCELERATOR_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace geom { + +/** + * Acceleration structure for querying mesh geometry. + */ +class mesh_accelerator +{ +public: + struct ray_query_result + { + float t; + geom::mesh::face* face; + }; + + mesh_accelerator(); + + /** + * Builds the acceleration structure. + */ + void build(const mesh& mesh); + + /** + * 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 occurred. + */ + std::optional query_nearest(const ray& ray) const; + +private: + typedef unordered_octree32 octree_type; + + aabb get_node_bounds(typename octree_type::node_type node) const; + + void query_nearest_recursive(float& nearest_t, geom::mesh::face*& nearest_face, typename octree_type::node_type node, const ray& ray) const; + + /// Returns the max-depth node in which the point is located + typename octree_type::node_type find_node(const float3& point) const; + + octree_type octree; + float3 node_dimensions[octree_type::max_depth + 1]; + float3 center_offset; + std::unordered_map> face_map; +}; + +} // namespace geom + +#endif // ANTKEEPER_GEOM_MESH_ACCELERATOR_HPP + diff --git a/src/engine/geom/mesh-functions.cpp b/src/engine/geom/mesh-functions.cpp new file mode 100644 index 0000000..baefaef --- /dev/null +++ b/src/engine/geom/mesh-functions.cpp @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include + +namespace geom { + +struct edge_hasher +{ + std::size_t operator()(const std::array& v) const noexcept + { + std::size_t hash = std::hash()(v[0]); + return hash ^ (std::hash()(v[1]) + 0x9e3779b9 + (hash << 6) + (hash >> 2)); + } +}; + +void create_triangle_mesh(mesh& mesh, const std::vector& vertices, const std::vector>& triangles) +{ + for (const auto& vertex: vertices) + mesh.add_vertex(vertex); + + std::unordered_map, geom::mesh::edge*, edge_hasher> edge_map; + const std::vector& mesh_vertices = mesh.get_vertices(); + std::vector loop(3); + + for (const auto& triangle: triangles) + { + geom::mesh::vertex* triangle_vertices[3] = + { + mesh_vertices[triangle[0]], + mesh_vertices[triangle[1]], + mesh_vertices[triangle[2]] + }; + + for (int j = 0; j < 3; ++j) + { + geom::mesh::vertex* start = triangle_vertices[j]; + geom::mesh::vertex* end = triangle_vertices[(j + 1) % 3]; + + if (auto it = edge_map.find({start->index, end->index}); it != edge_map.end()) + { + loop[j] = it->second; + } + else + { + loop[j] = mesh.add_edge(start, end); + edge_map[{start->index, end->index}] = loop[j]; + edge_map[{end->index, start->index}] = loop[j]->symmetric; + } + + } + + mesh.add_face(loop); + } +} + +void calculate_face_normals(float3* normals, const mesh& mesh) +{ + const std::vector& faces = mesh.get_faces(); + + for (std::size_t i = 0; i < faces.size(); ++i) + { + const mesh::face& face = *(faces[i]); + const float3& a = face.edge->vertex->position; + const float3& b = face.edge->next->vertex->position; + const float3& c = face.edge->previous->vertex->position; + + normals[i] = math::normalize(math::cross(b - a, c - a)); + } +} + +float3 calculate_face_normal(const mesh::face& face) +{ + const float3& a = face.edge->vertex->position; + const float3& b = face.edge->next->vertex->position; + const float3& c = face.edge->previous->vertex->position; + return math::normalize(math::cross(b - a, c - a)); +} + +void calculate_vertex_tangents(float4* tangents, const float2* texcoords, const float3* normals, const mesh& mesh) +{ + const std::vector& faces = mesh.get_faces(); + const std::vector& vertices = mesh.get_vertices(); + + // Allocate tangent and bitangent buffers + float3* tangent_buffer = new float3[vertices.size()]; + float3* bitangent_buffer = new float3[vertices.size()]; + for (std::size_t i = 0; i < vertices.size(); ++i) + { + tangent_buffer[i] = {0.0f, 0.0f, 0.0f}; + bitangent_buffer[i] = {0.0f, 0.0f, 0.0f}; + } + + // Accumulate tangents and bitangents + for (std::size_t i = 0; i < faces.size(); ++i) + { + const mesh::face& face = *(faces[i]); + std::size_t ia = face.edge->vertex->index; + std::size_t ib = face.edge->next->vertex->index; + std::size_t ic = face.edge->previous->vertex->index; + const float3& a = vertices[ia]->position; + const float3& b = vertices[ib]->position; + const float3& c = vertices[ic]->position; + const float2& uva = texcoords[ia]; + const float2& uvb = texcoords[ib]; + const float2& uvc = texcoords[ic]; + + float3 ba = b - a; + float3 ca = c - a; + float2 uvba = uvb - uva; + float2 uvca = uvc - uva; + + float f = 1.0f / (uvba.x() * uvca.y() - uvca.x() * uvba.y()); + float3 tangent = (ba * uvca.y() - ca * uvba.y()) * f; + float3 bitangent = (ba * -uvca.x() + ca * uvba.x()) * f; + + tangent_buffer[ia] += tangent; + tangent_buffer[ib] += tangent; + tangent_buffer[ic] += tangent; + bitangent_buffer[ia] += bitangent; + bitangent_buffer[ib] += bitangent; + bitangent_buffer[ic] += bitangent; + } + + // Orthogonalize tangents + for (std::size_t i = 0; i < vertices.size(); ++i) + { + const float3& n = normals[i]; + const float3& t = tangent_buffer[i]; + const float3& b = bitangent_buffer[i]; + + // Gram-Schmidt orthogonalize tangent + float3 tangent = math::normalize(t - n * math::dot(n, t)); + + // Calculate bitangent sign + float bitangent_sign = (math::dot(math::cross(n, t), b) < 0.0f) ? -1.0f : 1.0f; + + tangents[i] = {tangent.x(), tangent.y(), tangent.z(), bitangent_sign}; + } + + // Free faceted tangents and bitangents + delete[] tangent_buffer; + delete[] bitangent_buffer; +} + +aabb calculate_bounds(const mesh& mesh) +{ + float3 bounds_min; + float3 bounds_max; + for (int i = 0; i < 3; ++i) + { + bounds_min[i] = std::numeric_limits::infinity(); + bounds_max[i] = -std::numeric_limits::infinity(); + } + + for (const mesh::vertex* vertex: mesh.get_vertices()) + { + const auto& position = vertex->position; + for (int i = 0; i < 3; ++i) + { + bounds_min[i] = std::min(bounds_min[i], position[i]); + bounds_max[i] = std::max(bounds_max[i], position[i]); + } + } + + return aabb{bounds_min, bounds_max}; +} + +mesh::vertex* poke_face(mesh& mesh, std::size_t index) +{ + mesh::face* face = mesh.get_faces()[index]; + + // Collect face edges and sum edge vertex positions + mesh::loop loop = {face->edge}; + float3 sum_positions = face->edge->vertex->position; + for (mesh::edge* edge = face->edge->next; edge != face->edge; edge = edge->next) + { + loop.push_back(edge); + sum_positions += edge->vertex->position; + } + + if (loop.size() <= 2) + return nullptr; + + // Remove face + mesh.remove_face(face); + + // Add vertex in center + mesh::vertex* center = mesh.add_vertex(sum_positions / static_cast(loop.size())); + + // Create first triangle + geom::mesh::edge* ab = loop[0]; + geom::mesh::edge* bc = mesh.add_edge(ab->next->vertex, center); + geom::mesh::edge* ca = mesh.add_edge(center, ab->vertex); + mesh.add_face({ab, bc, ca}); + + // Save first triangle CA edge + geom::mesh::edge* first_triangle_ca = ca; + + // Create remaining triangles + for (std::size_t i = 1; i < loop.size(); ++i) + { + ab = loop[i]; + ca = bc->symmetric; + + if (i == loop.size() - 1) + bc = first_triangle_ca->symmetric; + else + bc = mesh.add_edge(ab->next->vertex, center); + + mesh.add_face({ab, bc, ca}); + } + + return center; +} + +} // namespace geom diff --git a/src/engine/geom/mesh-functions.hpp b/src/engine/geom/mesh-functions.hpp new file mode 100644 index 0000000..c1fbfd8 --- /dev/null +++ b/src/engine/geom/mesh-functions.hpp @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GEOM_MESH_FUNCTIONS_HPP +#define ANTKEEPER_GEOM_MESH_FUNCTIONS_HPP + +#include +#include +#include +#include +#include + +namespace geom { + +/** + * Creates a triangle mesh from a list of vertices and indices. + * + * @param[out] mesh Mesh to which vertices, edges, and faces will be added. + * @param vertices Vertex positions + * @param triangles Triangle indices + * @return Newly created triangle mesh. + */ +void create_triangle_mesh(mesh& mesh, const std::vector& vertices, const std::vector>& triangles); + +/** + * Calculates normals for each face. + * + * @param[out] Array in which faceted normals will be stored. + */ +void calculate_face_normals(float3* normals, const mesh& mesh); + +float3 calculate_face_normal(const mesh::face& face); + +/** + * Calculates smooth tangents per-vertex. + * + * @param[out] tangents Array in which vertex tangents will be stored. A bitangent sign is stored in each tangent vector's fourth component. + * @param[in] texcoords Array containing vertex texture coordinates. + * @param[in] normals Array containing vertex normals. + */ +void calculate_vertex_tangents(float4* tangents, const float2* texcoords, const float3* normals, const mesh& mesh); + +/** + * Calculates the AABB bounds of a mesh. + */ +aabb calculate_bounds(const mesh& mesh); + +/** + * Triangulates a face by adding a new vertex in the center, then creating triangles between the edges of the original face and the new vertex. + * + * @param mesh Mesh containing the face to poke. + * @param index Index of the face to poke. + * @return Pointer to the newly-created vertex in the center of the face, or `nullptr` if the face could not be poked. + */ +mesh::vertex* poke_face(mesh& mesh, std::size_t index); + +} // namespace geom + +#endif // ANTKEEPER_GEOM_MESH_FUNCTIONS_HPP + diff --git a/src/engine/geom/mesh.cpp b/src/engine/geom/mesh.cpp new file mode 100644 index 0000000..e20a5c9 --- /dev/null +++ b/src/engine/geom/mesh.cpp @@ -0,0 +1,436 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include + +namespace geom { + +mesh::mesh(const mesh& other) +{ + *this = other; +} + +mesh::~mesh() +{ + clear(); +} + +mesh& mesh::operator=(const mesh& other) +{ + // Clear the mesh + clear(); + + // Resize vertices, edges, and faces + vertices.resize(other.vertices.size()); + edges.resize(other.edges.size()); + faces.resize(other.faces.size()); + + // Allocate vertices + for (std::size_t i = 0; i < vertices.size(); ++i) + vertices[i] = new vertex(); + + // Allocate edges + for (std::size_t i = 0; i < edges.size(); ++i) + { + edges[i] = new edge(); + edges[i]->symmetric = new edge(); + edges[i]->symmetric->symmetric = edges[i]; + } + + // Allocate faces + for (std::size_t i = 0; i < faces.size(); ++i) + faces[i] = new face(); + + // Copy vertices + for (std::size_t i = 0; i < vertices.size(); ++i) + { + vertex* va = vertices[i]; + const vertex* vb = other.vertices[i]; + + va->index = vb->index; + va->position = vb->position; + va->edge = nullptr; + + if (vb->edge) + { + va->edge = edges[vb->edge->index]; + if (vb->edge != other.edges[vb->edge->index]) + va->edge = va->edge->symmetric; + } + } + + // Copy edges + for (std::size_t i = 0; i < edges.size(); ++i) + { + edge* ea = edges[i]; + const edge* eb = other.edges[i]; + + for (std::size_t j = 0; j < 2; ++j) + { + ea->index = eb->index; + ea->vertex = vertices[eb->vertex->index]; + + ea->face = nullptr; + if (eb->face) + ea->face = faces[eb->face->index]; + + ea->previous = edges[eb->previous->index]; + if (eb->previous != other.edges[eb->previous->index]) + ea->previous = ea->previous->symmetric; + + ea->next = edges[eb->next->index]; + if (eb->next != other.edges[eb->next->index]) + ea->next = ea->next->symmetric; + + ea = ea->symmetric; + eb = eb->symmetric; + } + } + + // Copy faces + for (std::size_t i = 0; i < faces.size(); ++i) + { + face* fa = faces[i]; + const face* fb = other.faces[i]; + + fa->index = fb->index; + + fa->edge = edges[fb->edge->index]; + if (fb->edge != other.edges[fb->edge->index]) + fa->edge = fa->edge->symmetric; + } + + return *this; +} + +void mesh::clear() noexcept +{ + // Deallocate vertices + for (mesh::vertex* vertex: vertices) + delete vertex; + + // Deallocate edges + for (mesh::edge* edge: edges) + { + delete edge->symmetric; + delete edge; + } + + // Deallocate faces + for (mesh::face* face: faces) + delete face; + + vertices.clear(); + edges.clear(); + faces.clear(); +} + +mesh::vertex* mesh::add_vertex(const float3& position) +{ + mesh::vertex* vertex = new mesh::vertex + { + vertices.size(), + nullptr, + position + }; + + vertices.push_back(vertex); + + return vertex; +} + +mesh::edge* mesh::add_edge(mesh::vertex* a, mesh::vertex* b) +{ + mesh::edge* ab = new mesh::edge(); + mesh::edge* ba = new mesh::edge(); + + ab->index = edges.size(); + ab->vertex = a; + ab->face = nullptr; + ab->previous = ba; + ab->next = ba; + ab->symmetric = ba; + + ba->index = edges.size(); + ba->vertex = b; + ba->face = nullptr; + ba->previous = ab; + ba->next = ab; + ba->symmetric = ab; + + if (!a->edge) + { + a->edge = ab; + } + else + { + mesh::edge* a_in = find_free_incident(a); + mesh::edge* a_out = a_in->next; + a_in->next = ab; + ab->previous = a_in; + ba->next = a_out; + a_out->previous = ba; + } + + if (!b->edge) + { + b->edge = ba; + } + else + { + mesh::edge* b_in = find_free_incident(b); + mesh::edge* b_out = b_in->next; + b_in->next = ba; + ba->previous = b_in; + ab->next = b_out; + b_out->previous = ab; + } + + // Add edge + edges.push_back(ab); + + return ab; +} + +mesh::face* mesh::add_face(const loop& loop) +{ + if (loop.empty()) + { + throw std::runtime_error("Empty edge loop"); + } + + // Validate edge loop + for (std::size_t i = 0; i < loop.size(); ++i) + { + mesh::edge* current = loop[i]; + mesh::edge* next = loop[(i + 1) % loop.size()]; + + if (current->symmetric->vertex != next->vertex) + { + // Disconnected edge loop + throw std::runtime_error("Disconnected edge loop"); + } + + if (current->face) + { + // This edge already has a face + throw std::runtime_error("Non-manifold mesh 1"); + } + } + + // Make edges adjacent + for (std::size_t i = 0; i < loop.size(); ++i) + { + if (!make_adjacent(loop[i], loop[(i + 1) % loop.size()])) + { + throw std::runtime_error("Non-manifold mesh 2"); + } + } + + // Create face + mesh::face* face = new mesh::face(); + face->edge = loop[0]; + face->index = faces.size(); + + // Add face + faces.push_back(face); + + // Connect edges to the face + for (mesh::edge* edge: loop) + { + edge->face = face; + } + + return face; +} + +void mesh::remove_face(mesh::face* face) +{ + // Nullify pointers to this face + mesh::edge* edge = face->edge; + do + { + edge->face = nullptr; + edge = edge->next; + } + while (edge != face->edge); + + // Adjust indices of faces after this face + for (std::size_t i = face->index + 1; i < faces.size(); ++i) + { + --faces[i]->index; + } + + // Remove face from the faces vector + faces.erase(faces.begin() + face->index); + + // Deallocate face + delete face; +} + +void mesh::remove_edge(mesh::edge* edge) +{ + mesh::edge* ab = edge; + mesh::edge* ba = edge->symmetric; + mesh::vertex* a = ab->vertex; + mesh::edge* a_in = ab->previous; + mesh::edge* a_out = ba->next; + mesh::vertex* b = ba->vertex; + mesh::edge* b_in = ba->previous; + mesh::edge* b_out = ab->next; + + // Remove dependent faces + if (ab->face) + remove_face(ab->face); + if (ba->face) + remove_face(ba->face); + + // Re-link edges + if (a->edge == ab) + a->edge = (a_out == ab) ? nullptr : a_out; + if (b->edge == ba) + b->edge = (b_out == ba) ? nullptr : b_out; + a_in->next = a_out; + a_out->previous = a_in; + b_in->next = b_out; + b_out->previous = b_in; + + // Adjust indices of edges after this edge + for (std::size_t i = edge->index + 1; i < edges.size(); ++i) + { + --edges[i]->index; + --edges[i]->symmetric->index; + } + + // Remove edge from the edges vector + edges.erase(edges.begin() + edge->index); + + // Deallocate edge + delete edge->symmetric; + delete edge; +} + +void mesh::remove_vertex(mesh::vertex* vertex) +{ + // Remove connected edges + if (vertex->edge) + { + mesh::edge* current = nullptr; + mesh::edge* next = vertex->edge; + + do + { + current = next; + + next = next->symmetric->next; + if (next == current) + { + next = next->symmetric->next; + } + + remove_edge(current); + } + while (current != next); + } + + // Adjust indices of vertices after this vertex + for (std::size_t i = vertex->index + 1; i < vertices.size(); ++i) + { + --vertices[i]->index; + } + + // Remove vertex from the vertices vector + vertices.erase(vertices.begin() + vertex->index); + + // Deallocate vertex + delete vertex; +} + +mesh::edge* mesh::find_free_incident(mesh::vertex* vertex) const +{ + mesh::edge* begin = vertex->edge->symmetric; + mesh::edge* current = begin; + + do + { + if (!current->face) + { + return current; + } + + current = current->next->symmetric; + } + while (current != begin); + + return nullptr; +} + +mesh::edge* mesh::find_free_incident(mesh::edge* start_edge, mesh::edge* end_edge) const +{ + if (start_edge == end_edge) + { + return nullptr; + } + + mesh::edge* current = start_edge; + do + { + if (!current->face) + { + return current; + } + + current = current->next->symmetric; + } + while (current != end_edge); + + return nullptr; +} + +bool mesh::make_adjacent(mesh::edge* in, mesh::edge* out) +{ + if (in->next == out) + { + return true; + } + + mesh::edge* b = in->next; + mesh::edge* d = out->previous; + mesh::edge* g = find_free_incident(out->symmetric, in); + if (!g) + { + return false; + } + + mesh::edge* h = g->next; + + in->next = out; + out->previous = in; + + g->next = b; + b->previous = g; + + d->next = h; + h->previous = d; + + return true; +} + +} // namespace geom diff --git a/src/engine/geom/mesh.hpp b/src/engine/geom/mesh.hpp new file mode 100644 index 0000000..7138cdd --- /dev/null +++ b/src/engine/geom/mesh.hpp @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GEOM_MESH_HPP +#define ANTKEEPER_GEOM_MESH_HPP + +#include +#include + +namespace geom { + +/** + * Half-edge mesh. + * + * @see http://kaba.hilvi.org/homepage/blog/halfedge/halfedge.htm + */ +class mesh +{ +public: + struct vertex; + struct edge; + struct face; + + /** + * Half-edge mesh vertex, containing its index, a pointer to its parent edge, and its position vector. + */ + struct vertex + { + /// Index of this vertex. + std::size_t index; + + /// Pointer to the edge to which this vertex belongs. + mesh::edge* edge; + + /// Vertex position. + float3 position; + }; + + /** + * Half-edge mesh edge, containing its index and pointers to its starting vertex, parent face, and related edges. + */ + struct edge + { + /// Index of this edge. + std::size_t index; + + /// Pointer to the vertex at which the edge starts. + mesh::vertex* vertex; + + /// Pointer to the face on the left of this edge. + mesh::face* face; + + /// Pointer to the previous edge in the parent face. + mesh::edge* previous; + + /// Pointer to the next edge in the parent face. + mesh::edge* next; + + /// Pointer to the symmetric edge. + mesh::edge* symmetric; + }; + + /** + * Half-edge mesh face, containing its index and a pointer to its first edge. + */ + struct face + { + /// Index of this face. + std::size_t index; + + /// Pointer to the first edge in this face. + mesh::edge* edge; + }; + + /** + * List of edges which form a face. + */ + typedef std::vector loop; + + /** + * Constructs a mesh. + */ + constexpr mesh() noexcept = default; + + /** + * Copy-constructs a mesh. + */ + mesh(const mesh& other); + + /** + * Destructs a mesh. + */ + ~mesh() noexcept; + + /** + * Copies another mesh into this mesh. + * + * @param other Mesh to copy. + * + * @return Reference to this mesh. + */ + mesh& operator=(const mesh& other); + + /** + * Removes all vertices, edges, and faces from the mesh. + */ + void clear() noexcept; + + /** + * Adds a vertex to the mesh. + * + * @param position Position of the vertex. + * + * @return Pointer to the added vertex. + * + * @warning The added vertex will initially have a null edge. + */ + mesh::vertex* add_vertex(const float3& position); + + /** + * Adds two half edges to the mesh. + * + * @param a Vertex from which the edge originates. + * @param b Vertex at which the edge ends. + * + * @return Pointer to the half edge `ab`. The symmetric half edge `ba` can be accessed through `ab->symmetric`. + */ + mesh::edge* add_edge(mesh::vertex* a, mesh::vertex* b); + + /** + * Adds a face to the mesh. + * + * @param loop List of edges which form the face. + * @return Pointer to the added face. + * + * @exception std::runtime_error Empty edge loop. + * @exception std::runtime_error Disconnected edge loop. + * @exception std::runtime_error Non-manifold mesh 1. + * @exception std::runtime_error Non-manifold mesh 2. + */ + mesh::face* add_face(const loop& loop); + + /** + * Removes a face from the mesh. + * + * @param face Face to be removed. This face will be deallocated after removal. + */ + void remove_face(mesh::face* face); + + /** + * Removes an edge and all dependent faces from the mesh. + * + * @param edge Edge to be removed. This edge will be deallocated after removal. + */ + void remove_edge(mesh::edge* edge); + + /** + * Removes a vertex, all dependent edges, and all dependent faces from the mesh. + * + * @param vertex Vertex to be removed. This vertex will be deallocated after removal. + */ + void remove_vertex(mesh::vertex* vertex); + + /// Returns the mesh vertices. + const std::vector& get_vertices() const; + + /// Returns the mesh edges. + const std::vector& get_edges() const; + + /// Returns the mesh faces. + const std::vector& get_faces() const; + +private: + mesh::edge* find_free_incident(mesh::vertex* vertex) const; + mesh::edge* find_free_incident(mesh::edge* start_edge, mesh::edge* end_edge) const; + bool make_adjacent(mesh::edge* in_edge, mesh::edge* out_edge); + + std::vector vertices; + std::vector edges; + std::vector faces; +}; + +inline const std::vector& mesh::get_vertices() const +{ + return vertices; +} + +inline const std::vector& mesh::get_edges() const +{ + return edges; +} + +inline const std::vector& mesh::get_faces() const +{ + return faces; +} + +} // namespace geom + +#endif // ANTKEEPER_GEOM_MESH_HPP diff --git a/src/engine/geom/meshes/grid.cpp b/src/engine/geom/meshes/grid.cpp new file mode 100644 index 0000000..425f3f5 --- /dev/null +++ b/src/engine/geom/meshes/grid.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include +#include + +namespace geom { +namespace meshes { + +geom::mesh* grid_xy(float length, std::size_t subdivisions_x, std::size_t subdivisions_y) +{ + // Allocate new mesh + geom::mesh* mesh = new geom::mesh(); + + // Determine vertex count and placement + std::size_t columns = subdivisions_x + 1; + std::size_t rows = subdivisions_y + 1; + float column_length = length / static_cast(columns); + float row_length = length / static_cast(rows); + + // Generate mesh vertices + float3 position; + position.z() = 0.0f; + position.y() = -length * 0.5f; + for (std::size_t i = 0; i <= rows; ++i) + { + position.x() = -length * 0.5f; + for (std::size_t j = 0; j <= columns; ++j) + { + mesh->add_vertex(position); + + position.x() += column_length; + } + + position.y() += row_length; + } + + // Function to eliminate duplicate edges + std::map, geom::mesh::edge*> edge_map; + auto add_or_find_edge = [&](geom::mesh::vertex* start, geom::mesh::vertex* end) -> geom::mesh::edge* + { + geom::mesh::edge* edge; + if (auto it = edge_map.find({start->index, end->index}); it != edge_map.end()) + { + edge = it->second; + } + else + { + edge = mesh->add_edge(start, end); + edge_map[{start->index, end->index}] = edge; + edge_map[{end->index, start->index}] = edge->symmetric; + } + + return edge; + }; + + // Connect vertices with edges and faces + const std::vector& vertices = mesh->get_vertices(); + for (std::size_t i = 0; i < rows; ++i) + { + for (std::size_t j = 0; j < columns; ++j) + { + geom::mesh::vertex* a = vertices[i * (columns + 1) + j]; + geom::mesh::vertex* b = vertices[(i + 1) * (columns + 1) + j]; + geom::mesh::vertex* c = vertices[i * (columns + 1) + j + 1]; + geom::mesh::vertex* d = vertices[(i + 1) * (columns + 1) + j + 1]; + + // a---c + // | | + // b---d + geom::mesh::edge* ab = add_or_find_edge(a, b); + geom::mesh::edge* bd = add_or_find_edge(b, d); + geom::mesh::edge* dc = add_or_find_edge(d, c); + geom::mesh::edge* ca = add_or_find_edge(c, a); + + mesh->add_face({ab, bd, dc, ca}); + } + } + + return mesh; +} + +} // namespace meshes +} // namespace geom diff --git a/src/engine/geom/meshes/grid.hpp b/src/engine/geom/meshes/grid.hpp new file mode 100644 index 0000000..7fba2a6 --- /dev/null +++ b/src/engine/geom/meshes/grid.hpp @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GEOM_MESHES_GRID_HPP +#define ANTKEEPER_GEOM_MESHES_GRID_HPP + +#include + +namespace geom { +namespace meshes { + +/** + * Generates a grid mesh on the XY plane. + * + * @param length Side length of the grid. + * @param subdivisions_x Number of subdivisions on the x-axis. + * @param subdivisions_y Number of subdivisions on the y-axis. + * @return Grid mesh on the XY plane. + */ +geom::mesh* grid_xy(float length, std::size_t subdivisions_x, std::size_t subdivisions_y); + +} // namespace meshes +} // namespace geom + +#endif // ANTKEEPER_GEOM_MESHES_GRID_HPP diff --git a/src/geom/morton.hpp b/src/engine/geom/morton.hpp similarity index 100% rename from src/geom/morton.hpp rename to src/engine/geom/morton.hpp diff --git a/src/engine/geom/octree.hpp b/src/engine/geom/octree.hpp new file mode 100644 index 0000000..26c0669 --- /dev/null +++ b/src/engine/geom/octree.hpp @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GEOM_OCTREE_HPP +#define ANTKEEPER_GEOM_OCTREE_HPP + +#include +#include + +namespace geom { + +/// An octree, or 3-dimensional hyperoctree. +template +using octree = hyperoctree; + +/// Octree with an 8-bit node type (2 depth levels). +template +using octree8 = octree; + +/// Octree with a 16-bit node type (4 depth levels). +template +using octree16 = octree; + +/// Octree with a 32-bit node type (9 depth levels). +template +using octree32 = octree; + +/// Octree with a 64-bit node type (19 depth levels). +template +using octree64 = octree; + +/// Octree with unordered node storage and traversal. +template +using unordered_octree = octree; + +/// Unordered octree with an 8-bit node type (2 depth levels). +typedef unordered_octree unordered_octree8; + +/// Unordered octree with a 16-bit node type (4 depth levels). +typedef unordered_octree unordered_octree16; + +/// Unordered octree with a 32-bit node type (9 depth levels). +typedef unordered_octree unordered_octree32; + +/// Unordered octree with a 64-bit node type (19 depth levels). +typedef unordered_octreeunordered_octree64; + +} // namespace geom + +#endif // ANTKEEPER_GEOM_OCTREE_HPP diff --git a/src/engine/geom/plane.hpp b/src/engine/geom/plane.hpp new file mode 100644 index 0000000..95558ff --- /dev/null +++ b/src/engine/geom/plane.hpp @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GEOM_PLANE_HPP +#define ANTKEEPER_GEOM_PLANE_HPP + +#include + +namespace geom { + +/** + * A flat 2-dimensional surface. + */ +template +struct plane +{ + typedef math::vector vector_type; + + /// Plane normal vector. + vector_type normal; + + /// Plane distance. + T distance; + + /** + * Creates a plane given a normal vector and distance. + */ + plane(const vector_type& normal, T distance); + + /** + * Creates a plane given a normal vector and offset vector. + */ + plane(const vector_type& normal, const vector_type& offset); + + /** + * Creates a plane given three points. + */ + plane(const vector_type& a, const vector_type& b, const vector_type& c); + + /** + * Creates a plane given its coefficients. + * + * @param coefficients Vector containing the plane coefficients, A, B, C and D, as x, y, z, and w, respectively. + */ + plane(const math::vector& coefficients); + + /// Creates an uninitialized plane. + plane() = default; + + /** + * Calculates the signed distance between a plane and a vector. + * + * @param v Vector. + * @return Signed distance between the plane and vector. + */ + T signed_distance(const vector_type& v) const; + + /** + * Calculates the point of intersection between three planes. + */ + static vector_type intersection(const plane& p0, const plane& p1, const plane& p2); +}; + +template +inline plane::plane(const vector_type& normal, T distance): + normal(normal), + distance(distance) +{} + +template +plane::plane(const vector_type& normal, const vector_type& offset): + normal(normal), + distance(-math::dot(normal, offset)) +{} + +template +plane::plane(const vector_type& a, const vector_type& b, const vector_type& c) +{ + normal = math::normalize(math::cross(c - b, a - b)); + distance = -(math::dot(normal, b)); +} + +template +plane::plane(const math::vector& coefficients) +{ + const vector_type abc = math::vector(coefficients); + const float inverse_length = T(1) / math::length(abc); + + normal = abc * inverse_length; + distance = coefficients[3] * inverse_length; +} + +template +inline T plane::signed_distance(const vector_type& v) const +{ + return distance + math::dot(normal, v); +} + +template +typename plane::vector_type plane::intersection(const plane& p0, const plane& p1, const plane& p2) +{ + return -(p0.distance * math::cross(p1.normal, p2.normal) + p1.distance * math::cross(p2.normal, p0.normal) + p2.distance * math::cross(p0.normal, p1.normal)) / math::dot(p0.normal, math::cross(p1.normal, p2.normal)); +} + +} // namespace geom + +#endif // ANTKEEPER_GEOM_PLANE_HPP diff --git a/src/engine/geom/primitive/box.hpp b/src/engine/geom/primitive/box.hpp new file mode 100644 index 0000000..02efa7e --- /dev/null +++ b/src/engine/geom/primitive/box.hpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GEOM_PRIMITIVE_BOX_HPP +#define ANTKEEPER_GEOM_PRIMITIVE_BOX_HPP + +#include + +namespace geom { +namespace primitive { + +/// 3-dimensional hyperrectangle. +template +using box = hyperrectangle; + +} // namespace primitive +} // namespace geom + +#endif // ANTKEEPER_GEOM_PRIMITIVE_BOX_HPP diff --git a/src/engine/geom/primitive/circle.hpp b/src/engine/geom/primitive/circle.hpp new file mode 100644 index 0000000..de4e317 --- /dev/null +++ b/src/engine/geom/primitive/circle.hpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GEOM_PRIMITIVE_CIRCLE_HPP +#define ANTKEEPER_GEOM_PRIMITIVE_CIRCLE_HPP + +#include + +namespace geom { +namespace primitive { + +/// 2-dimensional hypersphere. +template +using circle = hypersphere; + +} // namespace primitive +} // namespace geom + +#endif // ANTKEEPER_GEOM_PRIMITIVE_CIRCLE_HPP diff --git a/src/engine/geom/primitive/hyperplane.hpp b/src/engine/geom/primitive/hyperplane.hpp new file mode 100644 index 0000000..74ca232 --- /dev/null +++ b/src/engine/geom/primitive/hyperplane.hpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GEOM_PRIMITIVE_HYPERPLANE_HPP +#define ANTKEEPER_GEOM_PRIMITIVE_HYPERPLANE_HPP + +#include + +namespace geom { +namespace primitive { + +/** + * *n*-dimensional plane. + * + * @tparam T Real type. + * @tparam N Number of dimensions. + */ +template +struct hyperplane +{ + typedef math::vector vector_type; + + /// Hyperplane normal. + vector_type normal; + + /// Hyperplane constant. + T constant; + + /// Constructs a hyperplane. + constexpr hyperplane() noexcept = default; + + /** + * Constructs a hyperplane given a point and a normal. + * + * @param point Point. + * @param normal Normal. + */ + constexpr hyperplane(const vector_type& point, const vector_type& normal) noexcept: + normal(normal), + constant(-math::dot(normal, point)) + {} + + /** + * Calculates the signed distance from the hyperplane to a point. + * + * @param point Input point. + * + * @return Signed distance from the hyperplane to @p point. + */ + constexpr T distance(const vector_type& point) const noexcept + { + return math::dot(normal, point) + constant; + } +}; + +} // namespace primitive +} // namespace geom + +#endif // ANTKEEPER_GEOM_PRIMITIVE_HYPERPLANE_HPP diff --git a/src/engine/geom/primitive/hyperrectangle.hpp b/src/engine/geom/primitive/hyperrectangle.hpp new file mode 100644 index 0000000..04e17f9 --- /dev/null +++ b/src/engine/geom/primitive/hyperrectangle.hpp @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GEOM_PRIMITIVE_HYPERRECTANGLE_HPP +#define ANTKEEPER_GEOM_PRIMITIVE_HYPERRECTANGLE_HPP + +#include +#include + +namespace geom { +namespace primitive { + +/** + * *n*-dimensional axis-aligned rectangle. + * + * @tparam T Real type. + * @tparam N Number of dimensions. + */ +template +struct hyperrectangle +{ + typedef math::vector vector_type; + + /// Minimum extent of the hyperrectangle. + vector_type min; + + /// Maximum extent of the hyperrectangle. + vector_type max; + + /** + * Tests whether a point is contained within this hyperrectangle. + * + * @param point Point to test for containment. + * + * @return `true` if the point is contained within this hyperrectangle, `false` otherwise. + */ + constexpr bool contains(const vector_type& point) const noexcept + { + for (std::size_t i = 0; i < N; ++i) + if (point[i] < min[i] || point[i] > max[i]) + return false; + return true; + } + + /** + * Tests whether another hyperrectangle is contained within this hyperrectangle. + * + * @param other Hyperrectangle to test for containment. + * + * @return `true` if the hyperrectangle is contained within this hyperrectangle, `false` otherwise. + */ + constexpr bool contains(const hyperrectangle& other) const noexcept + { + for (std::size_t i = 0; i < N; ++i) + if (other.min[i] < min[i] || other.max[i] > max[i]) + return false; + return true; + } + + /// Returns the center position of the hyperrectangle. + constexpr vector_type center() const noexcept + { + return (min + max) / T{2}; + } + + /** + * Calculates the signed distance from the hyperrectangle to a point. + * + * @param point Input point. + * + * @return Signed distance from the hyperrectangle to @p point. + */ + T distance(const vector_type& point) const noexcept + { + vector_type d = math::abs(point - center()) - size() * T{0.5}; + return math::length(math::max(vector_type::zero(), d)) + std::min(T{0}, math::max(d)); + } + + /** + * Extends the hyperrectangle to include a point. + * + * @param point Point to include in the hyperrectangle. + */ + void extend(const vector_type& point) noexcept + { + min = math::min(min, point); + max = math::max(max, point); + } + + /** + * Extends the hyperrectangle to include another hyperrectangle. + * + * @param other Hyperrectangle to include in this hyperrectangle. + */ + void extend(const hyperrectangle& other) noexcept + { + min = math::min(min, other.min); + max = math::max(max, other.max); + } + + /** + * Tests whether another hyperrectangle intersects this hyperrectangle. + * + * @param other Hyperrectangle to test for intersection. + * + * @return `true` if the hyperrectangle intersects this hyperrectangle, `false` otherwise. + */ + constexpr bool intersects(const hyperrectangle& other) const noexcept + { + for (std::size_t i = 0; i < N; ++i) + if (other.min[i] > max[i] || other.max[i] < min[i]) + return false; + return true; + } + + /// Returns the size of the hyperrectangle. + constexpr vector_type size() const noexcept + { + return max - min; + } + + /** + * Returns `false` if any coordinates of `min` are greater than `max`. + */ + constexpr bool valid() const noexcept + { + for (std::size_t i = 0; i < N; ++i) + if (min[i] > max[i]) + return false; + return true; + } + + /// Calculates the volume of the hyperrectangle. + constexpr T volume() const noexcept + { + T v = max[0] - min[0]; + for (std::size_t i = 1; i < N; ++i) + v *= max[i] - min[i]; + return v; + } +}; + +} // namespace primitive +} // namespace geom + +#endif // ANTKEEPER_GEOM_PRIMITIVE_HYPERRECTANGLE_HPP diff --git a/src/engine/geom/primitive/hypersphere.hpp b/src/engine/geom/primitive/hypersphere.hpp new file mode 100644 index 0000000..eb0bb64 --- /dev/null +++ b/src/engine/geom/primitive/hypersphere.hpp @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GEOM_PRIMITIVE_HYPERSPHERE_HPP +#define ANTKEEPER_GEOM_PRIMITIVE_HYPERSPHERE_HPP + +#include +#include + +namespace geom { +namespace primitive { + +/** + * *n*-dimensional sphere. + * + * @tparam T Real type. + * @tparam N Number of dimensions. + */ +template +struct hypersphere +{ + typedef math::vector vector_type; + + /// Hypersphere center. + vector_type center; + + /// Hypersphere radius. + T radius; + + /** + * Tests whether a point is contained within this hypersphere. + * + * @param point Point to test for containment. + * + * @return `true` if the point is contained within this hypersphere, `false` otherwise. + */ + constexpr bool contains(const vector_type& point) const noexcept + { + return math::sqr_distance(center, point) <= radius * radius; + } + + /** + * Tests whether another hypersphere is contained within this hypersphere. + * + * @param other Hypersphere to test for containment. + * + * @return `true` if the hypersphere is contained within this hypersphere, `false` otherwise. + */ + constexpr bool contains(const hypersphere& other) const noexcept + { + const T containment_radius = radius - other.radius; + if (containment_radius < T{0}) + return false; + + return math::sqr_distance(center, other.center) <= containment_radius * containment_radius; + } + + /** + * Calculates the signed distance from the hypersphere to a point. + * + * @param point Input point. + * + * @return Signed distance from the hypersphere to @p point. + */ + T distance(const vector_type& point) const noexcept + { + return math::distance(center, point) - radius; + } + + /** + * Tests whether another hypersphere intersects this hypersphere. + * + * @param other Hypersphere to test for intersection. + * + * @return `true` if the hypersphere intersects this hypersphere, `false` otherwise. + */ + constexpr bool intersects(const hypersphere& other) const noexcept + { + const T intersection_radius = radius + other.radius; + return math::sqr_distance(center, other.center) <= intersection_radius * intersection_radius; + } + + /** + * Volume calculation helper function. + * + * @tparam M Dimension. + * + * @param r Radius. + * + * @return Volume. + */ + /// @private + /// @{ + template + static constexpr T volume(T r) noexcept + { + return (math::two_pi / static_cast(M)) * r * r * volume(r); + } + + template <> + static constexpr T volume<1>(T r) noexcept + { + return r * T{2}; + } + + template <> + static constexpr T volume<0>(T r) noexcept + { + return T{1}; + } + /// @} + + /// Calculates the volume of the hypersphere. + inline constexpr T volume() const noexcept + { + return volume(radius); + } +}; + +} // namespace primitive +} // namespace geom + +#endif // ANTKEEPER_GEOM_PRIMITIVE_HYPERSPHERE_HPP diff --git a/src/engine/geom/primitive/intersection.hpp b/src/engine/geom/primitive/intersection.hpp new file mode 100644 index 0000000..73789b3 --- /dev/null +++ b/src/engine/geom/primitive/intersection.hpp @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GEOM_PRIMITIVE_INTERSECTION_HPP +#define ANTKEEPER_GEOM_PRIMITIVE_INTERSECTION_HPP + +#include +#include +#include +#include +#include +#include + +namespace geom { +namespace primitive { + +/** + * Ray-hyperplane intersection test. + * + * @param ray Ray. + * @param hyperplane Hyperplane. + * + * @return Distance along the ray to the point of intersection, or `std::nullopt` if no intersection occurred. + */ +/// @{ +template +constexpr std::optional intersection(const ray& ray, const hyperplane& hyperplane) noexcept +{ + const T cos_theta = math::dot(ray.direction, hyperplane.normal); + if (cos_theta != T{0}) + { + const T t = -hyperplane.distance(ray.origin) / cos_theta; + if (t >= T{0}) + return t; + } + + return std::nullopt; +} + +template +inline constexpr std::optional intersection(const hyperplane& hyperplane, const ray& ray) noexcept +{ + return intersection(ray, hyperplane); +} +/// @} + +/** + * Ray-hyperrectangle intersection test. + * + * @param ray Ray. + * @param hyperrectangle Hyperrectangle. + * + * @return Tuple containing the distances along the ray to the first and second points of intersection, or `std::nullopt` if no intersection occurred. + */ +/// @{ +template +constexpr std::optional> intersection(const ray& ray, const hyperrectangle& hyperrectangle) noexcept +{ + T t0 = -std::numeric_limits::infinity(); + T t1 = std::numeric_limits::infinity(); + + for (std::size_t i = 0; i < N; ++i) + { + if (!ray.direction[i]) + { + if (ray.origin[i] < hyperrectangle.min[i] || ray.origin[i] > hyperrectangle.max[i]) + return std::nullopt; + } + else + { + T min = (hyperrectangle.min[i] - ray.origin[i]) / ray.direction[i]; + T max = (hyperrectangle.max[i] - ray.origin[i]) / ray.direction[i]; + t0 = std::max(t0, std::min(min, max)); + t1 = std::min(t1, std::max(min, max)); + } + } + + if (t0 > t1 || t1 < T{0}) + return std::nullopt; + + return {t0, t1}; +} + +template +inline constexpr std::optional> intersection(const hyperrectangle& hyperrectangle, const ray& ray) noexcept +{ + return intersection(ray, hyperrectangle); +} +/// @} + +/** + * Ray-hypersphere intersection test. + * + * @param ray Ray. + * @param hypersphere Hypersphere. + * + * @return Tuple containing the distances along the ray to the first and second points of intersection, or `std::nullopt` if no intersection occurred. + */ +template +std::optional> intersection(const ray& ray, const hypersphere& hypersphere) noexcept +{ + const math::vector displacement = ray.origin - hypersphere.center; + const T b = math::dot(displacement, ray.direction); + const T c = math::sqr_length(displacement) - hypersphere.radius * hypersphere.radius; + T h = b * b - c; + + if (h < T{0}) + return std::nullopt; + + h = std::sqrt(h); + + T t0 = -b - h; + T t1 = -b + h; + if (t0 > t1) + std::swap(t0, t1); + + if (t0 < T{0}) + return std::nullopt; + + return std::tuple{t0, t1}; +} + +/** + * Hyperrectangle-hyperrectangle intersection test. + * + * @param a First hyperrectangle. + * @param b Second hyperrectangle. + * + * @return `true` if an intersection occurred, `false` otherwise. + */ +template +inline constexpr bool intersection(const hyperrectangle& a, const hyperrectangle& b) noexcept +{ + return a.intersects(b); +} + +/** + * Hyperrectangle-hypersphere intersection test. + * + * @param hyperrectangle Hyperrectangle. + * @param hypersphere Hypersphere. + * + * @return `true` if an intersection occurred, `false` otherwise. + */ +/// @{ +template +constexpr bool intersection(const hyperrectangle& hyperrectangle, const hypersphere& hypersphere) noexcept +{ + T sqr_distance{0}; + for (std::size_t i = 0; i < N; ++i) + { + if (hypersphere.center[i] < hyperrectangle.min[i]) + { + const T difference = hyperrectangle.min[i] - hypersphere.center[i]; + sqr_distance += difference * difference; + } + else if (hypersphere.center[i] > hyperrectangle.max[i]) + { + const T difference = hypersphere.center[i] - hyperrectangle.max[i]; + sqr_distance += difference * difference; + } + } + + return sqr_distance <= hypersphere.radius * hypersphere.radius; +} + +template +inline constexpr bool intersection(const hypersphere& hypersphere, const hyperrectangle& hyperrectangle) noexcept +{ + return intersection(hyperrectangle, hypersphere); +} +/// @} + +/** + * Hypersphere-hypersphere intersection test. + * + * @param a First hypersphere. + * @param b Second hypersphere. + * + * @return `true` if an intersection occurred, `false` otherwise. + */ +template +inline constexpr bool intersection(const hypersphere& a, const hypersphere& b) noexcept +{ + return a.intersects(b); +} + +} // namespace primitive +} // namespace geom + +#endif // ANTKEEPER_GEOM_PRIMITIVE_INTERSECTION_HPP diff --git a/src/engine/geom/primitive/line-segment.hpp b/src/engine/geom/primitive/line-segment.hpp new file mode 100644 index 0000000..99e1c20 --- /dev/null +++ b/src/engine/geom/primitive/line-segment.hpp @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GEOM_PRIMITIVE_LINE_SEGMENT_HPP +#define ANTKEEPER_GEOM_PRIMITIVE_LINE_SEGMENT_HPP + +#include + +namespace geom { +namespace primitive { + +/** + * *n*-dimensional line segment. + * + * @tparam T Real type. + * @tparam N Number of dimensions. + */ +template +struct line_segment +{ + typedef math::vector vector_type; + + /// First endpoint. + vector_type a; + + /// Second endpoint. + vector_type b; + + /** + * Calculates the length of the line segment. + * + * @return Length of the line segment. + */ + T length() const noexcept + { + return math::distance(a, b); + } +}; + +} // namespace primitive +} // namespace geom + +#endif // ANTKEEPER_GEOM_PRIMITIVE_LINE_SEGMENT_HPP diff --git a/src/engine/geom/primitive/line.hpp b/src/engine/geom/primitive/line.hpp new file mode 100644 index 0000000..e35e091 --- /dev/null +++ b/src/engine/geom/primitive/line.hpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GEOM_PRIMITIVE_LINE_HPP +#define ANTKEEPER_GEOM_PRIMITIVE_LINE_HPP + +#include + +namespace geom { +namespace primitive { + +/// 2-dimensional hyperplane. +template +using line = hyperplane; + +} // namespace primitive +} // namespace geom + +#endif // ANTKEEPER_GEOM_PRIMITIVE_LINE_HPP diff --git a/src/engine/geom/primitive/plane.hpp b/src/engine/geom/primitive/plane.hpp new file mode 100644 index 0000000..5ceea4f --- /dev/null +++ b/src/engine/geom/primitive/plane.hpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GEOM_PRIMITIVE_PLANE_HPP +#define ANTKEEPER_GEOM_PRIMITIVE_PLANE_HPP + +#include + +namespace geom { +namespace primitive { + +/// 3-dimensional hyperplane. +template +using plane = hyperplane; + +} // namespace primitive +} // namespace geom + +#endif // ANTKEEPER_GEOM_PRIMITIVE_PLANE_HPP diff --git a/src/engine/geom/primitive/ray.hpp b/src/engine/geom/primitive/ray.hpp new file mode 100644 index 0000000..c1b46a6 --- /dev/null +++ b/src/engine/geom/primitive/ray.hpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GEOM_PRIMITIVE_RAY_HPP +#define ANTKEEPER_GEOM_PRIMITIVE_RAY_HPP + +#include + +namespace geom { +namespace primitive { + +/** + * Half of a line proceeding from an initial point. + * + * @tparam T Real type. + * @tparam N Number of dimensions. + */ +template +struct ray +{ + typedef math::vector vector_type; + + /// Ray origin position. + vector_type origin; + + /// Ray direction vector. + vector_type direction; + + /** + * Extrapolates from the ray origin along the ray direction vector. + * + * @param distance Signed extrapolation distance. + * + * @return Extrapolated coordinates. + */ + constexpr vector_type extrapolate(T distance) const noexcept + { + return origin + direction * distance; + } +}; + +} // namespace primitive +} // namespace geom + +#endif // ANTKEEPER_GEOM_PRIMITIVE_RAY_HPP diff --git a/src/engine/geom/primitive/rectangle.hpp b/src/engine/geom/primitive/rectangle.hpp new file mode 100644 index 0000000..b42c18b --- /dev/null +++ b/src/engine/geom/primitive/rectangle.hpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GEOM_PRIMITIVE_RECTANGLE_HPP +#define ANTKEEPER_GEOM_PRIMITIVE_RECTANGLE_HPP + +#include + +namespace geom { +namespace primitive { + +/// 2-dimensional hyperrectangle. +template +using rectangle = hyperrectangle; + +} // namespace primitive +} // namespace geom + +#endif // ANTKEEPER_GEOM_PRIMITIVE_RECTANGLE_HPP diff --git a/src/engine/geom/primitive/sphere.hpp b/src/engine/geom/primitive/sphere.hpp new file mode 100644 index 0000000..3cdc956 --- /dev/null +++ b/src/engine/geom/primitive/sphere.hpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GEOM_PRIMITIVE_SPHERE_HPP +#define ANTKEEPER_GEOM_PRIMITIVE_SPHERE_HPP + +#include + +namespace geom { +namespace primitive { + +/// 3-dimensional hypersphere. +template +using sphere = hypersphere; + +} // namespace primitive +} // namespace geom + +#endif // ANTKEEPER_GEOM_PRIMITIVE_SPHERE_HPP diff --git a/src/engine/geom/projection.hpp b/src/engine/geom/projection.hpp new file mode 100644 index 0000000..d49c476 --- /dev/null +++ b/src/engine/geom/projection.hpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GEOM_PROJECTION_HPP +#define ANTKEEPER_GEOM_PROJECTION_HPP + +#include + +namespace geom { + +template +math::vector project_on_plane(const math::vector& v, const math::vector& p, const math::vector& n) +{ + return v - n * math::dot(v - p, n); +} + +} // namespace geom + +#endif // ANTKEEPER_GEOM_PROJECTION_HPP diff --git a/src/engine/geom/quadtree.hpp b/src/engine/geom/quadtree.hpp new file mode 100644 index 0000000..6240147 --- /dev/null +++ b/src/engine/geom/quadtree.hpp @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GEOM_QUADTREE_HPP +#define ANTKEEPER_GEOM_QUADTREE_HPP + +#include +#include + +namespace geom { + +/// A quadtree, or 2-dimensional hyperoctree. +template +using quadtree = hyperoctree; + +/// Quadtree with an 8-bit node type (2 depth levels). +template +using quadtree8 = quadtree; + +/// Quadtree with a 16-bit node type (6 depth levels). +template +using quadtree16 = quadtree; + +/// Quadtree with a 32-bit node type (13 depth levels). +template +using quadtree32 = quadtree; + +/// Quadtree with a 64-bit node type (29 depth levels). +template +using quadtree64 = quadtree; + +/// Quadtree with unordered node storage and traversal. +template +using unordered_quadtree = quadtree; + +/// Unordered quadtree with an 8-bit node type (2 depth levels). +typedef unordered_quadtree unordered_quadtree8; + +/// Unordered quadtree with a 16-bit node type (6 depth levels). +typedef unordered_quadtree unordered_quadtree16; + +/// Unordered quadtree with a 32-bit node type (13 depth levels). +typedef unordered_quadtree unordered_quadtree32; + +/// Unordered quadtree with a 64-bit node type (29 depth levels). +typedef unordered_quadtreeunordered_quadtree64; + +} // namespace geom + +#endif // ANTKEEPER_GEOM_QUADTREE_HPP diff --git a/src/engine/geom/ray.hpp b/src/engine/geom/ray.hpp new file mode 100644 index 0000000..026c6b2 --- /dev/null +++ b/src/engine/geom/ray.hpp @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GEOM_RAY_HPP +#define ANTKEEPER_GEOM_RAY_HPP + +#include + +namespace geom { + +/** + * Half of a line proceeding from an initial point. + */ +template +struct ray +{ + typedef math::vector vector_type; + + /// Origin of the ray. + vector_type origin; + + /// Normalized direction vector of the ray. + vector_type direction; + + /** + * Extrapolates from the ray origin along the ray direction vector. + * + * @param distance Distance to extrapolate. + * @return Extrapolated coordinates. + */ + vector_type extrapolate(T distance) const; +}; + +template +inline typename ray::vector_type ray::extrapolate(T distance) const +{ + return origin + direction * distance; +} + +} // namespace geom + +#endif // ANTKEEPER_GEOM_RAY_HPP + diff --git a/src/engine/geom/rect-pack.hpp b/src/engine/geom/rect-pack.hpp new file mode 100644 index 0000000..0fb59cd --- /dev/null +++ b/src/engine/geom/rect-pack.hpp @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GEOM_RECT_PACK_HPP +#define ANTKEEPER_GEOM_RECT_PACK_HPP + +#include + +namespace geom { + +/** + * Node used in 2D rectangle packing. + * + * @see geom::rect_pack + */ +template +struct rect_pack_node +{ + /// Scalar type. + typedef T scalar_type; + + /// Rect type. + typedef rect rect_type; + + /// Creates a rect pack node. + rect_pack_node(); + + /// Destroys a rect pack node and its children. + ~rect_pack_node(); + + /// Pointers to the two children of the node, if any. + rect_pack_node* children[2]; + + /// Bounds of the node. + rect_type bounds; + + /// `true` if the node is occupied, `false` otherwise. + bool occupied; +}; + +template +rect_pack_node::rect_pack_node(): + bounds{T(0), T(0), T(0), T(0)}, + occupied(false) +{ + children[0] = nullptr; + children[1] = nullptr; +} + +template +rect_pack_node::~rect_pack_node() +{ + delete children[0]; + delete children[1]; +} + +/** + * Packs 2D rectangles. + * + * @see geom::rect_pack_node + * + * @see http://www.blackpawn.com/texts/lightmaps/ + */ +template +class rect_pack +{ +public: + /// Scalar type. + typedef T scalar_type; + + /// Node type. + typedef rect_pack_node node_type; + + /** + * Creates a rect pack and sets the bounds of the root node. + * + * @param w Width of the root node. + * @param h Height of the root node. + */ + rect_pack(scalar_type w, scalar_type h); + + /** + * Creates an empty rect pack. + */ + rect_pack(); + + /** + * Clears the pack and resizes the root node bounds. + * + * @param w New width of the root node. + * @param h New height of the root node. + * + * @see rect_pack::clear() + */ + void resize(scalar_type w, scalar_type h); + + /// Clear the pack, deallocating all nodes. + void clear(); + + /** + * Packs a rect into the rect pack. + * + * @param w Width of the rect. + * @param h Height of the rect. + * @return Pointer to the node in which the rect was packed, or `nullptr` if the rect could not be packed. + */ + const node_type* pack(scalar_type w, scalar_type h); + + /// Returns a reference to the root node. + const node_type& get_root() const; + +private: + static node_type* insert(node_type& parent, scalar_type w, scalar_type h); + + static void free(); + + node_type root; +}; + +template +rect_pack::rect_pack(scalar_type w, scalar_type h) +{ + root.bounds = {T(0), T(0), w, h}; +} + +template +rect_pack::rect_pack(): + rect_pack(0, 0) +{} + +template +void rect_pack::resize(scalar_type w, scalar_type h) +{ + clear(); + root.bounds = {T(0), T(0), w, h}; +} + +template +void rect_pack::clear() +{ + delete root.children[0]; + delete root.children[1]; + root.children[0] = nullptr; + root.children[1] = nullptr; + root.occupied = false; +} + +template +const typename rect_pack::node_type* rect_pack::pack(scalar_type w, scalar_type h) +{ + return insert(root, w, h); +} + +template +inline const typename rect_pack::node_type& rect_pack::get_root() const +{ + return root; +} + +template +typename rect_pack::node_type* rect_pack::insert(node_type& node, scalar_type w, scalar_type h) +{ + // If not a leaf node + if (node.children[0] && node.children[1]) + { + // Attempt to insert into first child + node_type* result = insert(*node.children[0], w, h); + if (result) + return result; + + // Cannot fit in first child, attempt to insert into second child + return insert(*node.children[1], w, h); + + } + + // Abort if node occupied + if (node.occupied) + return nullptr; + + // Determine node dimensions + scalar_type node_w = node.bounds.max.x() - node.bounds.min.x(); + scalar_type node_h = node.bounds.max.y() - node.bounds.min.y(); + + // Check if rect is larger than node + if (w > node_w || h > node_h) + return nullptr; + + // Check for a perfect fit + if (w == node_w && h == node_h) + { + node.occupied = true; + return &node; + } + + // Split the node + node.children[0] = new node_type(); + node.children[1] = new node_type(); + + // Determine split direction + scalar_type dw = node_w - w; + scalar_type dh = node_h - h; + if (dw > dh) + { + node.children[0]->bounds.min = node.bounds.min; + node.children[0]->bounds.max = {node.bounds.min.x() + w, node.bounds.max.y()}; + node.children[1]->bounds.min = {node.bounds.min.x() + w, node.bounds.min.y()}; + node.children[1]->bounds.max = {node.bounds.max}; + } + else + { + node.children[0]->bounds.min = node.bounds.min; + node.children[0]->bounds.max = {node.bounds.max.x(), node.bounds.min.y() + h}; + node.children[1]->bounds.min = {node.bounds.min.x(), node.bounds.min.y() + h}; + node.children[1]->bounds.max = {node.bounds.max}; + } + + // Insert into first child + return insert(*node.children[0], w, h); +} + +} // namespace geom + +#endif // ANTKEEPER_GEOM_RECT_PACK_HPP diff --git a/src/engine/geom/rect.hpp b/src/engine/geom/rect.hpp new file mode 100644 index 0000000..57e1438 --- /dev/null +++ b/src/engine/geom/rect.hpp @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GEOM_RECT_HPP +#define ANTKEEPER_GEOM_RECT_HPP + +#include + +namespace geom { + +/** + * 2D rectangle. + */ +template +struct rect +{ + typedef math::vector vector_type; + + vector_type min; + vector_type max; +}; + +} // namespace geom + +#endif // ANTKEEPER_GEOM_RECT_HPP diff --git a/src/engine/geom/sdf.hpp b/src/engine/geom/sdf.hpp new file mode 100644 index 0000000..525b806 --- /dev/null +++ b/src/engine/geom/sdf.hpp @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GEOM_SDF_HPP +#define ANTKEEPER_GEOM_SDF_HPP + +#include +#include + +namespace geom { + +/// Signed distance functions. +namespace sdf { + +inline float3 translate(const float3& sample, const float3& offset) +{ + return sample - offset; +} + +inline float sphere(const float3& p, float r) +{ + return math::length(p) - r; +} + +inline float cylinder(const float3& p, float r, float h) +{ + float dx = std::abs(math::length(math::swizzle<0, 2>(p))) - r; + float dy = std::abs(p[1]) - h; + return std::min(std::max(dx, dy), 0.0f) + math::length(float2{std::max(dx, 0.0f), std::max(dy, 0.0f)}); +} + +inline float op_union(float a, float b) +{ + return std::min(a, b); +} + +inline float op_difference(float a, float b) +{ + return std::max(-a, b); +} + +inline float op_intersection(float a, float b) +{ + return std::max(a, b); +} + +inline float op_round(float d, float r) +{ + return d - r; +} + +} // namespace sdf +} // namespace geom + +#endif // ANTKEEPER_GEOM_SDF_HPP + diff --git a/src/engine/geom/solid-angle.hpp b/src/engine/geom/solid-angle.hpp new file mode 100644 index 0000000..4269ab9 --- /dev/null +++ b/src/engine/geom/solid-angle.hpp @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GEOM_SOLID_ANGLE_HPP +#define ANTKEEPER_GEOM_SOLID_ANGLE_HPP + +#include +#include + +namespace geom { + +/// Solid angle functions. +namespace solid_angle { + +/** + * Calculates the solid angle of a cone. + * + * @param theta Apex angle of the cone, in radians. + * + * @return Solid angle of the cone, in steradians. + */ +template +T cone(T theta) +{ + return math::two_pi * (T(1) - std::cos(theta)); +} + +} // namespace solid_angle +} // namespace geom + +#endif // ANTKEEPER_GEOM_SOLID_ANGLE_HPP diff --git a/src/engine/geom/sphere.hpp b/src/engine/geom/sphere.hpp new file mode 100644 index 0000000..51f5982 --- /dev/null +++ b/src/engine/geom/sphere.hpp @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GEOM_SPHERE_HPP +#define ANTKEEPER_GEOM_SPHERE_HPP + +#include +#include +#include +#include + +namespace geom { + +/** + * Bounding sphere. + */ +template +struct sphere: public bounding_volume +{ + typedef math::vector vector_type; + + vector_type center; + T radius; + + sphere(const vector_type& center, T radius); + sphere(); + + virtual bounding_volume_type get_bounding_volume_type() const; + virtual bool intersects(const sphere& sphere) const; + virtual bool intersects(const aabb& aabb) const; + virtual bool contains(const sphere& sphere) const; + virtual bool contains(const aabb& aabb) const; + virtual bool contains(const vector_type& point) const; +}; + +template +sphere::sphere(const vector_type& center, T radius): + center(center), + radius(radius) +{} + +template +sphere::sphere() +{} + +template +inline bounding_volume_type sphere::get_bounding_volume_type() const +{ + return bounding_volume_type::sphere; +} + +template +bool sphere::intersects(const sphere& sphere) const +{ + vector_type d = center - sphere.center; + T r = radius + sphere.radius; + return (math::dot(d, d) <= r * r); +} + +template +bool sphere::intersects(const aabb& aabb) const +{ + return aabb.intersects(*this); +} + +template +bool sphere::contains(const sphere& sphere) const +{ + T containment_radius = radius - sphere.radius; + if (containment_radius < T(0)) + return false; + + vector_type d = center - sphere.center; + return (math::dot(d, d) <= containment_radius * containment_radius); +} + +template +bool sphere::contains(const aabb& aabb) const +{ + T distance = T(0); + + vector_type a = center - aabb.min_point; + vector_type b = center - aabb.max_point; + + distance += std::max(a.x() * a.x(), b.x() * b.x()); + distance += std::max(a.y() * a.y(), b.y() * b.y()); + distance += std::max(a.z() * a.z(), b.z() * b.z()); + + return (distance <= radius * radius); +} + +template +bool sphere::contains(const vector_type& point) const +{ + vector_type d = center - point; + return (math::dot(d, d) <= radius * radius); +} + +} // namespace geom + +#endif // ANTKEEPER_GEOM_SPHERE_HPP diff --git a/src/engine/geom/spherical.hpp b/src/engine/geom/spherical.hpp new file mode 100644 index 0000000..3881665 --- /dev/null +++ b/src/engine/geom/spherical.hpp @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GEOM_SPHERICAL_HPP +#define ANTKEEPER_GEOM_SPHERICAL_HPP + +#include +#include + +namespace geom { + +/// Functions which operate on spherical coordinates. +namespace spherical { + +/** + * Converts spherical coordinates to Cartesian (rectangular) coordinates. + * + * @param v Spherical coordinates, in the ISO order of radial distance, polar angle (radians), and azimuthal angle (radians). + * @return Cartesian coordinates. + * + * @see geom::coordinates::cartesian + */ +template +math::vector3 to_cartesian(const math::vector3& v); + +template +math::vector3 to_cartesian(const math::vector3& v) +{ + const T x = v[0] * std::cos(v[1]); + + return math::vector3 + { + x * std::cos(v[2]), + x * std::sin(v[2]), + v[0] * std::sin(v[1]) + }; +} + +} // namespace spherical +} // namespace geom + +#endif // ANTKEEPER_GEOM_SPHERICAL_HPP diff --git a/src/engine/geom/view-frustum.hpp b/src/engine/geom/view-frustum.hpp new file mode 100644 index 0000000..630e988 --- /dev/null +++ b/src/engine/geom/view-frustum.hpp @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GEOM_VIEW_FRUSTUM_HPP +#define ANTKEEPER_GEOM_VIEW_FRUSTUM_HPP + +#include +#include +#include +#include + +namespace geom { + +/** + * View frustum. + */ +template +class view_frustum +{ +public: + typedef math::vector vector_type; + typedef math::matrix matrix_type; + typedef plane plane_type; + + /** + * Creates a view frustum from a view-projection matrix. + * + * @param view_projection View-projection matrix. + */ + view_frustum(const matrix_type& view_projection); + + /// Creates an uninitialized view frustum. + view_frustum(); + + /** + * Recalculates the view frustum from a view-projection matrix. + * + * @param view_projection View-projection matrix. + */ + void set_matrix(const matrix_type& view_projection); + + /// Returns a convex hull which describes the bounds of the view frustum. + const convex_hull& get_bounds() const; + + /// Returns the left clipping plane. + const plane_type& get_left() const; + + /// Returns the right clipping plane. + const plane_type& get_right() const; + + /// Returns the bottom clipping plane. + const plane_type& get_bottom() const; + + /// Returns the top clipping plane. + const plane_type& get_top() const; + + /// Returns the near clipping plane. + const plane_type& get_near() const; + + /// Returns the far clipping plane. + const plane_type& get_far() const; + + /** + * Returns an array containing the corners of the view frustum bounds. + * + * @return Array containing the corners of the view frustum bounds. Corners are stored in the following order: NTL, NTR, NBL, NBR, FTL, FTR, FBL, FBR; where N is near, F is far, T is top, B is bottom, L is left, and R is right, therefore NTL refers to the corner shared between the near, top, and left clipping planes. + */ + const std::array& get_corners() const; + +private: + void recalculate_planes(const matrix_type& view_projection); + void recalculate_corners(); + + convex_hull bounds; + std::array corners; +}; + +template +view_frustum::view_frustum(const matrix_type& view_projection): + bounds(6) +{ + set_matrix(view_projection); +} + +template +view_frustum::view_frustum(): + view_frustum(math::matrix4::identity()) +{} + +template +void view_frustum::set_matrix(const matrix_type& view_projection) +{ + recalculate_planes(view_projection); + recalculate_corners(); +} + +template +inline const convex_hull& view_frustum::get_bounds() const +{ + return bounds; +} + +template +inline const typename view_frustum::plane_type& view_frustum::get_left() const +{ + return bounds.planes[0]; +} + +template +inline const typename view_frustum::plane_type& view_frustum::get_right() const +{ + return bounds.planes[1]; +} + +template +inline const typename view_frustum::plane_type& view_frustum::get_bottom() const +{ + return bounds.planes[2]; +} + +template +inline const typename view_frustum::plane_type& view_frustum::get_top() const +{ + return bounds.planes[3]; +} + +template +inline const typename view_frustum::plane_type& view_frustum::get_near() const +{ + return bounds.planes[4]; +} + +template +inline const typename view_frustum::plane_type& view_frustum::get_far() const +{ + return bounds.planes[5]; +} + +template +inline const std::array::vector_type, 8>& view_frustum::get_corners() const +{ + return corners; +} + +template +void view_frustum::recalculate_planes(const matrix_type& view_projection) +{ + matrix_type transpose = math::transpose(view_projection); + bounds.planes[0] = plane_type(transpose[3] + transpose[0]); + bounds.planes[1] = plane_type(transpose[3] - transpose[0]); + bounds.planes[2] = plane_type(transpose[3] + transpose[1]); + bounds.planes[3] = plane_type(transpose[3] - transpose[1]); + bounds.planes[4] = plane_type(transpose[3] + transpose[2]); + bounds.planes[5] = plane_type(transpose[3] - transpose[2]); +} + +template +void view_frustum::recalculate_corners() +{ + /// @TODO THESE CORNERS MAY BE INCORRECT!!!!!!!!!!! + corners[0] = plane_type::intersection(get_near(), get_top(), get_left()); + corners[1] = plane_type::intersection(get_near(), get_top(), get_right()); + corners[2] = plane_type::intersection(get_near(), get_bottom(), get_left()); + corners[3] = plane_type::intersection(get_near(), get_bottom(), get_right()); + corners[4] = plane_type::intersection(get_far(), get_top(), get_left()); + corners[5] = plane_type::intersection(get_far(), get_top(), get_right()); + corners[6] = plane_type::intersection(get_far(), get_bottom(), get_left()); + corners[7] = plane_type::intersection(get_far(), get_bottom(), get_right()); +} + +} // namespace geom + +#endif // ANTKEEPER_GEOM_VIEW_FRUSTUM_HPP + diff --git a/src/gl/buffer-usage.hpp b/src/engine/gl/buffer-usage.hpp similarity index 100% rename from src/gl/buffer-usage.hpp rename to src/engine/gl/buffer-usage.hpp diff --git a/src/gl/color-space.hpp b/src/engine/gl/color-space.hpp similarity index 100% rename from src/gl/color-space.hpp rename to src/engine/gl/color-space.hpp diff --git a/src/gl/drawing-mode.hpp b/src/engine/gl/drawing-mode.hpp similarity index 100% rename from src/gl/drawing-mode.hpp rename to src/engine/gl/drawing-mode.hpp diff --git a/src/gl/element-array-type.hpp b/src/engine/gl/element-array-type.hpp similarity index 100% rename from src/gl/element-array-type.hpp rename to src/engine/gl/element-array-type.hpp diff --git a/src/engine/gl/framebuffer.cpp b/src/engine/gl/framebuffer.cpp new file mode 100644 index 0000000..cf5fe56 --- /dev/null +++ b/src/engine/gl/framebuffer.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include + +namespace gl { + +static constexpr GLenum attachment_lut[] = +{ + GL_COLOR_ATTACHMENT0, + GL_DEPTH_ATTACHMENT, + GL_STENCIL_ATTACHMENT +}; + +framebuffer::framebuffer(int width, int height): + gl_framebuffer_id(0), + dimensions({width, height}), + color_attachment(nullptr), + depth_attachment(nullptr), + stencil_attachment(nullptr) +{ + glGenFramebuffers(1, &gl_framebuffer_id); +} + +framebuffer::framebuffer(): + gl_framebuffer_id(0), + dimensions({0, 0}), + color_attachment(nullptr), + depth_attachment(nullptr), + stencil_attachment(nullptr) +{} + +framebuffer::~framebuffer() +{ + if (gl_framebuffer_id) + { + glDeleteFramebuffers(1, &gl_framebuffer_id); + } +} + +void framebuffer::resize(const std::array& dimensions) +{ + this->dimensions = dimensions; +} + +void framebuffer::attach(framebuffer_attachment_type attachment_type, texture_2d* texture) +{ + glBindFramebuffer(GL_FRAMEBUFFER, gl_framebuffer_id); + + GLenum gl_attachment = attachment_lut[static_cast(attachment_type)]; + glFramebufferTexture2D(GL_FRAMEBUFFER, gl_attachment, GL_TEXTURE_2D, texture->gl_texture_id, 0); + + if (attachment_type == framebuffer_attachment_type::color) + color_attachment = texture; + else if (attachment_type == framebuffer_attachment_type::depth) + depth_attachment = texture; + else if (attachment_type == framebuffer_attachment_type::stencil) + stencil_attachment = texture; + + if (!color_attachment) + { + glDrawBuffer(GL_NONE); + glReadBuffer(GL_NONE); + } + else + { + glDrawBuffer(GL_COLOR_ATTACHMENT0); + glReadBuffer(GL_COLOR_ATTACHMENT0); + } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + +} // namespace gl diff --git a/src/gl/framebuffer.hpp b/src/engine/gl/framebuffer.hpp similarity index 100% rename from src/gl/framebuffer.hpp rename to src/engine/gl/framebuffer.hpp diff --git a/src/engine/gl/gl.hpp b/src/engine/gl/gl.hpp new file mode 100644 index 0000000..6cdf2ce --- /dev/null +++ b/src/engine/gl/gl.hpp @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GL_HPP +#define ANTKEEPER_GL_HPP + +/// Graphics library interface. +namespace gl {} + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif // ANTKEEPER_GL_HPP diff --git a/src/gl/pixel-format.hpp b/src/engine/gl/pixel-format.hpp similarity index 100% rename from src/gl/pixel-format.hpp rename to src/engine/gl/pixel-format.hpp diff --git a/src/gl/pixel-type.hpp b/src/engine/gl/pixel-type.hpp similarity index 100% rename from src/gl/pixel-type.hpp rename to src/engine/gl/pixel-type.hpp diff --git a/src/engine/gl/rasterizer.cpp b/src/engine/gl/rasterizer.cpp new file mode 100644 index 0000000..1601b50 --- /dev/null +++ b/src/engine/gl/rasterizer.cpp @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include +#include +#include + +namespace gl { + +static constexpr GLenum drawing_mode_lut[] = +{ + GL_POINTS, + GL_LINE_STRIP, + GL_LINE_LOOP, + GL_LINES, + GL_LINE_STRIP_ADJACENCY, + GL_LINES_ADJACENCY, + GL_TRIANGLE_STRIP, + GL_TRIANGLE_FAN, + GL_TRIANGLES, + GL_TRIANGLE_STRIP_ADJACENCY, + GL_TRIANGLES_ADJACENCY +}; + +static constexpr GLenum element_array_type_lut[] = +{ + GL_UNSIGNED_BYTE, + GL_UNSIGNED_SHORT, + GL_UNSIGNED_INT +}; + +rasterizer::rasterizer(): + bound_vao(nullptr), + bound_shader_program(nullptr) +{ + // Determine dimensions of default framebuffer + GLint scissor_box[4] = {0, 0, 0, 0}; + glGetIntegerv(GL_SCISSOR_BOX, scissor_box); + + // Setup default framebuffer + default_framebuffer = new framebuffer(); + default_framebuffer->gl_framebuffer_id = 0; + default_framebuffer->dimensions = {scissor_box[2], scissor_box[3]}; + + // Bind default framebuffer + bound_framebuffer = default_framebuffer; +} + +rasterizer::~rasterizer() +{ + delete default_framebuffer; +} + +void rasterizer::context_resized(int width, int height) +{ + default_framebuffer->dimensions = {width, height}; +} + +void rasterizer::use_framebuffer(const gl::framebuffer& framebuffer) +{ + if (bound_framebuffer != &framebuffer) + { + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.gl_framebuffer_id); + bound_framebuffer = &framebuffer; + } +} + +void rasterizer::set_clear_color(float r, float g, float b, float a) +{ + glClearColor(r, g, b, a); +} + +void rasterizer::set_clear_depth(float depth) +{ + glClearDepth(depth); +} + +void rasterizer::set_clear_stencil(int s) +{ + glClearStencil(s); +} + +void rasterizer::clear_framebuffer(bool color, bool depth, bool stencil) +{ + GLbitfield mask = 0; + + if (color) + mask |= GL_COLOR_BUFFER_BIT; + if (depth) + mask |= GL_DEPTH_BUFFER_BIT; + if (stencil) + mask |= GL_STENCIL_BUFFER_BIT; + + glClear(mask); +} + +void rasterizer::set_viewport(int x, int y, int width, int height) +{ + glViewport(x, y, static_cast(width), static_cast(height)); +} + +void rasterizer::use_program(const shader_program& program) +{ + if (bound_shader_program != &program) + { + glUseProgram(program.gl_program_id); + bound_shader_program = &program; + } +} + +void rasterizer::draw_arrays(const vertex_array& vao, drawing_mode mode, std::size_t offset, std::size_t count) +{ + GLenum gl_mode = drawing_mode_lut[static_cast(mode)]; + + if (bound_vao != &vao) + { + glBindVertexArray(vao.gl_array_id); + bound_vao = &vao; + } + + glDrawArrays(gl_mode, static_cast(offset), static_cast(count)); +} + +void rasterizer::draw_arrays_instanced(const vertex_array& vao, drawing_mode mode, std::size_t offset, std::size_t count, std::size_t instance_count) +{ + GLenum gl_mode = drawing_mode_lut[static_cast(mode)]; + + if (bound_vao != &vao) + { + glBindVertexArray(vao.gl_array_id); + bound_vao = &vao; + } + + glDrawArraysInstanced(gl_mode, static_cast(offset), static_cast(count), static_cast(instance_count)); +} + +void rasterizer::draw_elements(const vertex_array& vao, drawing_mode mode, std::size_t offset, std::size_t count, element_array_type type) +{ + GLenum gl_mode = drawing_mode_lut[static_cast(mode)]; + GLenum gl_type = element_array_type_lut[static_cast(type)]; + + if (bound_vao != &vao) + { + glBindVertexArray(vao.gl_array_id); + bound_vao = &vao; + } + + glDrawElements(gl_mode, static_cast(count), gl_type, (const GLvoid*)offset); +} + +} // namespace gl diff --git a/src/gl/rasterizer.hpp b/src/engine/gl/rasterizer.hpp similarity index 100% rename from src/gl/rasterizer.hpp rename to src/engine/gl/rasterizer.hpp diff --git a/src/engine/gl/shader-input.cpp b/src/engine/gl/shader-input.cpp new file mode 100644 index 0000000..000bdd0 --- /dev/null +++ b/src/engine/gl/shader-input.cpp @@ -0,0 +1,773 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +namespace gl { + +shader_input::shader_input(shader_program* program, std::size_t input_index, int gl_uniform_location, const std::string& name, shader_variable_type data_type, std::size_t element_count, int texture_unit): + program(program), + input_index(input_index), + gl_uniform_location(gl_uniform_location), + name(name), + data_type(data_type), + element_count(element_count), + texture_unit(texture_unit) +{} + +shader_input::~shader_input() +{} + +bool shader_input::upload(const bool& value) const +{ + if (gl_uniform_location == -1) + return false; + + glUniform1i(gl_uniform_location, static_cast(value)); + return true; +} + +bool shader_input::upload(const bool2& value) const +{ + if (gl_uniform_location == -1) + return false; + + const GLint values[] = {value[0], value[1]}; + glUniform2iv(gl_uniform_location, 1, values); + return true; +} + +bool shader_input::upload(const bool3& value) const +{ + if (gl_uniform_location == -1) + return false; + + const GLint values[] = {value[0], value[1], value[2]}; + glUniform3iv(gl_uniform_location, 1, values); + return true; +} + +bool shader_input::upload(const bool4& value) const +{ + if (gl_uniform_location == -1) + return false; + + const GLint values[] = {value[0], value[1], value[2], value[3]}; + glUniform4iv(gl_uniform_location, 1, values); + return true; +} + +bool shader_input::upload(const int& value) const +{ + if (gl_uniform_location == -1) + return false; + + glUniform1i(gl_uniform_location, value); + return true; +} + +bool shader_input::upload(const int2& value) const +{ + if (gl_uniform_location == -1) + return false; + + glUniform2iv(gl_uniform_location, 1, value.data()); + return true; +} + +bool shader_input::upload(const int3& value) const +{ + if (gl_uniform_location == -1) + return false; + + glUniform3iv(gl_uniform_location, 1, value.data()); + return true; +} + +bool shader_input::upload(const int4& value) const +{ + if (gl_uniform_location == -1) + return false; + + glUniform4iv(gl_uniform_location, 1, value.data()); + return true; +} + +bool shader_input::upload(const unsigned int& value) const +{ + if (gl_uniform_location == -1) + return false; + + glUniform1ui(gl_uniform_location, value); + return true; +} + +bool shader_input::upload(const uint2& value) const +{ + if (gl_uniform_location == -1) + return false; + + glUniform2uiv(gl_uniform_location, 1, value.data()); + return true; +} + +bool shader_input::upload(const uint3& value) const +{ + if (gl_uniform_location == -1) + return false; + + glUniform3uiv(gl_uniform_location, 1, value.data()); + return true; +} + +bool shader_input::upload(const uint4& value) const +{ + if (gl_uniform_location == -1) + return false; + + glUniform4uiv(gl_uniform_location, 1, value.data()); + return true; +} + +bool shader_input::upload(const float& value) const +{ + if (gl_uniform_location == -1) + return false; + + glUniform1f(gl_uniform_location, value); + return true; +} + +bool shader_input::upload(const float2& value) const +{ + if (gl_uniform_location == -1) + return false; + + glUniform2fv(gl_uniform_location, 1, value.data()); + return true; +} + +bool shader_input::upload(const float3& value) const +{ + if (gl_uniform_location == -1) + return false; + + glUniform3fv(gl_uniform_location, 1, value.data()); + return true; +} + +bool shader_input::upload(const float4& value) const +{ + if (gl_uniform_location == -1) + return false; + + glUniform4fv(gl_uniform_location, 1, value.data()); + return true; +} + +bool shader_input::upload(const float2x2& value) const +{ + if (gl_uniform_location == -1) + return false; + + glUniformMatrix2fv(gl_uniform_location, 1, GL_FALSE, value[0].data()); + return true; +} + +bool shader_input::upload(const float3x3& value) const +{ + if (gl_uniform_location == -1) + return false; + + glUniformMatrix3fv(gl_uniform_location, 1, GL_FALSE, value[0].data()); + return true; +} + +bool shader_input::upload(const float4x4& value) const +{ + if (gl_uniform_location == -1) + return false; + + glUniformMatrix4fv(gl_uniform_location, 1, GL_FALSE, value[0].data()); + return true; +} + +bool shader_input::upload(const texture_1d* value) const +{ + if (gl_uniform_location == -1) + return false; + + // Bind texture to a texture unit reserved by this shader input + glActiveTexture(GL_TEXTURE0 + texture_unit); + glBindTexture(GL_TEXTURE_1D, value->gl_texture_id); + + // Upload texture unit index to shader + glUniform1i(gl_uniform_location, texture_unit); + + return true; +} + +bool shader_input::upload(const texture_2d* value) const +{ + if (gl_uniform_location == -1) + return false; + + // Bind texture to a texture unit reserved by this shader input + glActiveTexture(GL_TEXTURE0 + texture_unit); + glBindTexture(GL_TEXTURE_2D, value->gl_texture_id); + + // Upload texture unit index to shader + glUniform1i(gl_uniform_location, texture_unit); + + return true; +} + +bool shader_input::upload(const texture_3d* value) const +{ + if (gl_uniform_location == -1) + return false; + + // Bind texture to a texture unit reserved by this shader input + glActiveTexture(GL_TEXTURE0 + texture_unit); + glBindTexture(GL_TEXTURE_3D, value->gl_texture_id); + + // Upload texture unit index to shader + glUniform1i(gl_uniform_location, texture_unit); + + return true; +} + +bool shader_input::upload(const texture_cube* value) const +{ + if (gl_uniform_location == -1) + return false; + + // Bind texture to a texture unit reserved by this shader input + glActiveTexture(GL_TEXTURE0 + texture_unit); + glBindTexture(GL_TEXTURE_CUBE_MAP, value->gl_texture_id); + + // Upload texture unit index to shader + glUniform1i(gl_uniform_location, texture_unit); + + return true; +} + +bool shader_input::upload(std::size_t index, const bool& value) const +{ + if (gl_uniform_location == -1) + return false; + + glUniform1i(gl_uniform_location + static_cast(index), static_cast(value)); + return true; +} + +bool shader_input::upload(std::size_t index, const bool2& value) const +{ + if (gl_uniform_location == -1) + return false; + + const GLint values[] = {value[0], value[1]}; + glUniform2iv(gl_uniform_location + static_cast(index), 1, values); + return true; +} + +bool shader_input::upload(std::size_t index, const bool3& value) const +{ + if (gl_uniform_location == -1) + return false; + + const GLint values[] = {value[0], value[1], value[3]}; + glUniform3iv(gl_uniform_location + static_cast(index), 1, values); + return true; +} + +bool shader_input::upload(std::size_t index, const bool4& value) const +{ + if (gl_uniform_location == -1) + return false; + + const GLint values[] = {value[0], value[1], value[3], value[4]}; + glUniform4iv(gl_uniform_location + static_cast(index), 1, values); + return true; +} + +bool shader_input::upload(std::size_t index, const int& value) const +{ + if (gl_uniform_location == -1) + return false; + + glUniform1i(gl_uniform_location + static_cast(index), value); + return true; +} + +bool shader_input::upload(std::size_t index, const int2& value) const +{ + if (gl_uniform_location == -1) + return false; + + glUniform2iv(gl_uniform_location + static_cast(index), 1, value.data()); + return true; +} + +bool shader_input::upload(std::size_t index, const int3& value) const +{ + if (gl_uniform_location == -1) + return false; + + glUniform3iv(gl_uniform_location + static_cast(index), 1, value.data()); + return true; +} + +bool shader_input::upload(std::size_t index, const int4& value) const +{ + if (gl_uniform_location == -1) + return false; + + glUniform4iv(gl_uniform_location + static_cast(index), 1, value.data()); + return true; +} + +bool shader_input::upload(std::size_t index, const unsigned int& value) const +{ + if (gl_uniform_location == -1) + return false; + + glUniform1ui(gl_uniform_location + static_cast(index), value); + return true; +} + +bool shader_input::upload(std::size_t index, const uint2& value) const +{ + if (gl_uniform_location == -1) + return false; + + glUniform2uiv(gl_uniform_location + static_cast(index), 1, value.data()); + return true; +} + +bool shader_input::upload(std::size_t index, const uint3& value) const +{ + if (gl_uniform_location == -1) + return false; + + glUniform3uiv(gl_uniform_location + static_cast(index), 1, value.data()); + return true; +} + +bool shader_input::upload(std::size_t index, const uint4& value) const +{ + if (gl_uniform_location == -1) + return false; + + glUniform4uiv(gl_uniform_location + static_cast(index), 1, value.data()); + return true; +} + +bool shader_input::upload(std::size_t index, const float& value) const +{ + if (gl_uniform_location == -1) + return false; + + glUniform1f(gl_uniform_location + static_cast(index), value); + return true; +} + +bool shader_input::upload(std::size_t index, const float2& value) const +{ + if (gl_uniform_location == -1) + return false; + + glUniform2fv(gl_uniform_location + static_cast(index), 1, value.data()); + return true; +} + +bool shader_input::upload(std::size_t index, const float3& value) const +{ + if (gl_uniform_location == -1) + return false; + + glUniform3fv(gl_uniform_location + static_cast(index), 1, value.data()); + return true; +} + +bool shader_input::upload(std::size_t index, const float4& value) const +{ + if (gl_uniform_location == -1) + return false; + + glUniform4fv(gl_uniform_location + static_cast(index), 1, value.data()); + return true; +} + +bool shader_input::upload(std::size_t index, const float2x2& value) const +{ + if (gl_uniform_location == -1) + return false; + + glUniformMatrix2fv(gl_uniform_location + static_cast(index) * 2, 1, GL_FALSE, value[0].data()); + return true; +} + +bool shader_input::upload(std::size_t index, const float3x3& value) const +{ + if (gl_uniform_location == -1) + return false; + + glUniformMatrix3fv(gl_uniform_location + static_cast(index) * 3, 1, GL_FALSE, value[0].data()); + return true; +} + +bool shader_input::upload(std::size_t index, const float4x4& value) const +{ + if (gl_uniform_location == -1) + return false; + + glUniformMatrix4fv(gl_uniform_location + static_cast(index) * 4, 1, GL_FALSE, value[0].data()); + return true; +} + +bool shader_input::upload(std::size_t index, const texture_1d* value) const +{ + if (gl_uniform_location == -1) + return false; + + // Bind texture to a texture unit reserved by this shader input + glActiveTexture(GL_TEXTURE0 + texture_unit + static_cast(index)); + glBindTexture(GL_TEXTURE_1D, value->gl_texture_id); + + // Upload texture unit index to shader + glUniform1i(gl_uniform_location + static_cast(index), texture_unit + static_cast(index)); + + return true; +} + +bool shader_input::upload(std::size_t index, const texture_2d* value) const +{ + if (gl_uniform_location == -1) + return false; + + // Bind texture to a texture unit reserved by this shader input + glActiveTexture(GL_TEXTURE0 + texture_unit + static_cast(index)); + glBindTexture(GL_TEXTURE_2D, value->gl_texture_id); + + // Upload texture unit index to shader + glUniform1i(gl_uniform_location + static_cast(index), texture_unit + static_cast(index)); + + return true; +} + +bool shader_input::upload(std::size_t index, const texture_3d* value) const +{ + if (gl_uniform_location == -1) + return false; + + // Bind texture to a texture unit reserved by this shader input + glActiveTexture(GL_TEXTURE0 + texture_unit + static_cast(index)); + glBindTexture(GL_TEXTURE_3D, value->gl_texture_id); + + // Upload texture unit index to shader + glUniform1i(gl_uniform_location + static_cast(index), texture_unit + static_cast(index)); + + return true; +} + +bool shader_input::upload(std::size_t index, const texture_cube* value) const +{ + if (gl_uniform_location == -1) + return false; + + // Bind texture to a texture unit reserved by this shader input + glActiveTexture(GL_TEXTURE0 + texture_unit + static_cast(index)); + glBindTexture(GL_TEXTURE_CUBE_MAP, value->gl_texture_id); + + // Upload texture unit index to shader + glUniform1i(gl_uniform_location + static_cast(index), texture_unit + static_cast(index)); + + return true; +} + +bool shader_input::upload(std::size_t index, const bool* values, std::size_t count) const +{ + if (gl_uniform_location == -1) + return false; + + int* int_values = new int[count]; + for (std::size_t i = 0; i < count; ++i) + int_values[i] = values[i]; + glUniform1iv(gl_uniform_location + static_cast(index), static_cast(count), &(*int_values)); + delete[] int_values; + + return true; +} + +bool shader_input::upload(std::size_t index, const bool2* values, std::size_t count) const +{ + if (gl_uniform_location == -1) + return false; + + int2* int2_values = new int2[count]; + for (std::size_t i = 0; i < count; ++i) + int2_values[i] = {values[i][0], values[i][1]}; + glUniform2iv(gl_uniform_location + static_cast(index), static_cast(count), &((*int2_values)[0])); + delete[] int2_values; + + return true; +} + +bool shader_input::upload(std::size_t index, const bool3* values, std::size_t count) const +{ + if (gl_uniform_location == -1) + return false; + + int3* int3_values = new int3[count]; + for (std::size_t i = 0; i < count; ++i) + int3_values[i] = {values[i][0], values[i][1], values[i][2]}; + glUniform3iv(gl_uniform_location + static_cast(index), static_cast(count), &((*int3_values)[0])); + delete[] int3_values; + + return true; +} + +bool shader_input::upload(std::size_t index, const bool4* values, std::size_t count) const +{ + if (gl_uniform_location == -1) + return false; + + int4* int4_values = new int4[count]; + for (std::size_t i = 0; i < count; ++i) + int4_values[i] = {values[i][0], values[i][1], values[i][2], values[i][3]}; + glUniform4iv(gl_uniform_location + static_cast(index), static_cast(count), &((*int4_values)[0])); + delete[] int4_values; + + return true; +} + +bool shader_input::upload(std::size_t index, const int* values, std::size_t count) const +{ + if (gl_uniform_location == -1) + return false; + + glUniform1iv(gl_uniform_location + static_cast(index), static_cast(count), &(*values)); + return true; +} + +bool shader_input::upload(std::size_t index, const int2* values, std::size_t count) const +{ + if (gl_uniform_location == -1) + return false; + + glUniform2iv(gl_uniform_location + static_cast(index), static_cast(count), (*values).data()); + return true; +} + +bool shader_input::upload(std::size_t index, const int3* values, std::size_t count) const +{ + if (gl_uniform_location == -1) + return false; + + glUniform3iv(gl_uniform_location + static_cast(index), static_cast(count), (*values).data()); + return true; +} + +bool shader_input::upload(std::size_t index, const int4* values, std::size_t count) const +{ + if (gl_uniform_location == -1) + return false; + + glUniform4iv(gl_uniform_location + static_cast(index), static_cast(count), (*values).data()); + return true; +} + +bool shader_input::upload(std::size_t index, const unsigned int* values, std::size_t count) const +{ + if (gl_uniform_location == -1) + return false; + + glUniform1uiv(gl_uniform_location + static_cast(index), static_cast(count), &(*values)); + return true; +} + +bool shader_input::upload(std::size_t index, const uint2* values, std::size_t count) const +{ + if (gl_uniform_location == -1) + return false; + + glUniform2uiv(gl_uniform_location + static_cast(index), static_cast(count), (*values).data()); + return true; +} + +bool shader_input::upload(std::size_t index, const uint3* values, std::size_t count) const +{ + if (gl_uniform_location == -1) + return false; + + glUniform3uiv(gl_uniform_location + static_cast(index), static_cast(count), (*values).data()); + return true; +} + +bool shader_input::upload(std::size_t index, const uint4* values, std::size_t count) const +{ + if (gl_uniform_location == -1) + return false; + + glUniform4uiv(gl_uniform_location + static_cast(index), static_cast(count), (*values).data()); + return true; +} + +bool shader_input::upload(std::size_t index, const float* values, std::size_t count) const +{ + if (gl_uniform_location == -1) + return false; + + glUniform1fv(gl_uniform_location + static_cast(index), static_cast(count), &(*values)); + return true; +} + +bool shader_input::upload(std::size_t index, const float2* values, std::size_t count) const +{ + if (gl_uniform_location == -1) + return false; + + glUniform2fv(gl_uniform_location + static_cast(index), static_cast(count), (*values).data()); + return true; +} + +bool shader_input::upload(std::size_t index, const float3* values, std::size_t count) const +{ + if (gl_uniform_location == -1) + return false; + + glUniform3fv(gl_uniform_location + static_cast(index), static_cast(count), (*values).data()); + return true; +} + +bool shader_input::upload(std::size_t index, const float4* values, std::size_t count) const +{ + if (gl_uniform_location == -1) + return false; + + glUniform4fv(gl_uniform_location + static_cast(index), static_cast(count), (*values).data()); + return true; +} + +bool shader_input::upload(std::size_t index, const float2x2* values, std::size_t count) const +{ + if (gl_uniform_location == -1) + return false; + + glUniformMatrix2fv(gl_uniform_location + static_cast(index) * 2, static_cast(count), GL_FALSE, (*values)[0].data()); + return true; +} + +bool shader_input::upload(std::size_t index, const float3x3* values, std::size_t count) const +{ + if (gl_uniform_location == -1) + return false; + + glUniformMatrix3fv(gl_uniform_location + static_cast(index) * 3, static_cast(count), GL_FALSE, (*values)[0].data()); + return true; +} + +bool shader_input::upload(std::size_t index, const float4x4* values, std::size_t count) const +{ + if (gl_uniform_location == -1) + return false; + + glUniformMatrix4fv(gl_uniform_location + static_cast(index) * 4, static_cast(count), GL_FALSE, (*values)[0].data()); + return true; +} + +bool shader_input::upload(std::size_t index, const texture_1d** values, std::size_t count) const +{ + if (gl_uniform_location == -1) + return false; + + for (std::size_t i = 0; i < count; ++i) + { + // Bind texture to a texture unit reserved by this shader input + glActiveTexture(GL_TEXTURE0 + texture_unit + static_cast(index + i)); + glBindTexture(GL_TEXTURE_1D, values[i]->gl_texture_id); + + // Upload texture unit index to shader + glUniform1i(gl_uniform_location + static_cast(index + i), texture_unit + static_cast(index + i)); + } + + return true; +} + +bool shader_input::upload(std::size_t index, const texture_2d** values, std::size_t count) const +{ + if (gl_uniform_location == -1) + return false; + + for (std::size_t i = 0; i < count; ++i) + { + // Bind texture to a texture unit reserved by this shader input + glActiveTexture(GL_TEXTURE0 + texture_unit + static_cast(index + i)); + glBindTexture(GL_TEXTURE_2D, values[i]->gl_texture_id); + + // Upload texture unit index to shader + glUniform1i(gl_uniform_location + static_cast(index + i), texture_unit + static_cast(index + i)); + } + + return true; +} + +bool shader_input::upload(std::size_t index, const texture_3d** values, std::size_t count) const +{ + if (gl_uniform_location == -1) + return false; + + for (std::size_t i = 0; i < count; ++i) + { + // Bind texture to a texture unit reserved by this shader input + glActiveTexture(GL_TEXTURE0 + texture_unit + static_cast(index + i)); + glBindTexture(GL_TEXTURE_3D, values[i]->gl_texture_id); + + // Upload texture unit index to shader + glUniform1i(gl_uniform_location + static_cast(index + i), texture_unit + static_cast(index + i)); + } + + return true; +} + +bool shader_input::upload(std::size_t index, const texture_cube** values, std::size_t count) const +{ + if (gl_uniform_location == -1) + return false; + + for (std::size_t i = 0; i < count; ++i) + { + // Bind texture to a texture unit reserved by this shader input + glActiveTexture(GL_TEXTURE0 + texture_unit + static_cast(index + i)); + glBindTexture(GL_TEXTURE_CUBE_MAP, values[i]->gl_texture_id); + + // Upload texture unit index to shader + glUniform1i(gl_uniform_location + static_cast(index + i), texture_unit + static_cast(index + i)); + } + + return true; +} + +} // namespace gl diff --git a/src/engine/gl/shader-input.hpp b/src/engine/gl/shader-input.hpp new file mode 100644 index 0000000..002c6a2 --- /dev/null +++ b/src/engine/gl/shader-input.hpp @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GL_SHADER_INPUT_HPP +#define ANTKEEPER_GL_SHADER_INPUT_HPP + +#include +#include + +namespace gl { + +class shader_program; +class texture_1d; +class texture_2d; +class texture_3d; +class texture_cube; +enum class shader_variable_type; + +/** + * Port through which data can be uploaded to shader variables. + */ +class shader_input +{ +public: + /** + * Returns the type of data which can be passed through this input. + */ + shader_variable_type get_data_type() const; + + /** + * Returns `true` if the input data is stored in an array. + */ + bool is_array() const; + + /** + * Returns the number of elements the array can contain, or `1` if the data is not stored in an array. + */ + std::size_t get_element_count() const; + + /** + * Uploads a value to the shader. + * + * @param value Value to upload. + * @return `true` if the value was uploaded successfully, `false` otherwise. + */ + ///@{ + bool upload(const bool& value) const; + bool upload(const bool2& value) const; + bool upload(const bool3& value) const; + bool upload(const bool4& value) const; + bool upload(const int& value) const; + bool upload(const int2& value) const; + bool upload(const int3& value) const; + bool upload(const int4& value) const; + bool upload(const unsigned int& value) const; + bool upload(const uint2& value) const; + bool upload(const uint3& value) const; + bool upload(const uint4& value) const; + bool upload(const float& value) const; + bool upload(const float2& value) const; + bool upload(const float3& value) const; + bool upload(const float4& value) const; + bool upload(const float2x2& value) const; + bool upload(const float3x3& value) const; + bool upload(const float4x4& value) const; + bool upload(const texture_1d* value) const; + bool upload(const texture_2d* value) const; + bool upload(const texture_3d* value) const; + bool upload(const texture_cube* value) const; + ///@} + + /** + * Uploads a single array element to the shader. + * + * @param index Index of an array element. + * @param values Value to upload. + * @return `true` if the value was uploaded successfully, `false` otherwise. + */ + ///@{ + bool upload(std::size_t index, const bool& value) const; + bool upload(std::size_t index, const bool2& value) const; + bool upload(std::size_t index, const bool3& value) const; + bool upload(std::size_t index, const bool4& value) const; + bool upload(std::size_t index, const int& value) const; + bool upload(std::size_t index, const int2& value) const; + bool upload(std::size_t index, const int3& value) const; + bool upload(std::size_t index, const int4& value) const; + bool upload(std::size_t index, const unsigned int& value) const; + bool upload(std::size_t index, const uint2& value) const; + bool upload(std::size_t index, const uint3& value) const; + bool upload(std::size_t index, const uint4& value) const; + bool upload(std::size_t index, const float& value) const; + bool upload(std::size_t index, const float2& value) const; + bool upload(std::size_t index, const float3& value) const; + bool upload(std::size_t index, const float4& value) const; + bool upload(std::size_t index, const float2x2& value) const; + bool upload(std::size_t index, const float3x3& value) const; + bool upload(std::size_t index, const float4x4& value) const; + bool upload(std::size_t index, const texture_1d* value) const; + bool upload(std::size_t index, const texture_2d* value) const; + bool upload(std::size_t index, const texture_3d* value) const; + bool upload(std::size_t index, const texture_cube* value) const; + ///@} + + /** + * Uploads a range of array elements to the shader. + * + * @param index Index of the first array element. + * @param values Pointer to an array of values. + * @param count Number of elements to upload. + * @return `true` if the value was fed successfully, `false` otherwise. + */ + ///@{ + bool upload(std::size_t index, const bool* values, std::size_t count) const; + bool upload(std::size_t index, const bool2* values, std::size_t count) const; + bool upload(std::size_t index, const bool3* values, std::size_t count) const; + bool upload(std::size_t index, const bool4* values, std::size_t count) const; + bool upload(std::size_t index, const int* values, std::size_t count) const; + bool upload(std::size_t index, const int2* values, std::size_t count) const; + bool upload(std::size_t index, const int3* values, std::size_t count) const; + bool upload(std::size_t index, const int4* values, std::size_t count) const; + bool upload(std::size_t index, const unsigned int* values, std::size_t count) const; + bool upload(std::size_t index, const uint2* values, std::size_t count) const; + bool upload(std::size_t index, const uint3* values, std::size_t count) const; + bool upload(std::size_t index, const uint4* values, std::size_t count) const; + bool upload(std::size_t index, const float* values, std::size_t count) const; + bool upload(std::size_t index, const float2* values, std::size_t count) const; + bool upload(std::size_t index, const float3* values, std::size_t count) const; + bool upload(std::size_t index, const float4* values, std::size_t count) const; + bool upload(std::size_t index, const float2x2* values, std::size_t count) const; + bool upload(std::size_t index, const float3x3* values, std::size_t count) const; + bool upload(std::size_t index, const float4x4* values, std::size_t count) const; + bool upload(std::size_t index, const texture_1d** values, std::size_t count) const; + bool upload(std::size_t index, const texture_2d** values, std::size_t count) const; + bool upload(std::size_t index, const texture_3d** values, std::size_t count) const; + bool upload(std::size_t index, const texture_cube** values, std::size_t count) const; + ///@} + +private: + friend class shader_program; + + /** + * Creates a shader input. + * + * @param program Shader program with which this input is associated. + * @param gl_uniform_location Location of the shader uniform with which this shader input is associated. + * @param name Name of the input. + * @param data_type Type of data which can be passed through this input. + * @param element_count Number of elements which the array can contain, or `0` if input data is not stored in an array. + * @param texture_unit Texture unit to which texture shader variables can be bound, or `-1` if the data type is not a texture type. + */ + shader_input(shader_program* program, std::size_t input_index, int gl_uniform_location, const std::string& name, shader_variable_type data_type, std::size_t element_count, int texture_unit); + + /** + * Destroys a shader input. + */ + ~shader_input(); + + shader_program* program; + std::size_t input_index; + int gl_uniform_location; + std::string name; + shader_variable_type data_type; + std::size_t element_count; + int texture_unit; +}; + +inline shader_variable_type shader_input::get_data_type() const +{ + return data_type; +} + +inline bool shader_input::is_array() const +{ + return (element_count > 1); +} + +inline std::size_t shader_input::get_element_count() const +{ + return element_count; +} + +} // namespace gl + +#endif // ANTKEEPER_GL_SHADER_INPUT_HPP + diff --git a/src/engine/gl/shader-object.cpp b/src/engine/gl/shader-object.cpp new file mode 100644 index 0000000..58f2f6a --- /dev/null +++ b/src/engine/gl/shader-object.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include + +namespace gl { + +static constexpr GLenum gl_shader_type_lut[] = +{ + GL_VERTEX_SHADER, + GL_FRAGMENT_SHADER, + GL_GEOMETRY_SHADER +}; + +shader_object::shader_object(shader_stage stage): + gl_shader_id(0), + stage(stage), + compiled(false) +{ + // Look up OpenGL shader type enumeration that corresponds to the given stage + GLenum gl_shader_type = gl_shader_type_lut[static_cast(stage)]; + + // Create an OpenGL shader object + gl_shader_id = glCreateShader(gl_shader_type); + + // Handle OpenGL errors + if (!gl_shader_id) + { + throw std::runtime_error("An error occurred while creating an OpenGL shader object."); + } +} + +shader_object::~shader_object() +{ + // Flag the OpenGL shader object for deletion + glDeleteShader(gl_shader_id); +} + +void shader_object::source(const std::string& source_code) +{ + // Replace OpenGL shader object source code + GLint gl_length = static_cast(source_code.length()); + const GLchar* gl_string = source_code.c_str(); + glShaderSource(gl_shader_id, 1, &gl_string, &gl_length); + + // Handle OpenGL errors + switch (glGetError()) + { + case GL_INVALID_VALUE: + throw std::runtime_error("OpenGL shader object handle is not a value generated by OpenGL."); + break; + + case GL_INVALID_OPERATION: + throw std::runtime_error("OpenGL shader object handle is not a shader object."); + break; + } +} + +bool shader_object::compile() +{ + // Compile OpenGL shader object + glCompileShader(gl_shader_id); + + // Handle OpenGL errors + switch (glGetError()) + { + case GL_INVALID_VALUE: + throw std::runtime_error("OpenGL shader object handle is not a value generated by OpenGL."); + break; + + case GL_INVALID_OPERATION: + throw std::runtime_error("OpenGL shader object handle is not a shader object."); + break; + } + + // Get OpenGL shader object compilation status + GLint gl_compile_status; + glGetShaderiv(gl_shader_id, GL_COMPILE_STATUS, &gl_compile_status); + compiled = (gl_compile_status == GL_TRUE); + + // Get OpenGL shader object info log length + GLint gl_info_log_length; + glGetShaderiv(gl_shader_id, GL_INFO_LOG_LENGTH, &gl_info_log_length); + + if (gl_info_log_length > 0) + { + // Resize string to accommodate OpenGL shader object info log + info_log.resize(gl_info_log_length); + + // Read OpenGL shader object info log into string + glGetShaderInfoLog(gl_shader_id, gl_info_log_length, &gl_info_log_length, info_log.data()); + + // Remove redundant null terminator from string + info_log.pop_back(); + } + else + { + // Empty info log + info_log.clear(); + } + + // Return compilation status + return compiled; +} + +} // namespace gl diff --git a/src/engine/gl/shader-object.hpp b/src/engine/gl/shader-object.hpp new file mode 100644 index 0000000..bba5339 --- /dev/null +++ b/src/engine/gl/shader-object.hpp @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GL_SHADER_OBJECT_HPP +#define ANTKEEPER_GL_SHADER_OBJECT_HPP + +#include +#include +#include + +namespace gl { + +class shader_program; + +/** + * Shader object which can be compiled and linked to a shader program. + * + * @see gl::shader_program + * @see gl::shader_stage + */ +class shader_object +{ +public: + /** + * Creates an empty shader object for the specified shader stage. + * + * @param stage Shader stage in which this shader object will be used. + * + * @exception std::runtime_error An error occurred while creating an OpenGL shader object. + */ + shader_object(shader_stage stage); + + /** + * Destroys a shader object. + */ + ~shader_object(); + + /** + * Replaces the source code of the shader object. + * + * @param source_code String containing shader object source code. + * + * @exception std::runtime_error Shader object handle is not a value generated by OpenGL. + * @exception std::runtime_error Shader object handle is not a shader object. + */ + void source(const std::string& source_code); + + /** + * Compiles the shader object. + * + * @return `true` if the shader object was compiled successfully, `false` otherwise. If compilation fails, check the info log via shader_object::get_info_log() for more information. + * + * @exception std::runtime_error Shader object handle is not a value generated by OpenGL. + * @exception std::runtime_error Shader object handle is not a shader object. + * + * @see shader_object::get_info_log() + */ + bool compile(); + + /// Returns the shader stage of this shader object. + shader_stage get_stage() const; + + /// Returns the shader object info log, which is updated when the shader object is compiled. + const std::string& get_info_log() const; + + /// Returns `true` if the shader object has been successfully compiled, `false` otherwise. + bool was_compiled() const; + + shader_object(const shader_object&) = delete; + shader_object& operator=(const shader_object&) = delete; + +private: + friend class shader_program; + + unsigned int gl_shader_id; + shader_stage stage; + std::string info_log; + bool compiled; +}; + +inline shader_stage shader_object::get_stage() const +{ + return stage; +} + +inline const std::string& shader_object::get_info_log() const +{ + return info_log; +} + +inline bool shader_object::was_compiled() const +{ + return compiled; +} + +} // namespace gl + +#endif // ANTKEEPER_GL_SHADER_OBJECT_HPP diff --git a/src/engine/gl/shader-program.cpp b/src/engine/gl/shader-program.cpp new file mode 100644 index 0000000..151aff3 --- /dev/null +++ b/src/engine/gl/shader-program.cpp @@ -0,0 +1,339 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +namespace gl { + +shader_program::shader_program(): + gl_program_id(0), + linked(false) +{ + // Create an OpenGL shader program + gl_program_id = glCreateProgram(); + + // Handle OpenGL errors + if (!gl_program_id) + { + throw std::runtime_error("An error occurred while creating an OpenGL shader program."); + } +} + +shader_program::~shader_program() +{ + // Delete shader inputs + free_inputs(); + + // Detach all shader objects + detach_all(); + + // Delete the OpenGL shader program + glDeleteProgram(gl_program_id); +} + +void shader_program::attach(const shader_object* object) +{ + if (attached_objects.find(object) != attached_objects.end()) + { + throw std::runtime_error("Shader object is already attached to the shader program."); + } + else + { + // Check that both the OpenGL shader program and OpenGL shader object are valid + if (glIsProgram(gl_program_id) != GL_TRUE) + throw std::runtime_error("OpenGL shader program is not a valid program object."); + if (glIsShader(object->gl_shader_id) != GL_TRUE) + throw std::runtime_error("OpenGL shader object is not a valid shader object."); + + // Attach the OpenGL shader object to the OpenGL shader program + glAttachShader(gl_program_id, object->gl_shader_id); + + // Handle OpenGL errors + switch (glGetError()) + { + case GL_INVALID_OPERATION: + throw std::runtime_error("OpenGL shader object is already attached to the shader program."); + break; + } + + // Add shader object to set of attached objects + attached_objects.insert(object); + } +} + +void shader_program::detach(const shader_object* object) +{ + if (attached_objects.find(object) == attached_objects.end()) + { + throw std::runtime_error("Shader object is not attached to the shader program."); + } + else + { + // Check that both the OpenGL shader program and OpenGL shader object are valid + if (glIsProgram(gl_program_id) != GL_TRUE) + throw std::runtime_error("OpenGL shader program is not a valid program object."); + if (glIsShader(object->gl_shader_id) != GL_TRUE) + throw std::runtime_error("OpenGL shader object is not a valid shader object."); + + // Detach the OpenGL shader object from the OpenGL shader program + glDetachShader(gl_program_id, object->gl_shader_id); + + // Handle OpenGL errors + switch (glGetError()) + { + case GL_INVALID_OPERATION: + throw std::runtime_error("OpenGL shader object is not attached to the shader program."); + break; + } + + // Remove shader object from set of attached objects + attached_objects.erase(object); + } +} + +void shader_program::detach_all() +{ + while (!attached_objects.empty()) + detach(*attached_objects.begin()); +} + +bool shader_program::link() +{ + // Check that the OpenGL shader program is valid + if (glIsProgram(gl_program_id) != GL_TRUE) + throw std::runtime_error("OpenGL shader program is not a valid program object."); + + // Link OpenGL shader program + glLinkProgram(gl_program_id); + + // Handle OpenGL errors + switch (glGetError()) + { + case GL_INVALID_OPERATION: + throw std::runtime_error("OpenGL shader program is the currently active program object and transform feedback mode is active."); + break; + } + + // Get OpenGL shader program linking status + GLint gl_link_status; + glGetProgramiv(gl_program_id, GL_LINK_STATUS, &gl_link_status); + linked = (gl_link_status == GL_TRUE); + + // Get OpenGL shader program info log length + GLint gl_info_log_length; + glGetProgramiv(gl_program_id, GL_INFO_LOG_LENGTH, &gl_info_log_length); + + if (gl_info_log_length > 0) + { + // Resize string to accommodate OpenGL shader program info log + info_log.resize(gl_info_log_length); + + // Read OpenGL shader program info log into string + glGetProgramInfoLog(gl_program_id, gl_info_log_length, &gl_info_log_length, info_log.data()); + + // Remove redundant null terminator from string + info_log.pop_back(); + } + else + { + // Empty info log + info_log.clear(); + } + + // Find shader inputs + find_inputs(); + + return linked; +} + +void shader_program::find_inputs() +{ + // Get maximum uniform name length + GLint max_uniform_name_length = 0; + glGetProgramiv(gl_program_id, GL_ACTIVE_UNIFORM_MAX_LENGTH, &max_uniform_name_length); + + // Allocate uniform name buffer + GLchar* uniform_name = new GLchar[max_uniform_name_length]; + + // Get number of active uniforms in the shader + GLint active_uniform_count = 0; + glGetProgramiv(gl_program_id, GL_ACTIVE_UNIFORMS, &active_uniform_count); + + // Set first available texture unit + int available_texture_unit = 0; + + // For each active uniform + for (GLuint uniform_index = 0; uniform_index < static_cast(active_uniform_count); ++uniform_index) + { + // Get information about uniform + GLsizei uniform_name_length; + GLint uniform_size; + GLenum uniform_type; + glGetActiveUniform(gl_program_id, uniform_index, static_cast(max_uniform_name_length), &uniform_name_length, &uniform_size, &uniform_type, &uniform_name[0]); + + // Get name without array symbols + std::string input_name = uniform_name; + std::size_t bracketPos = input_name.find_first_of("["); + if (bracketPos != std::string::npos) + { + input_name = input_name.substr(0, bracketPos); + } + + // Determine corresponding shader variable data type + shader_variable_type variable_type; + int texture_unit = -1; + bool unsupported = false; + switch (uniform_type) + { + case GL_BOOL: + variable_type = shader_variable_type::bool1; + break; + case GL_BOOL_VEC2: + variable_type = shader_variable_type::bool2; + break; + case GL_BOOL_VEC3: + variable_type = shader_variable_type::bool3; + break; + case GL_BOOL_VEC4: + variable_type = shader_variable_type::bool4; + break; + + case GL_INT: + variable_type = shader_variable_type::int1; + break; + case GL_INT_VEC2: + variable_type = shader_variable_type::int2; + break; + case GL_INT_VEC3: + variable_type = shader_variable_type::int3; + break; + case GL_INT_VEC4: + variable_type = shader_variable_type::int4; + break; + + case GL_UNSIGNED_INT: + variable_type = shader_variable_type::uint1; + break; + case GL_UNSIGNED_INT_VEC2: + variable_type = shader_variable_type::uint2; + break; + case GL_UNSIGNED_INT_VEC3: + variable_type = shader_variable_type::uint3; + break; + case GL_UNSIGNED_INT_VEC4: + variable_type = shader_variable_type::uint4; + break; + + case GL_FLOAT: + variable_type = shader_variable_type::float1; + break; + case GL_FLOAT_VEC2: + variable_type = shader_variable_type::float2; + break; + case GL_FLOAT_VEC3: + variable_type = shader_variable_type::float3; + break; + case GL_FLOAT_VEC4: + variable_type = shader_variable_type::float4; + break; + + case GL_FLOAT_MAT2: + variable_type = shader_variable_type::float2x2; + break; + case GL_FLOAT_MAT3: + variable_type = shader_variable_type::float3x3; + break; + case GL_FLOAT_MAT4: + variable_type = shader_variable_type::float4x4; + break; + + case GL_SAMPLER_1D: + case GL_SAMPLER_1D_SHADOW: + variable_type = shader_variable_type::texture_1d; + texture_unit = available_texture_unit; + available_texture_unit += uniform_size; + break; + + case GL_SAMPLER_2D: + case GL_SAMPLER_2D_SHADOW: + variable_type = shader_variable_type::texture_2d; + texture_unit = available_texture_unit; + available_texture_unit += uniform_size; + break; + + case GL_SAMPLER_3D: + variable_type = shader_variable_type::texture_3d; + texture_unit = available_texture_unit; + available_texture_unit += uniform_size; + break; + + case GL_SAMPLER_CUBE: + variable_type = shader_variable_type::texture_cube; + texture_unit = available_texture_unit; + available_texture_unit += uniform_size; + break; + + default: + unsupported = true; + break; + } + + // Check if data type is supported + if (unsupported) + { + std::string message = std::string("Shader uniform \"") + std::string(uniform_name) + std::string("\" has unsupported data type."); + throw std::runtime_error(message.c_str()); + } + + // Get uniform location + GLint uniform_location = glGetUniformLocation(gl_program_id, uniform_name); + if (uniform_location == -1) + { + //std::string message = std::string("Unable to get location for uniform \"") + std::string(uniform_name) + std::string("\""); + //throw std::runtime_error(message.c_str()); + } + else + { + // Create new shader input + shader_input* input = new shader_input(this, inputs.size(), uniform_location, input_name, variable_type, uniform_size, texture_unit); + input_map[input_name] = input; + inputs.push_back(input); + } + } + + // Free uniform name buffer + delete[] uniform_name; +} + +void shader_program::free_inputs() +{ + for (shader_input* input: inputs) + { + delete input; + } + + inputs.clear(); +} + +} // namespace gl diff --git a/src/gl/shader-program.hpp b/src/engine/gl/shader-program.hpp similarity index 100% rename from src/gl/shader-program.hpp rename to src/engine/gl/shader-program.hpp diff --git a/src/gl/shader-stage.hpp b/src/engine/gl/shader-stage.hpp similarity index 100% rename from src/gl/shader-stage.hpp rename to src/engine/gl/shader-stage.hpp diff --git a/src/gl/shader-variable-type.hpp b/src/engine/gl/shader-variable-type.hpp similarity index 100% rename from src/gl/shader-variable-type.hpp rename to src/engine/gl/shader-variable-type.hpp diff --git a/src/engine/gl/texture-1d.cpp b/src/engine/gl/texture-1d.cpp new file mode 100644 index 0000000..54e858d --- /dev/null +++ b/src/engine/gl/texture-1d.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include + +namespace gl { + +texture_1d::texture_1d(std::uint16_t width, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data): + texture(width, type, format, color_space, data) +{} + +texture_1d::~texture_1d() +{} + +void texture_1d::resize(std::uint16_t width, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data) +{ + texture::resize(width, type, format, color_space, data); +} + +void texture_1d::set_wrapping(gl::texture_wrapping wrap_s) +{ + texture::set_wrapping(wrap_s); +} + +} // namespace gl diff --git a/src/engine/gl/texture-1d.hpp b/src/engine/gl/texture-1d.hpp new file mode 100644 index 0000000..dc2f301 --- /dev/null +++ b/src/engine/gl/texture-1d.hpp @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GL_TEXTURE_1D_HPP +#define ANTKEEPER_GL_TEXTURE_1D_HPP + +#include + +namespace gl { + +/** + * A 1D texture which can be uploaded to shaders via shader inputs. + */ +class texture_1d: public texture +{ +public: + /// @copydoc texture::texture(std::uint16_t, gl::pixel_type, gl::pixel_format, gl::color_space, const void*) + texture_1d(std::uint16_t width, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const void* data = nullptr); + + /// Destructs a 1D texture. + virtual ~texture_1d(); + + /// @copydoc texture::resize(std::uint16_t, gl::pixel_type, gl::pixel_format, gl::color_space, const void*) + virtual void resize(std::uint16_t width, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data); + + /// @copydoc texture::set_wrapping(gl::texture_wrapping) + virtual void set_wrapping(gl::texture_wrapping wrap_s); +}; + +} // namespace gl + +#endif // ANTKEEPER_GL_TEXTURE_1D_HPP diff --git a/src/engine/gl/texture-2d.cpp b/src/engine/gl/texture-2d.cpp new file mode 100644 index 0000000..bbc5bcb --- /dev/null +++ b/src/engine/gl/texture-2d.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include + +namespace gl { + +texture_2d::texture_2d(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data): + texture(width, height, type, format, color_space, data) +{} + +texture_2d::~texture_2d() +{} + +void texture_2d::resize(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data) +{ + texture::resize(width, height, type, format, color_space, data); +} + +void texture_2d::resize(std::uint16_t width, std::uint16_t height, const void* data) +{ + texture::resize(width, height, get_pixel_type(), get_pixel_format(), get_color_space(), data); +} + +void texture_2d::set_wrapping(gl::texture_wrapping wrap_s, texture_wrapping wrap_t) +{ + texture::set_wrapping(wrap_s, wrap_t); +} + +} // namespace gl diff --git a/src/engine/gl/texture-2d.hpp b/src/engine/gl/texture-2d.hpp new file mode 100644 index 0000000..5783b12 --- /dev/null +++ b/src/engine/gl/texture-2d.hpp @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GL_TEXTURE_2D_HPP +#define ANTKEEPER_GL_TEXTURE_2D_HPP + +#include + +namespace gl { + +/** + * A 2D texture which can be uploaded to shaders via shader inputs. + */ +class texture_2d: public texture +{ +public: + /// @copydoc texture::texture(std::uint16_t, std::uint16_t, gl::pixel_type, gl::pixel_format, gl::color_space, const void*) + texture_2d(std::uint16_t width, std::uint16_t height, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const void* data = nullptr); + + /// Destructs a 2D texture. + virtual ~texture_2d(); + + /// @copydoc texture::resize(std::uint16_t, std::uint16_t, gl::pixel_type, gl::pixel_format, gl::color_space, const void*) + virtual void resize(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data); + + /** + * Resizes the texture. + * + * @param width Texture width, in pixels. + * @param height Texture height, in pixels. + * @param data Pointer to pixel data. + */ + void resize(std::uint16_t width, std::uint16_t height, const void* data); + + /// @copydoc texture::set_wrapping(gl::texture_wrapping, gl::texture_wrapping) + virtual void set_wrapping(gl::texture_wrapping wrap_s, texture_wrapping wrap_t); +}; + +} // namespace gl + +#endif // ANTKEEPER_GL_TEXTURE_2D_HPP diff --git a/src/engine/gl/texture-3d.cpp b/src/engine/gl/texture-3d.cpp new file mode 100644 index 0000000..6a1b9fe --- /dev/null +++ b/src/engine/gl/texture-3d.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include + +namespace gl { + +texture_3d::texture_3d(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data): + texture(width, height, depth, type, format, color_space, data) +{} + +texture_3d::~texture_3d() +{} + +void texture_3d::resize(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data) +{ + texture::resize(width, height, depth, type, format, color_space, data); +} + +void texture_3d::set_wrapping(gl::texture_wrapping wrap_s, texture_wrapping wrap_t, texture_wrapping wrap_r) +{ + texture::set_wrapping(wrap_s, wrap_t, wrap_r); +} + +} // namespace gl diff --git a/src/engine/gl/texture-3d.hpp b/src/engine/gl/texture-3d.hpp new file mode 100644 index 0000000..e4e75e1 --- /dev/null +++ b/src/engine/gl/texture-3d.hpp @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GL_TEXTURE_3D_HPP +#define ANTKEEPER_GL_TEXTURE_3D_HPP + +#include + +namespace gl { + +/** + * A 3D texture which can be uploaded to shaders via shader inputs. + */ +class texture_3d: public texture +{ +public: + /// @copydoc texture::texture(std::uint16_t, std::uint16_t, std::uint16_t, gl::pixel_type, gl::pixel_format, gl::color_space, const void*) + texture_3d(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const void* data = nullptr); + + /// Destructs a 3D texture. + virtual ~texture_3d(); + + /// @copydoc texture::resize(std::uint16_t, std::uint16_t, std::uint16_t, gl::pixel_type, gl::pixel_format, gl::color_space, const void*) + virtual void resize(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data); + + /// @copydoc texture::set_wrapping(gl::texture_wrapping, gl::texture_wrapping, gl::texture_wrapping) + virtual void set_wrapping(gl::texture_wrapping wrap_s, texture_wrapping wrap_t, texture_wrapping wrap_r); +}; + +} // namespace gl + +#endif // ANTKEEPER_GL_TEXTURE_3D_HPP diff --git a/src/engine/gl/texture-cube.cpp b/src/engine/gl/texture-cube.cpp new file mode 100644 index 0000000..b4a507a --- /dev/null +++ b/src/engine/gl/texture-cube.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include + +namespace gl { + +texture_cube::texture_cube(): + gl_texture_id(0), + face_size(0) +{ + glGenTextures(1, &gl_texture_id); +} + +texture_cube::~texture_cube() +{ + glDeleteTextures(1, &gl_texture_id); +} + +} // namespace gl \ No newline at end of file diff --git a/src/gl/texture-cube.hpp b/src/engine/gl/texture-cube.hpp similarity index 100% rename from src/gl/texture-cube.hpp rename to src/engine/gl/texture-cube.hpp diff --git a/src/gl/texture-filter.hpp b/src/engine/gl/texture-filter.hpp similarity index 100% rename from src/gl/texture-filter.hpp rename to src/engine/gl/texture-filter.hpp diff --git a/src/gl/texture-wrapping.hpp b/src/engine/gl/texture-wrapping.hpp similarity index 100% rename from src/gl/texture-wrapping.hpp rename to src/engine/gl/texture-wrapping.hpp diff --git a/src/engine/gl/texture.cpp b/src/engine/gl/texture.cpp new file mode 100644 index 0000000..682c5c6 --- /dev/null +++ b/src/engine/gl/texture.cpp @@ -0,0 +1,267 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include +#include +#include + +namespace gl { + +static constexpr GLenum pixel_format_lut[] = +{ + GL_DEPTH_COMPONENT, + GL_DEPTH_STENCIL, + GL_RED, + GL_RG, + GL_RGB, + GL_BGR, + GL_RGBA, + GL_BGRA +}; + +static constexpr GLenum pixel_type_lut[] = +{ + GL_BYTE, + GL_UNSIGNED_BYTE, + GL_SHORT, + GL_UNSIGNED_SHORT, + GL_INT, + GL_UNSIGNED_INT, + GL_HALF_FLOAT, + GL_FLOAT +}; + +static constexpr GLenum linear_internal_format_lut[][8] = +{ + {GL_NONE, GL_NONE, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT32, GL_DEPTH_COMPONENT32, GL_NONE, GL_DEPTH_COMPONENT32F}, + + // Note: GL_DEPTH32F_STENCIL8 is actually a 64-bit format, 32 depth bits, 8 stencil bits, and 24 alignment bits. + {GL_NONE, GL_NONE, GL_NONE, GL_NONE, GL_DEPTH24_STENCIL8, GL_DEPTH24_STENCIL8, GL_NONE, GL_DEPTH32F_STENCIL8}, + + {GL_R8, GL_R8, GL_R16, GL_R16, GL_R32F, GL_R32F, GL_R16F, GL_R32F}, + {GL_RG8, GL_RG8, GL_RG16, GL_RG16, GL_RG32F, GL_RG32F, GL_RG16F, GL_RG32F}, + {GL_RGB8, GL_RGB8, GL_RGB16, GL_RGB16, GL_RGB32F, GL_RGB32F, GL_RGB16F, GL_RGB32F}, + {GL_RGB8, GL_RGB8, GL_RGB16, GL_RGB16, GL_RGB32F, GL_RGB32F, GL_RGB16F, GL_RGB32F}, + {GL_RGBA8, GL_RGBA8, GL_RGBA16, GL_RGBA16, GL_RGBA32F, GL_RGBA32F, GL_RGBA16F, GL_RGBA32F}, + {GL_RGBA8, GL_RGBA8, GL_RGBA16, GL_RGBA16, GL_RGBA32F, GL_RGBA32F, GL_RGBA16F, GL_RGBA32F} +}; + +static constexpr GLenum srgb_internal_format_lut[][8] = +{ + {GL_NONE, GL_NONE, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT32, GL_DEPTH_COMPONENT32, GL_NONE, GL_DEPTH_COMPONENT32F}, + {GL_NONE, GL_NONE, GL_NONE, GL_NONE, GL_DEPTH24_STENCIL8, GL_DEPTH24_STENCIL8, GL_NONE, GL_DEPTH32F_STENCIL8}, + {GL_SRGB8, GL_SRGB8, GL_R16, GL_R16, GL_R32F, GL_R32F, GL_R16F, GL_R32F}, + {GL_SRGB8, GL_SRGB8, GL_RG16, GL_RG16, GL_RG32F, GL_RG32F, GL_RG16F, GL_RG32F}, + {GL_SRGB8, GL_SRGB8, GL_RGB16, GL_RGB16, GL_RGB32F, GL_RGB32F, GL_RGB16F, GL_RGB32F}, + {GL_SRGB8, GL_SRGB8, GL_RGB16, GL_RGB16, GL_RGB32F, GL_RGB32F, GL_RGB16F, GL_RGB32F}, + {GL_SRGB8_ALPHA8, GL_SRGB8_ALPHA8, GL_RGBA16, GL_RGBA16, GL_RGBA32F, GL_RGBA32F, GL_RGBA16F, GL_RGBA32F}, + {GL_SRGB8_ALPHA8, GL_SRGB8_ALPHA8, GL_RGBA16, GL_RGBA16, GL_RGBA32F, GL_RGBA32F, GL_RGBA16F, GL_RGBA32F} +}; + +static constexpr GLint swizzle_mask_lut[][4] = +{ + {GL_RED, GL_RED, GL_RED, GL_ONE}, + {GL_RED, GL_GREEN, GL_ZERO, GL_ONE}, + {GL_RED, GL_RED, GL_RED, GL_ONE}, + {GL_RED, GL_RED, GL_RED, GL_GREEN}, + {GL_RED, GL_GREEN, GL_BLUE, GL_ONE}, + {GL_RED, GL_GREEN, GL_BLUE, GL_ONE}, + {GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA}, + {GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA} +}; + +static constexpr GLenum wrapping_lut[] = +{ + GL_CLAMP_TO_BORDER, + GL_CLAMP_TO_EDGE, + GL_REPEAT, + GL_MIRRORED_REPEAT +}; + +static constexpr GLenum min_filter_lut[] = +{ + GL_NEAREST, + GL_LINEAR, + GL_NEAREST_MIPMAP_NEAREST, + GL_LINEAR_MIPMAP_NEAREST, + GL_NEAREST_MIPMAP_LINEAR, + GL_LINEAR_MIPMAP_LINEAR +}; + +static constexpr GLenum mag_filter_lut[] = +{ + GL_NEAREST, + GL_LINEAR +}; + +texture::texture(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data): + gl_texture_target((depth) ? GL_TEXTURE_3D : (height) ? GL_TEXTURE_2D : GL_TEXTURE_1D), + gl_texture_id(0), + dimensions({0, 0, 0}), + wrapping({texture_wrapping::repeat, texture_wrapping::repeat, texture_wrapping::repeat}), + filters({texture_min_filter::linear_mipmap_linear, texture_mag_filter::linear}), + max_anisotropy(0.0f) +{ + glGenTextures(1, &gl_texture_id); + resize(width, height, depth, type, format, color_space, data); + set_wrapping(wrapping[0], wrapping[1], wrapping[2]); + set_filters(std::get<0>(filters), std::get<1>(filters)); + set_max_anisotropy(max_anisotropy); +} + +texture::texture(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data): + texture(width, height, 0, type, format, color_space, data) +{} + +texture::texture(std::uint16_t width, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data): + texture(width, 0, 0, type, format, color_space, data) +{} + +texture::~texture() +{ + glDeleteTextures(1, &gl_texture_id); +} + +void texture::set_filters(texture_min_filter min_filter, texture_mag_filter mag_filter) +{ + filters = {min_filter, mag_filter}; + + GLenum gl_min_filter = min_filter_lut[static_cast(min_filter)]; + GLenum gl_mag_filter = mag_filter_lut[static_cast(mag_filter)]; + + glBindTexture(gl_texture_target, gl_texture_id); + glTexParameteri(gl_texture_target, GL_TEXTURE_MIN_FILTER, gl_min_filter); + glTexParameteri(gl_texture_target, GL_TEXTURE_MAG_FILTER, gl_mag_filter); +} + +void texture::set_max_anisotropy(float anisotropy) +{ + max_anisotropy = std::max(0.0f, std::min(1.0f, anisotropy)); + + // Get the maximum supported anisotropy value + float gl_max_texture_max_anisotropy; + glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &gl_max_texture_max_anisotropy); + + // Lerp between 1.0 and GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT + float gl_max_anisotropy = 1.0f + max_anisotropy * (gl_max_texture_max_anisotropy - 1.0f); + + glBindTexture(gl_texture_target, gl_texture_id); + glTexParameterf(gl_texture_target, GL_TEXTURE_MAX_ANISOTROPY_EXT, gl_max_anisotropy); +} + +void texture::set_wrapping(gl::texture_wrapping wrap_s, gl::texture_wrapping wrap_t, gl::texture_wrapping wrap_r) +{ + wrapping = {wrap_s, wrap_t, wrap_r}; + + GLenum gl_wrap_s = wrapping_lut[static_cast(wrap_s)]; + GLenum gl_wrap_t = wrapping_lut[static_cast(wrap_t)]; + GLenum gl_wrap_r = wrapping_lut[static_cast(wrap_r)]; + + glBindTexture(gl_texture_target, gl_texture_id); + glTexParameteri(gl_texture_target, GL_TEXTURE_WRAP_S, gl_wrap_s); + glTexParameteri(gl_texture_target, GL_TEXTURE_WRAP_T, gl_wrap_t); + glTexParameteri(gl_texture_target, GL_TEXTURE_WRAP_R, gl_wrap_r); +} + +void texture::set_wrapping(gl::texture_wrapping wrap_s, gl::texture_wrapping wrap_t) +{ + std::get<0>(wrapping) = wrap_s; + std::get<1>(wrapping) = wrap_t; + + GLenum gl_wrap_s = wrapping_lut[static_cast(wrap_s)]; + GLenum gl_wrap_t = wrapping_lut[static_cast(wrap_t)]; + + glBindTexture(gl_texture_target, gl_texture_id); + glTexParameteri(gl_texture_target, GL_TEXTURE_WRAP_S, gl_wrap_s); + glTexParameteri(gl_texture_target, GL_TEXTURE_WRAP_T, gl_wrap_t); +} + +void texture::set_wrapping(gl::texture_wrapping wrap_s) +{ + std::get<0>(wrapping) = wrap_s; + + GLenum gl_wrap_s = wrapping_lut[static_cast(wrap_s)]; + + glBindTexture(gl_texture_target, gl_texture_id); + glTexParameteri(gl_texture_target, GL_TEXTURE_WRAP_S, gl_wrap_s); +} + +void texture::resize(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data) +{ + dimensions = {width, height, depth}; + pixel_type = type; + pixel_format = format; + this->color_space = color_space; + + GLenum gl_internal_format; + if (color_space == gl::color_space::srgb) + { + gl_internal_format = srgb_internal_format_lut[static_cast(format)][static_cast(type)]; + } + else + { + gl_internal_format = linear_internal_format_lut[static_cast(format)][static_cast(type)]; + } + + GLenum gl_format = pixel_format_lut[static_cast(format)]; + const GLint* gl_swizzle_mask = swizzle_mask_lut[static_cast(format)]; + + GLenum gl_type = pixel_type_lut[static_cast(type)]; + + // Special cases for depth + stencil pixel formats + if (gl_internal_format == GL_DEPTH24_STENCIL8) + gl_type = GL_UNSIGNED_INT_24_8; + else if (gl_internal_format == GL_DEPTH32F_STENCIL8) + gl_type = GL_FLOAT_32_UNSIGNED_INT_24_8_REV; + + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + glBindTexture(gl_texture_target, gl_texture_id); + if (depth) + glTexImage3D(gl_texture_target, 0, gl_internal_format, width, height, depth, 0, gl_format, gl_type, data); + else if (height) + glTexImage2D(gl_texture_target, 0, gl_internal_format, width, height, 0, gl_format, gl_type, data); + else if (width) + glTexImage1D(gl_texture_target, 0, gl_internal_format, width, 0, gl_format, gl_type, data); + + glGenerateMipmap(gl_texture_target); + glTexParameteriv(gl_texture_target, GL_TEXTURE_SWIZZLE_RGBA, gl_swizzle_mask); + + /// TODO: remove this + if (format == pixel_format::d) + { + glTexParameteri(gl_texture_target, GL_TEXTURE_COMPARE_FUNC, GL_LESS); + glTexParameteri(gl_texture_target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); + } +} + +void texture::resize(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data) +{ + resize(width, height, 0, type, format, color_space, data); +} + +void texture::resize(std::uint16_t width, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data) +{ + resize(width, 0, 0, type, format, color_space, data); +} + +} // namespace gl diff --git a/src/engine/gl/texture.hpp b/src/engine/gl/texture.hpp new file mode 100644 index 0000000..c5e042f --- /dev/null +++ b/src/engine/gl/texture.hpp @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GL_TEXTURE_HPP +#define ANTKEEPER_GL_TEXTURE_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace gl { + +class framebuffer; +class shader_input; + +/** + * Abstract base class for 1D, 2D, 3D, and cube textures which can be uploaded to shaders via shader inputs. + */ +class texture +{ +public: + /** + * Constructs a texture. + * + * @param width Texture width, in pixels. + * @param height Texture height, in pixels. For 2D or 3D textures. + * @param depth Texture depth, in pixels. For 3D textures only. + * @param type Pixel component data type. + * @param format Pixel format. + * @param color_space Color space of the pixel data. + * @param data Pointer to pixel data. + * + * @warning If the sRGB color space is specified, pixel data will be stored internally as 8 bits per channel, and automatically converted to linear space before reading. + */ + /// @{ + texture(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const void* data = nullptr); + texture(std::uint16_t width, std::uint16_t height, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const void* data = nullptr); + texture(std::uint16_t width, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const void* data = nullptr); + /// @} + + /** + * Destructs a texture. + */ + virtual ~texture() = 0; + + /** + * Sets the texture filtering modes. + * + * @param min_filter Texture minification filter mode. + * @param mag_filter Texture magnification filter mode. + */ + void set_filters(texture_min_filter min_filter, texture_mag_filter mag_filter); + + /** + * Sets the maximum anisotropy. + * + * @param level Max anisotropy on `[0.0, 1.0]`, with `0.0` indicating normal filtering, and `1.0` indicating maximum anisotropic filtering. + */ + void set_max_anisotropy(float anisotropy); + + /// Returns the dimensions of the texture, in pixels. + const std::array& get_dimensions() const; + + /// Returns the width of the texture, in pixels. + const std::uint16_t& get_width() const; + + /// Returns the height of the texture, in pixels. + const std::uint16_t& get_height() const; + + /// Returns the depth of the texture, in pixels. + const std::uint16_t& get_depth() const; + + /// Returns the pixel type enumeration. + const pixel_type& get_pixel_type() const; + + /// Returns the pixel format enumeration. + const pixel_format& get_pixel_format() const; + + /// Returns the color space enumeration. + const color_space& get_color_space() const; + + /// Returns the wrapping modes of the texture. + const std::array& get_wrapping() const; + + /// Returns the filtering modes of the texture. + const std::tuple& get_filters() const; + + /// Returns the maximum anisotropy. + float get_max_anisotropy() const; + +protected: + /** + * Sets the texture wrapping modes. + * + * @param wrap_s Wrapping mode for s-coordinates. + * @param wrap_t Wrapping mode for t-coordinates. + * @param wrap_r Wrapping mode for r-coordinates. + */ + /// @{ + virtual void set_wrapping(gl::texture_wrapping wrap_s, gl::texture_wrapping wrap_t, gl::texture_wrapping wrap_r); + virtual void set_wrapping(gl::texture_wrapping wrap_s, gl::texture_wrapping wrap_t); + virtual void set_wrapping(gl::texture_wrapping wrap_s); + /// @} + + /** + * Resizes the texture. + * + * @param width Texture width, in pixels. + * @param height Texture height, in pixels. For 2D or 3D textures. + * @param depth Texture depth, in pixels. For 3D textures only. + * @param type Pixel component data type. + * @param format Pixel format. + * @param color_space Color space of the pixel data. + * @param data Pointer to pixel data. + * + * @warning If the sRGB color space is specified, pixel data will be stored internally as 8 bits per channel, and automatically converted to linear space before reading. + */ + /// @{ + virtual void resize(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data); + virtual void resize(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data); + virtual void resize(std::uint16_t width, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data); + /// @} + +private: + friend class framebuffer; + friend class shader_input; + + unsigned int gl_texture_target; + unsigned int gl_texture_id; + std::array dimensions; + gl::pixel_type pixel_type; + gl::pixel_format pixel_format; + gl::color_space color_space; + std::array wrapping; + std::tuple filters; + float max_anisotropy; +}; + +inline const std::array& texture::get_dimensions() const +{ + return dimensions; +} + +inline const std::uint16_t& texture::get_width() const +{ + return dimensions[0]; +} + +inline const std::uint16_t& texture::get_height() const +{ + return dimensions[1]; +} + +inline const std::uint16_t& texture::get_depth() const +{ + return dimensions[2]; +} + +inline const pixel_type& texture::get_pixel_type() const +{ + return pixel_type; +} + +inline const pixel_format& texture::get_pixel_format() const +{ + return pixel_format; +} + +inline const color_space& texture::get_color_space() const +{ + return color_space; +} + +inline const std::array& texture::get_wrapping() const +{ + return wrapping; +} + +inline const std::tuple& texture::get_filters() const +{ + return filters; +} + +inline float texture::get_max_anisotropy() const +{ + return max_anisotropy; +} + +} // namespace gl + +#endif // ANTKEEPER_GL_TEXTURE_HPP diff --git a/src/engine/gl/vertex-array.cpp b/src/engine/gl/vertex-array.cpp new file mode 100644 index 0000000..1788161 --- /dev/null +++ b/src/engine/gl/vertex-array.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include +#include +#include + +namespace gl { + +static constexpr GLenum vertex_attribute_type_lut[] = +{ + GL_BYTE, + GL_UNSIGNED_BYTE, + GL_SHORT, + GL_UNSIGNED_SHORT, + GL_INT, + GL_UNSIGNED_INT, + GL_HALF_FLOAT, + GL_FLOAT, + GL_DOUBLE +}; + +vertex_array::vertex_array(): + gl_array_id(0) +{ + glGenVertexArrays(1, &gl_array_id); +} + +vertex_array::~vertex_array() +{ + glDeleteVertexArrays(1, &gl_array_id); +} + +void vertex_array::bind(attribute_location_type location, const vertex_attribute& attribute) +{ + if (attribute.buffer == nullptr) + { + throw std::invalid_argument("Cannot bind vertex attribute that has a null vertex buffer."); + } + + if (attribute.components < 1 || attribute.components > 4) + { + throw std::invalid_argument("Cannot bind vertex attribute that has an unsupported number of components (" + std::to_string(attribute.components) + ")"); + } + + attributes[location] = attribute; + + GLenum gl_type = vertex_attribute_type_lut[static_cast(attribute.type)]; + glBindVertexArray(gl_array_id); + glBindBuffer(GL_ARRAY_BUFFER, attribute.buffer->gl_buffer_id); + glVertexAttribPointer( + static_cast(location), + static_cast(attribute.components), + gl_type, + GL_FALSE, + static_cast(attribute.stride), + (const GLvoid*)attribute.offset); + glEnableVertexAttribArray(static_cast(location)); +} + +void vertex_array::unbind(attribute_location_type location) +{ + if (auto it = attributes.find(location); it != attributes.end()) + { + glBindVertexArray(gl_array_id); + glDisableVertexAttribArray(static_cast(location)); + + attributes.erase(it); + } + else + { + throw std::invalid_argument("Non-existent vertex attribute cannot be unbound."); + } +} + +const typename vertex_array::attribute_map_type& vertex_array::get_attributes() const +{ + return attributes; +} + +} // namespace gl diff --git a/src/engine/gl/vertex-array.hpp b/src/engine/gl/vertex-array.hpp new file mode 100644 index 0000000..66c565e --- /dev/null +++ b/src/engine/gl/vertex-array.hpp @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GL_VERTEX_ARRAY_HPP +#define ANTKEEPER_GL_VERTEX_ARRAY_HPP + +#include +#include +#include + +namespace gl { + +class rasterizer; +class vertex_buffer; + +/** + * Vertex array object (VAO), which describes how vertex attributes are stored in vertex buffer objects (VBOs). + * + * @see gl::vertex_attribute + * @see gl::vertex_buffer + */ +class vertex_array +{ +public: + /// Vertex attribute binding location type. + typedef std::uint_fast32_t attribute_location_type; + + /// Maps vertex attribute to binding locations. + typedef std::unordered_map attribute_map_type; + + /// Constructs a vertex array. + vertex_array(); + + /// Destructs a vertex array. + ~vertex_array(); + + vertex_array(const vertex_array&) = delete; + vertex_array& operator=(const vertex_array&) = delete; + + /** + * Binds a vertex attribute to the vertex array. + * + * @param location Location to which the vertex attribute should be bound. + * @param attribute Vertex attribute to bind. + * + * @except std::invalid_argument Cannot bind vertex attribute that has a null vertex buffer. + * @except std::invalid_argument Cannot bind vertex attribute that has an unsupported number of components. + */ + void bind(attribute_location_type location, const vertex_attribute& attribute); + + /** + * Unbinds a vertex attribute from the vertex array. + * + * @param location Location of the vertex attribute to unbind. + * + * @except std::invalid_argument Non-existent vertex attribute cannot be unbound. + */ + void unbind(attribute_location_type location); + + /// Returns a const reference to the map of vertex attributes bound to this vertex array. + const attribute_map_type& get_attributes() const; + +private: + friend class rasterizer; + + attribute_map_type attributes; + std::uint_fast32_t gl_array_id; +}; + +} // namespace gl + +#endif // ANTKEEPER_GL_VERTEX_ARRAY_HPP diff --git a/src/gl/vertex-attribute.hpp b/src/engine/gl/vertex-attribute.hpp similarity index 100% rename from src/gl/vertex-attribute.hpp rename to src/engine/gl/vertex-attribute.hpp diff --git a/src/engine/gl/vertex-buffer.cpp b/src/engine/gl/vertex-buffer.cpp new file mode 100644 index 0000000..dc5961b --- /dev/null +++ b/src/engine/gl/vertex-buffer.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include + +namespace gl { + +static constexpr GLenum buffer_usage_lut[] = +{ + GL_STREAM_DRAW, + GL_STREAM_READ, + GL_STREAM_COPY, + GL_STATIC_DRAW, + GL_STATIC_READ, + GL_STATIC_COPY, + GL_DYNAMIC_DRAW, + GL_DYNAMIC_READ, + GL_DYNAMIC_COPY +}; + +vertex_buffer::vertex_buffer(std::size_t size, const void* data, buffer_usage usage): + gl_buffer_id(0), + size(size), + usage(usage) +{ + GLenum gl_usage = buffer_usage_lut[static_cast(usage)]; + glGenBuffers(1, &gl_buffer_id); + glBindBuffer(GL_ARRAY_BUFFER, gl_buffer_id); + glBufferData(GL_ARRAY_BUFFER, static_cast(size), data, gl_usage); +} + +vertex_buffer::vertex_buffer(): + vertex_buffer(0, nullptr, buffer_usage::static_draw) +{} + +vertex_buffer::~vertex_buffer() +{ + glDeleteBuffers(1, &gl_buffer_id); +} + +void vertex_buffer::repurpose(buffer_usage usage, std::size_t size, const void* data) +{ + this->size = size; + this->usage = usage; + + GLenum gl_usage = buffer_usage_lut[static_cast(usage)]; + glBindBuffer(GL_ARRAY_BUFFER, gl_buffer_id); + glBufferData(GL_ARRAY_BUFFER, static_cast(size), data, gl_usage); +} + +void vertex_buffer::resize(std::size_t size, const void* data) +{ + repurpose(usage, size, data); +} + +void vertex_buffer::write(std::size_t offset, std::size_t size, const void* data) +{ + // Abort empty write operations + if (!size) + return; + + // Bounds check + if (offset + size > this->size) + throw std::out_of_range("Vertex buffer write operation exceeded buffer bounds."); + + glBindBuffer(GL_ARRAY_BUFFER, gl_buffer_id); + glBufferSubData(GL_ARRAY_BUFFER, static_cast(offset), static_cast(size), data); +} + +void vertex_buffer::read(std::size_t offset, std::size_t size, void* data) const +{ + // Abort empty read operations + if (!size) + return; + + // Bounds check + if (offset + size > this->size) + throw std::out_of_range("Vertex buffer read operation exceeded buffer bounds."); + + glBindBuffer(GL_ARRAY_BUFFER, gl_buffer_id); + glGetBufferSubData(GL_ARRAY_BUFFER, static_cast(offset), static_cast(size), data); +} + +} // namespace gl diff --git a/src/engine/gl/vertex-buffer.hpp b/src/engine/gl/vertex-buffer.hpp new file mode 100644 index 0000000..dae5cdb --- /dev/null +++ b/src/engine/gl/vertex-buffer.hpp @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GL_VERTEX_BUFFER_HPP +#define ANTKEEPER_GL_VERTEX_BUFFER_HPP + +#include +#include +#include + +namespace gl { + +class vertex_array; + +/** + * Vertex buffer object (VBO). + */ +class vertex_buffer +{ +public: + /** + * Creates a vertex buffer, settings its size, uploading its data, and setting its usage hint. + * + * @param size Size of the buffer's data, in bytes. + * @param data Pointer to data that will be copied into the buffer, or `nullptr` if no data is to be copied. + * @param usage Buffer usage hint. + */ + explicit vertex_buffer(std::size_t size, const void* data = nullptr, buffer_usage usage = buffer_usage::static_draw); + + /// Creates an empty vertex buffer. + vertex_buffer(); + + /// Destroys a vertex buffer. + ~vertex_buffer(); + + vertex_buffer(const vertex_buffer&) = delete; + vertex_buffer& operator=(const vertex_buffer&) = delete; + + /** + * Repurposes a vertex buffer, changing its usage hint, its size, and replacing its data. + * + * @param usage New usage hint for the buffer. + * @param size New size of the buffer's data, in bytes. + * @param data Pointer to data that will be copied into the buffer, or `nullptr` if no data is to be copied. + */ + void repurpose(buffer_usage usage, std::size_t size, const void* data = nullptr); + + /** + * Resizes the vertex buffer. + * + * @param size New size of the buffer's data, in bytes. + * @param data Pointer to data that will be copied into the buffer, or `nullptr` if no data is to be copied. + */ + void resize(std::size_t size, const void* data = nullptr); + + /** + * Writes data into the vertex buffer. + * + * @param offset Offset into the buffer's data, in bytes, where writing will begin. + * @param size Size of the write operation, in bytes. + * @param data Pointer to the data that will be written. + * + * @except std::out_of_range Vertex buffer write operation exceeded buffer bounds. + */ + void write(std::size_t offset, std::size_t size, const void* data); + + /** + * Reads a subset of the buffer's data from the GL and returns it to the application. + * + * @param offset Offset into the buffer's data, in bytes, where reading will begin. + * @param size Size of the data to read, in bytes. + * @param data Pointer to where the read bytes will be stored. + * + * @except std::out_of_range Vertex buffer read operation exceeded buffer bounds. + */ + void read(std::size_t offset, std::size_t size, void* data) const; + + /// Returns the size of the buffer's data, in bytes. + std::size_t get_size() const; + + /// Return's the buffer's usage hint. + buffer_usage get_usage() const; + +private: + friend class vertex_array; + + std::uint_fast32_t gl_buffer_id; + std::size_t size; + buffer_usage usage; +}; + +inline std::size_t vertex_buffer::get_size() const +{ + return size; +} + +inline buffer_usage vertex_buffer::get_usage() const +{ + return usage; +} + +} // namespace gl + +#endif // ANTKEEPER_GL_VERTEX_BUFFER_HPP diff --git a/src/i18n/i18n.hpp b/src/engine/i18n/i18n.hpp similarity index 100% rename from src/i18n/i18n.hpp rename to src/engine/i18n/i18n.hpp diff --git a/src/engine/i18n/string-map.cpp b/src/engine/i18n/string-map.cpp new file mode 100644 index 0000000..0535092 --- /dev/null +++ b/src/engine/i18n/string-map.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +/** + * Serializes a string map. + * + * @param[in] map String map to serialize. + * @param[in,out] ctx Serialize context. + * + * @throw serialize_error Write error. + */ +template <> +void serializer::serialize(const i18n::string_map& map, serialize_context& ctx) +{ + // Write number of entries + std::uint32_t size = static_cast(map.size()); + ctx.write32(reinterpret_cast(&size), 1); + + // Write entries + for (const auto& [key, value]: map) + { + // Write key + ctx.write32(reinterpret_cast(&key), 1); + + // Write string length + std::uint32_t length = static_cast(value.length()); + ctx.write32(reinterpret_cast(&length), 1); + + // Write string + ctx.write8(reinterpret_cast(value.data()), length); + } +} + +/** + * Deserializes a string map. + * + * @param[out] map String map to deserialize. + * @param[in,out] ctx Deserialize context. + * + * @throw deserialize_error Read error. + */ +template <> +void deserializer::deserialize(i18n::string_map& map, deserialize_context& ctx) +{ + map.clear(); + + // Read number of entries + std::uint32_t size = 0; + ctx.read32(reinterpret_cast(&size), 1); + + // Read entries + for (std::uint32_t i = 0; i < size; ++i) + { + // Read key + std::uint32_t key = 0; + ctx.read32(reinterpret_cast(&key), 1); + + // Read string length + std::uint32_t length = 0; + ctx.read32(reinterpret_cast(&length), 1); + + // Insert empty string into map + auto [iterator, inserted] = map.emplace + ( + std::piecewise_construct, + std::forward_as_tuple(key), + std::forward_as_tuple(static_cast(length), '\0') + ); + + // Read string + ctx.read8(reinterpret_cast(iterator->second.data()), length); + } +} diff --git a/src/i18n/string-map.hpp b/src/engine/i18n/string-map.hpp similarity index 100% rename from src/i18n/string-map.hpp rename to src/engine/i18n/string-map.hpp diff --git a/src/i18n/string-table.hpp b/src/engine/i18n/string-table.hpp similarity index 100% rename from src/i18n/string-table.hpp rename to src/engine/i18n/string-table.hpp diff --git a/src/input/action-events.hpp b/src/engine/input/action-events.hpp similarity index 100% rename from src/input/action-events.hpp rename to src/engine/input/action-events.hpp diff --git a/src/engine/input/action-map.cpp b/src/engine/input/action-map.cpp new file mode 100644 index 0000000..728df9e --- /dev/null +++ b/src/engine/input/action-map.cpp @@ -0,0 +1,454 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include +#include +#include + +namespace input { + +action_map::action_map(): + event_queue(nullptr), + enabled(false) +{} + +void action_map::enable() +{ + if (!enabled) + { + if (event_queue) + { + subscribe(); + } + + enabled = true; + } +} + +void action_map::disable() +{ + if (enabled) + { + if (event_queue) + { + unsubscribe(); + } + + enabled = false; + } +} + +void action_map::set_event_queue(event::queue* queue) +{ + if (event_queue != queue) + { + if (enabled) + { + if (event_queue) + { + unsubscribe(); + } + + event_queue = queue; + + if (event_queue) + { + subscribe(); + } + } + else + { + event_queue = queue; + } + } +} + +void action_map::add_mapping(action& action, const mapping& mapping) +{ + switch (mapping.get_mapping_type()) + { + case mapping_type::gamepad_axis: + add_gamepad_axis_mapping(action, static_cast(mapping)); + break; + + case mapping_type::gamepad_button: + add_gamepad_button_mapping(action, static_cast(mapping)); + break; + + case mapping_type::key: + add_key_mapping(action, static_cast(mapping)); + break; + + case mapping_type::mouse_button: + add_mouse_button_mapping(action, static_cast(mapping)); + break; + + case mapping_type::mouse_motion: + add_mouse_motion_mapping(action, static_cast(mapping)); + break; + + case mapping_type::mouse_scroll: + add_mouse_scroll_mapping(action, static_cast(mapping)); + break; + + default: + //std::unreachable(); + break; + } +} + +void action_map::add_gamepad_axis_mapping(action& action, gamepad_axis_mapping mapping) +{ + gamepad_axis_mappings.emplace_back(&action, std::move(mapping)); +} + +void action_map::add_gamepad_button_mapping(action& action, gamepad_button_mapping mapping) +{ + gamepad_button_mappings.emplace_back(&action, std::move(mapping)); +} + +void action_map::add_key_mapping(action& action, key_mapping mapping) +{ + key_mappings.emplace_back(&action, std::move(mapping)); +} + +void action_map::add_mouse_button_mapping(action& action, mouse_button_mapping mapping) +{ + mouse_button_mappings.emplace_back(&action, std::move(mapping)); +} + +void action_map::add_mouse_motion_mapping(action& action, mouse_motion_mapping mapping) +{ + mouse_motion_mappings.emplace_back(&action, std::move(mapping)); +} + +void action_map::add_mouse_scroll_mapping(action& action, mouse_scroll_mapping mapping) +{ + mouse_scroll_mappings.emplace_back(&action, std::move(mapping)); +} + +void action_map::remove_mappings(const action& action, mapping_type type) +{ + auto predicate = [&](const auto& tuple) -> bool + { + return std::get<0>(tuple) == &action; + }; + + switch (type) + { + case mapping_type::gamepad_axis: + std::erase_if(gamepad_axis_mappings, predicate); + break; + + case mapping_type::gamepad_button: + std::erase_if(gamepad_button_mappings, predicate); + break; + + case mapping_type::key: + std::erase_if(key_mappings, predicate); + break; + + case mapping_type::mouse_button: + std::erase_if(mouse_button_mappings, predicate); + break; + + case mapping_type::mouse_motion: + std::erase_if(mouse_motion_mappings, predicate); + break; + + case mapping_type::mouse_scroll: + std::erase_if(mouse_scroll_mappings, predicate); + break; + + default: + //std::unreachable(); + break; + } +} + +void action_map::remove_mappings(const action& action) +{ + auto predicate = [&](const auto& tuple) -> bool + { + return std::get<0>(tuple) == &action; + }; + + std::erase_if(gamepad_axis_mappings, predicate); + std::erase_if(gamepad_button_mappings, predicate); + std::erase_if(key_mappings, predicate); + std::erase_if(mouse_button_mappings, predicate); + std::erase_if(mouse_motion_mappings, predicate); + std::erase_if(mouse_scroll_mappings, predicate); +} + +void action_map::remove_mappings() +{ + gamepad_axis_mappings.clear(); + gamepad_button_mappings.clear(); + key_mappings.clear(); + mouse_button_mappings.clear(); + mouse_motion_mappings.clear(); + mouse_scroll_mappings.clear(); +} + +void action_map::handle_gamepad_axis_moved(const gamepad_axis_moved_event& event) +{ + for (const auto& [action, mapping]: gamepad_axis_mappings) + { + if (mapping.axis == event.axis && + (!mapping.gamepad || mapping.gamepad == event.gamepad)) + { + if (std::signbit(event.position) == mapping.direction) + { + action->evaluate(std::abs(event.position)); + } + else + { + action->evaluate(0.0f); + } + } + } +} + +void action_map::handle_gamepad_button_pressed(const gamepad_button_pressed_event& event) +{ + for (const auto& [action, mapping]: gamepad_button_mappings) + { + if (mapping.button == event.button && + (!mapping.gamepad || mapping.gamepad == event.gamepad)) + { + action->evaluate(1.0f); + } + } +} + +void action_map::handle_gamepad_button_released(const gamepad_button_released_event& event) +{ + for (const auto& [action, mapping]: gamepad_button_mappings) + { + if (mapping.button == event.button && + (!mapping.gamepad || mapping.gamepad == event.gamepad)) + { + action->evaluate(0.0f); + } + } +} + +void action_map::handle_key_pressed(const key_pressed_event& event) +{ + for (const auto& [action, mapping]: key_mappings) + { + if (mapping.scancode == event.scancode && + (!mapping.keyboard || mapping.keyboard == event.keyboard) && + (!mapping.modifiers || (mapping.modifiers & event.modifiers))) + { + if (!event.repeat) + { + action->evaluate(1.0f); + } + else if (mapping.repeat) + { + action->evaluate(0.0f); + action->evaluate(1.0f); + } + } + } +} + +void action_map::handle_key_released(const key_released_event& event) +{ + for (const auto& [action, mapping]: key_mappings) + { + if (mapping.scancode == event.scancode && + (!mapping.keyboard || mapping.keyboard == event.keyboard)) + { + action->evaluate(0.0f); + } + } +} + +void action_map::handle_mouse_moved(const mouse_moved_event& event) +{ + for (const auto& [action, mapping]: mouse_motion_mappings) + { + if (!mapping.mouse || mapping.mouse == event.mouse) + { + const float difference = static_cast(event.difference[static_cast>(mapping.axis)]); + + if (difference && std::signbit(difference) == mapping.direction) + { + action->evaluate(std::abs(difference)); + action->evaluate(0.0f); + } + } + } +} + +void action_map::handle_mouse_scrolled(const mouse_scrolled_event& event) +{ + for (const auto& [action, mapping]: mouse_scroll_mappings) + { + if (!mapping.mouse || mapping.mouse == event.mouse) + { + const auto velocity = event.velocity[static_cast>(mapping.axis)]; + + if (velocity && std::signbit(velocity) == mapping.direction) + { + action->evaluate(std::abs(velocity)); + action->evaluate(0.0f); + } + } + } +} + +void action_map::handle_mouse_button_pressed(const mouse_button_pressed_event& event) +{ + for (const auto& [action, mapping]: mouse_button_mappings) + { + if (mapping.button == event.button && + (!mapping.mouse || mapping.mouse == event.mouse)) + { + action->evaluate(1.0f); + } + } +} + +void action_map::handle_mouse_button_released(const mouse_button_released_event& event) +{ + for (const auto& [action, mapping]: mouse_button_mappings) + { + if (mapping.button == event.button && + (!mapping.mouse || mapping.mouse == event.mouse)) + { + action->evaluate(0.0f); + } + } +} + +std::vector action_map::get_gamepad_axis_mappings(const action& action) const +{ + std::vector mappings; + + for (const auto& [mapped_action, mapping]: gamepad_axis_mappings) + { + if (mapped_action == &action) + { + mappings.emplace_back(mapping); + } + } + + return mappings; +} + +std::vector action_map::get_gamepad_button_mappings(const action& action) const +{ + std::vector mappings; + + for (const auto& [mapped_action, mapping]: gamepad_button_mappings) + { + if (mapped_action == &action) + { + mappings.emplace_back(mapping); + } + } + + return mappings; +} + +std::vector action_map::get_key_mappings(const action& action) const +{ + std::vector mappings; + + for (const auto& [mapped_action, mapping]: key_mappings) + { + if (mapped_action == &action) + { + mappings.emplace_back(mapping); + } + } + + return mappings; +} + +std::vector action_map::get_mouse_button_mappings(const action& action) const +{ + std::vector mappings; + + for (const auto& [mapped_action, mapping]: mouse_button_mappings) + { + if (mapped_action == &action) + { + mappings.emplace_back(mapping); + } + } + + return mappings; +} + +std::vector action_map::get_mouse_motion_mappings(const action& action) const +{ + std::vector mappings; + + for (const auto& [mapped_action, mapping]: mouse_motion_mappings) + { + if (mapped_action == &action) + { + mappings.emplace_back(mapping); + } + } + + return mappings; +} + +std::vector action_map::get_mouse_scroll_mappings(const action& action) const +{ + std::vector mappings; + + for (const auto& [mapped_action, mapping]: mouse_scroll_mappings) + { + if (mapped_action == &action) + { + mappings.emplace_back(mapping); + } + } + + return mappings; +} + +void action_map::subscribe() +{ + subscriptions.emplace_back(event_queue->subscribe(std::bind_front(&action_map::handle_gamepad_axis_moved, this))); + subscriptions.emplace_back(event_queue->subscribe(std::bind_front(&action_map::handle_gamepad_button_pressed, this))); + subscriptions.emplace_back(event_queue->subscribe(std::bind_front(&action_map::handle_gamepad_button_released, this))); + subscriptions.emplace_back(event_queue->subscribe(std::bind_front(&action_map::handle_key_pressed, this))); + subscriptions.emplace_back(event_queue->subscribe(std::bind_front(&action_map::handle_key_released, this))); + subscriptions.emplace_back(event_queue->subscribe(std::bind_front(&action_map::handle_mouse_button_pressed, this))); + subscriptions.emplace_back(event_queue->subscribe(std::bind_front(&action_map::handle_mouse_button_released, this))); + subscriptions.emplace_back(event_queue->subscribe(std::bind_front(&action_map::handle_mouse_moved, this))); + subscriptions.emplace_back(event_queue->subscribe(std::bind_front(&action_map::handle_mouse_scrolled, this))); +} + +void action_map::unsubscribe() +{ + subscriptions.clear(); +} + +} // namespace input diff --git a/src/engine/input/action-map.hpp b/src/engine/input/action-map.hpp new file mode 100644 index 0000000..3e3b35f --- /dev/null +++ b/src/engine/input/action-map.hpp @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEER_INPUT_ACTION_MAP_HPP +#define ANTKEEER_INPUT_ACTION_MAP_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace input { + +/** + * Maps input to a set of contextually-related actions. + */ +class action_map +{ +public: + /** + * Constructs an action map. + */ + action_map(); + + /** + * Enables the mapping of input events to actions. + */ + void enable(); + + /** + * Disables the mapping of input events to actions. + */ + void disable(); + + /** + * Sets the event queue from which this action map will receive input events. + * + * @param queue Event queue from which this action map will receive input events. + */ + void set_event_queue(event::queue* queue); + + /** + * Maps input to an action. + * + * @param action Action to which input will be mapped. + * @param mapping Input mapping to add. + */ + /// @{ + void add_mapping(action& action, const mapping& mapping); + void add_gamepad_axis_mapping(action& action, gamepad_axis_mapping mapping); + void add_gamepad_button_mapping(action& action, gamepad_button_mapping mapping); + void add_key_mapping(action& action, key_mapping mapping); + void add_mouse_button_mapping(action& action, mouse_button_mapping mapping); + void add_mouse_motion_mapping(action& action, mouse_motion_mapping mapping); + void add_mouse_scroll_mapping(action& action, mouse_scroll_mapping mapping); + /// @} + + /** + * Unmaps input from an action. + * + * @param action Action from which input will be unmapped. + * @param type Type of input mapping to remove. + */ + void remove_mappings(const action& action, mapping_type type); + + /** + * Unmaps all input from an action. + * + * @param action Action from which input will be unmapped. + */ + void remove_mappings(const action& action); + + /** + * Unmaps all input from all actions in the action map. + */ + void remove_mappings(); + + /** + * Returns all of the gamepad axis mappings associated with an action. + * + * @param action Action with which associated mappings will be returned. + */ + std::vector get_gamepad_axis_mappings(const action& action) const; + + /** + * Returns all of the gamepad button mappings associated with an action. + * + * @param action Action with which associated mappings will be returned. + */ + std::vector get_gamepad_button_mappings(const action& action) const; + + /** + * Returns all of the key mappings associated with an action. + * + * @param action Action with which associated mappings will be returned. + */ + std::vector get_key_mappings(const action& action) const; + + /** + * Returns all of the mouse button mappings associated with an action. + * + * @param action Action with which associated mappings will be returned. + */ + std::vector get_mouse_button_mappings(const action& action) const; + + /** + * Returns all of the mouse motion mappings associated with an action. + * + * @param action Action with which associated mappings will be returned. + */ + std::vector get_mouse_motion_mappings(const action& action) const; + + /** + * Returns all of the mouse scroll associated with an action. + * + * @param action Action with which associated mappings will be returned. + */ + std::vector get_mouse_scroll_mappings(const action& action) const; + +private: + void subscribe(); + void unsubscribe(); + + void handle_gamepad_axis_moved(const gamepad_axis_moved_event& event); + void handle_gamepad_button_pressed(const gamepad_button_pressed_event& event); + void handle_gamepad_button_released(const gamepad_button_released_event& event); + void handle_key_pressed(const key_pressed_event& event); + void handle_key_released(const key_released_event& event); + void handle_mouse_button_pressed(const mouse_button_pressed_event& event); + void handle_mouse_button_released(const mouse_button_released_event& event); + void handle_mouse_moved(const mouse_moved_event& event); + void handle_mouse_scrolled(const mouse_scrolled_event& event); + + event::queue* event_queue; + bool enabled; + std::vector> subscriptions; + std::vector> gamepad_axis_mappings; + std::vector> gamepad_button_mappings; + std::vector> key_mappings; + std::vector> mouse_button_mappings; + std::vector> mouse_motion_mappings; + std::vector> mouse_scroll_mappings; +}; + +} // namespace input + +#endif // ANTKEEER_INPUT_ACTION_MAP_HPP diff --git a/src/engine/input/action.cpp b/src/engine/input/action.cpp new file mode 100644 index 0000000..cd11c21 --- /dev/null +++ b/src/engine/input/action.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include + +namespace input { + +static bool default_threshold_function(float x) noexcept +{ + return x > 0.0f; +} + +action::action(): + threshold_function(default_threshold_function), + active(false), + activated_event{this}, + active_event{this, 0.0f}, + deactivated_event{this} +{} + +void action::set_threshold_function(const threshold_function_type& function) +{ + threshold_function = function; +} + +void action::evaluate(float value) +{ + // Store activation state + const bool was_active = active; + + // Re-evaluate activation state + active = threshold_function(value); + + // Emit events + if (active) + { + if (!was_active) + { + activated_publisher.publish(activated_event); + } + + active_event.input_value = value; + active_publisher.publish(active_event); + } + else + { + if (was_active) + { + deactivated_publisher.publish(deactivated_event); + } + } +} + +void action::reset() +{ + active = false; +} + +} // namespace input diff --git a/src/engine/input/action.hpp b/src/engine/input/action.hpp new file mode 100644 index 0000000..bd6d836 --- /dev/null +++ b/src/engine/input/action.hpp @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_INPUT_ACTION_HPP +#define ANTKEEPER_INPUT_ACTION_HPP + +#include +#include +#include + +namespace input { + +/** + * Evaluates an activation state given input values and publishes events on activation state changes. + */ +class action +{ +public: + /** + * Threshold function type. + * + * Given an input value, returns `true` if the action should be considered active, and `false` otherwise. + */ + typedef std::function threshold_function_type; + + /// Constructs an action. + action(); + + /** + * Sets the threshold function. + * + * @param function Threshold function. + */ + void set_threshold_function(const threshold_function_type& function); + + /** + * Evaluates the activation state of the action, according to its threshold function and an input value. + * + * @param value Input value. + */ + void evaluate(float value); + + /** + * Resets the activation state of the action without publishing any events. + */ + void reset(); + + /// Returns the threshold function. + [[nodiscard]] inline const threshold_function_type& get_threshold_function() const noexcept + { + return threshold_function; + } + + /// Returns `true` if the action is active, `false` otherwise. + [[nodiscard]] inline bool is_active() const noexcept + { + return active; + } + + /// Returns the channel through which action activated events are published. + [[nodiscard]] inline ::event::channel& get_activated_channel() noexcept + { + return activated_publisher.channel(); + } + + /// Returns the channel through which action active events are published. + [[nodiscard]] inline ::event::channel& get_active_channel() noexcept + { + return active_publisher.channel(); + } + + /// Returns the channel through which action deactivated events are published. + [[nodiscard]] inline ::event::channel& get_deactivated_channel() noexcept + { + return deactivated_publisher.channel(); + } + +private: + threshold_function_type threshold_function; + bool active; + + action_activated_event activated_event; + action_active_event active_event; + action_deactivated_event deactivated_event; + + ::event::publisher activated_publisher; + ::event::publisher active_publisher; + ::event::publisher deactivated_publisher; +}; + +} // namespace input + +#endif // ANTKEEPER_INPUT_ACTION_HPP diff --git a/src/input/application-events.hpp b/src/engine/input/application-events.hpp similarity index 100% rename from src/input/application-events.hpp rename to src/engine/input/application-events.hpp diff --git a/src/input/device-events.hpp b/src/engine/input/device-events.hpp similarity index 100% rename from src/input/device-events.hpp rename to src/engine/input/device-events.hpp diff --git a/src/input/device-type.hpp b/src/engine/input/device-type.hpp similarity index 100% rename from src/input/device-type.hpp rename to src/engine/input/device-type.hpp diff --git a/src/engine/input/device.cpp b/src/engine/input/device.cpp new file mode 100644 index 0000000..0a50648 --- /dev/null +++ b/src/engine/input/device.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include + +namespace input { + +device::device(): + connected(true) +{} + +void device::connect() +{ + connected = true; + connected_publisher.publish({this}); +} + +void device::disconnect() +{ + connected = false; + disconnected_publisher.publish({this}); +} + +void device::set_uuid(const ::uuid& id) +{ + uuid = id; +} + +} // namespace input diff --git a/src/engine/input/device.hpp b/src/engine/input/device.hpp new file mode 100644 index 0000000..9bd8ca1 --- /dev/null +++ b/src/engine/input/device.hpp @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_INPUT_DEVICE_HPP +#define ANTKEEPER_INPUT_DEVICE_HPP + +#include +#include +#include +#include +#include +#include + +namespace input { + +/** + * Abstract base class for virtual devices that generate input events. + */ +class device +{ +public: + /// Constructs an input device. + device(); + + /// Destructs an input device. + virtual ~device() = default; + + /** + * Simulates the device being connected. + */ + void connect(); + + /** + * Simulates the device being disconnected. + * + * @note Disconnected devices can still generate input events. + */ + void disconnect(); + + /// Returns `true` if the device is currently connected. + [[nodiscard]] inline bool is_connected() const noexcept + { + return connected; + } + + /** + * Sets the universally unique identifier (UUID) of this input device. + * + * @param id UUID. + */ + void set_uuid(const ::uuid& id); + + /// Returns the universally unique identifier (UUID) of this input device. + [[nodiscard]] inline const ::uuid& get_uuid() const noexcept + { + return uuid; + } + + /// Returns the channel through which device connected events are published. + [[nodiscard]] inline ::event::channel& get_connected_channel() noexcept + { + return connected_publisher.channel(); + } + + /// Returns the channel through which device disconnected events are published. + [[nodiscard]] inline ::event::channel& get_disconnected_channel() noexcept + { + return disconnected_publisher.channel(); + } + + /// Returns the input device type. + [[nodiscard]] virtual constexpr device_type get_device_type() const noexcept = 0; + +private: + ::uuid uuid; + bool connected; + + ::event::publisher connected_publisher; + ::event::publisher disconnected_publisher; +}; + +} // namespace input + +#endif // ANTKEEPER_INPUT_DEVICE_HPP diff --git a/src/input/gamepad-axis.hpp b/src/engine/input/gamepad-axis.hpp similarity index 100% rename from src/input/gamepad-axis.hpp rename to src/engine/input/gamepad-axis.hpp diff --git a/src/input/gamepad-button.hpp b/src/engine/input/gamepad-button.hpp similarity index 100% rename from src/input/gamepad-button.hpp rename to src/engine/input/gamepad-button.hpp diff --git a/src/engine/input/gamepad-events.hpp b/src/engine/input/gamepad-events.hpp new file mode 100644 index 0000000..84c3ce0 --- /dev/null +++ b/src/engine/input/gamepad-events.hpp @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_INPUT_GAMEPAD_EVENTS_HPP +#define ANTKEEPER_INPUT_GAMEPAD_EVENTS_HPP + +#include +#include + +namespace input { + +class gamepad; + +/** + * Event generated when a gamepad button has been pressed. + */ +struct gamepad_button_pressed_event +{ + /// Gamepad that generated the event. + gamepad* gamepad; + + /// Gamepad button being pressed. + gamepad_button button; +}; + +/** + * Event generated when a gamepad button has been released. + */ +struct gamepad_button_released_event +{ + /// Gamepad that generated the event. + gamepad* gamepad; + + /// Gamepad button being released. + gamepad_button button; +}; + +/** + * Event generated when a gamepad axis has been moved. + */ +struct gamepad_axis_moved_event +{ + /// Gamepad that generated the event. + gamepad* gamepad; + + /// Gamepad axis being moved. + gamepad_axis axis; + + /// Position of the gamepad axis, on `[-1, 1]`. + float position; +}; + +} // namespace input + +#endif // ANTKEEPER_INPUT_GAMEPAD_EVENTS_HPP diff --git a/src/engine/input/gamepad.cpp b/src/engine/input/gamepad.cpp new file mode 100644 index 0000000..7084c1b --- /dev/null +++ b/src/engine/input/gamepad.cpp @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include +#include +#include + +namespace input { + +gamepad::gamepad(): + left_deadzone_cross(true), + right_deadzone_cross(true), + left_deadzone_roundness(0.0f), + right_deadzone_roundness(0.0f) +{ + for (int i = 0; i < 6; ++i) + { + axis_positions[i] = 0.0f; + axis_activation_min[i] = 0.15f; + axis_activation_max[i] = 0.98f; + axis_response_curves[i] = gamepad_response_curve::linear; + } +} + +void gamepad::set_activation_threshold(gamepad_axis axis, float min, float max) +{ + axis_activation_min[static_cast>(axis)] = min; + axis_activation_max[static_cast>(axis)] = max; +} + +void gamepad::set_response_curve(gamepad_axis axis, gamepad_response_curve curve) +{ + axis_response_curves[static_cast>(axis)] = curve; +} + +void gamepad::set_left_deadzone_cross(bool cross) +{ + left_deadzone_cross = cross; +} + +void gamepad::set_right_deadzone_cross(bool cross) +{ + right_deadzone_cross = cross; +} + +void gamepad::set_left_deadzone_roundness(float roundness) +{ + left_deadzone_roundness = roundness; +} + +void gamepad::set_right_deadzone_roundness(float roundness) +{ + right_deadzone_roundness = roundness; +} + +void gamepad::press(gamepad_button button) +{ + button_pressed_publisher.publish({this, button}); +} + +void gamepad::release(gamepad_button button) +{ + button_released_publisher.publish({this, button}); +} + +void gamepad::move(gamepad_axis axis, float position) +{ + const auto axis_index = static_cast>(axis); + + /// @TODO Support arbitrary number of gamepad axes. + if (axis_index >= 6) + return; + + // Update axis position + axis_positions[axis_index] = position; + + switch (axis) + { + case gamepad_axis::left_stick_x: + case gamepad_axis::left_stick_y: + if (left_deadzone_cross) + handle_axial_motion(axis); + else + handle_biaxial_motion(gamepad_axis::left_stick_x, gamepad_axis::left_stick_y); + break; + + case gamepad_axis::right_stick_x: + case gamepad_axis::right_stick_y: + if (right_deadzone_cross) + handle_axial_motion(axis); + else + handle_biaxial_motion(gamepad_axis::right_stick_x, gamepad_axis::right_stick_y); + break; + + case gamepad_axis::left_trigger: + case gamepad_axis::right_trigger: + default: + handle_axial_motion(axis); + break; + } +} + +void gamepad::handle_axial_motion(gamepad_axis axis) +{ + const auto axis_index = static_cast>(axis); + + // Get axis parameters + const float activation_min = axis_activation_min[axis_index]; + const float activation_max = axis_activation_max[axis_index]; + const float axis_position = axis_positions[axis_index]; + const gamepad_response_curve response_curve = axis_response_curves[axis_index]; + + // Remap axis position + float remapped_position = 0.0f; + if (std::abs(axis_position) > activation_min) + { + // Remap position according to activation thresholds and clamp to `[0, 1]`. + float response = math::map(std::abs(axis_position), activation_min, activation_max, 0.0f, 1.0f); + response = std::clamp(response, 0.0f, 1.0f); + + // Remap position according to axis response curve + response = curve_response(axis, response); + + // Restore sign of axis motion + response = (axis_position < 0.0f) ? -response : response; + + remapped_position = response; + } + + axis_moved_publisher.publish({this, axis, remapped_position}); +} + +void gamepad::handle_biaxial_motion(gamepad_axis axis_x, gamepad_axis axis_y) +{ + // Get axis parameters + const int x_axis_index = static_cast>(axis_x); + const int y_axis_index = static_cast>(axis_y); + const float x_activation_min = axis_activation_min[x_axis_index]; + const float x_activation_max = axis_activation_max[x_axis_index]; + const float y_activation_min = axis_activation_min[y_axis_index]; + const float y_activation_max = axis_activation_max[y_axis_index]; + const float x_axis_position = axis_positions[x_axis_index]; + const float y_axis_position = axis_positions[y_axis_index]; + const gamepad_response_curve x_response_curve = axis_response_curves[x_axis_index]; + const gamepad_response_curve y_response_curve = axis_response_curves[y_axis_index]; + const float deadzone_roundness = (axis_x == gamepad_axis::left_stick_x) ? left_deadzone_roundness : right_deadzone_roundness; + + const float radius = std::min(x_activation_min, y_activation_min) * deadzone_roundness; + const float dx = std::max(0.0f, std::abs(x_axis_position) - x_activation_min + radius); + const float dy = std::max(0.0f, std::abs(y_axis_position) - y_activation_min + radius); + const float distance = std::sqrt(dx * dx + dy * dy) - radius; + + if (distance > 0.0f) + { + const float nx = std::abs(x_axis_position) / distance; + const float ny = std::abs(y_axis_position) / distance; + const float ndx = (distance - x_activation_min) / (x_activation_max - x_activation_min); + const float ndy = (distance - y_activation_min) / (y_activation_max - y_activation_min); + + float response_x = std::clamp(nx * ndx, 0.0f, 1.0f); + float response_y = std::clamp(ny * ndy, 0.0f, 1.0f); + + response_x = curve_response(axis_x, response_x); + response_y = curve_response(axis_y, response_y); + + // Restore signs of axis motions + response_x = (x_axis_position < 0.0f) ? -response_x : response_x; + response_y = (y_axis_position < 0.0f) ? -response_y : response_y; + + axis_moved_publisher.publish({this, axis_x, response_x}); + axis_moved_publisher.publish({this, axis_y, response_y}); + } + else + { + axis_moved_publisher.publish({this, axis_x, 0.0f}); + axis_moved_publisher.publish({this, axis_y, 0.0f}); + } +} + +float gamepad::curve_response(gamepad_axis axis, float response) const +{ + const auto axis_index = static_cast>(axis); + const gamepad_response_curve response_curve = axis_response_curves[axis_index]; + + switch (response_curve) + { + case gamepad_response_curve::linear: + break; + + case gamepad_response_curve::square: + response = response * response; + break; + + case gamepad_response_curve::cube: + response = response * response * response; + break; + } + + return response; +} + +} // namespace input diff --git a/src/engine/input/gamepad.hpp b/src/engine/input/gamepad.hpp new file mode 100644 index 0000000..1b64670 --- /dev/null +++ b/src/engine/input/gamepad.hpp @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_INPUT_GAMEPAD_HPP +#define ANTKEEPER_INPUT_GAMEPAD_HPP + +#include +#include +#include +#include +#include + +namespace input { + +/// Gamepad axis activation response curves. +enum class gamepad_response_curve +{ + /// Linear response curve. + linear, + + /// Squared response curve. + square, + + /// Cubed response curve. + cube +}; + +/** + * A virtual gamepad which generates gamepad-related input events. + */ +class gamepad: public device +{ +public: + /** + * Constructs a gamepad input device. + */ + gamepad(); + + /// Destructs a gamepad input device. + virtual ~gamepad() = default; + + /** + * Sets the activation threshold for a gamepad axis. + * + * @param axis Gamepad axis. + * @param min Axis minimum activation threshold. + * @param max Axis maximum activation threshold. + */ + void set_activation_threshold(gamepad_axis axis, float min, float max); + + /** + * Sets the activation response curve of an axis. + * + * @param axis Gamepad axis. + * @param curve Activation response curve. + */ + void set_response_curve(gamepad_axis axis, gamepad_response_curve curve); + + /** + * Sets the type of deadzone shape for the axes on the left stick. + * + * @param cross If `true`, the x and y axes are independently activated, if `false`, activation of the x and y axes are dependent on their combined magnitude. + */ + void set_left_deadzone_cross(bool cross); + + /** + * Sets the type of deadzone shape for the axes on the right stick. + * + * @param cross If `true`, the x and y axes are independently activated, if `false`, activation of the x and y axes are dependent on their combined magnitude. + */ + void set_right_deadzone_cross(bool cross); + + /** + * Sets the roundness of the deadzone for the axes on the left stick. + * + * @param roundness Roundness of the deadzone shape for non-cross deadzones. A value of `0.0` results in a square deadzone, while a value of `1.0` results in a circular deadzone. Values between `0.0` and `1.0` result in a rounded rectangle deadzone. + */ + void set_left_deadzone_roundness(float roundness); + + /** + * Sets the roundness of the deadzone for the axes on the right stick. + * + * @param roundness Roundness of the deadzone shape for non-cross deadzones. A value of `0.0` results in a square deadzone, while a value of `1.0` results in a circular deadzone. Values between `0.0` and `1.0` result in a rounded rectangle deadzone. + */ + void set_right_deadzone_roundness(float roundness); + + /** + * Simulates a gamepad button press. + * + * @param button Button to press. + */ + void press(gamepad_button button); + + /** + * Simulates a gamepad button release. + * + * @param button Button to release. + */ + void release(gamepad_button button); + + /** + * Simulates a gamepad axis movement. + * + * @param axis Gamepad axis. + * @param position Position on the axis, on `[-1, 1]`. + */ + void move(gamepad_axis axis, float position); + + /// Returns the channel through which gamepad button pressed events are published. + [[nodiscard]] inline ::event::channel& get_button_pressed_channel() noexcept + { + return button_pressed_publisher.channel(); + } + + /// Returns the channel through which gamepad button released events are published. + [[nodiscard]] inline ::event::channel& get_button_released_channel() noexcept + { + return button_released_publisher.channel(); + } + + /// Returns the channel through which gamepad axis moved events are published. + [[nodiscard]] inline ::event::channel& get_axis_moved_channel() noexcept + { + return axis_moved_publisher.channel(); + } + + /// Returns device_type::gamepad. + [[nodiscard]] inline virtual constexpr device_type get_device_type() const noexcept + { + return device_type::gamepad; + } + +private: + void handle_axial_motion(gamepad_axis axis); + void handle_biaxial_motion(gamepad_axis axis_x, gamepad_axis axis_y); + float curve_response(gamepad_axis axis, float response) const; + + float axis_positions[6]; + float axis_activation_min[6]; + float axis_activation_max[6]; + gamepad_response_curve axis_response_curves[6]; + bool left_deadzone_cross; + bool right_deadzone_cross; + float left_deadzone_roundness; + float right_deadzone_roundness; + + ::event::publisher button_pressed_publisher; + ::event::publisher button_released_publisher; + ::event::publisher axis_moved_publisher; +}; + +} // namespace input + +#endif // ANTKEEPER_INPUT_GAMEPAD_HPP diff --git a/src/input/input.hpp b/src/engine/input/input.hpp similarity index 100% rename from src/input/input.hpp rename to src/engine/input/input.hpp diff --git a/src/engine/input/keyboard-events.hpp b/src/engine/input/keyboard-events.hpp new file mode 100644 index 0000000..bddafc8 --- /dev/null +++ b/src/engine/input/keyboard-events.hpp @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_INPUT_KEYBOARD_EVENTS_HPP +#define ANTKEEPER_INPUT_KEYBOARD_EVENTS_HPP + +#include +#include +#include + +namespace input { + +class keyboard; + +/** + * Event generated when a keyboard key has been pressed. + */ +struct key_pressed_event +{ + /// Keyboard that generated the event. + keyboard* keyboard; + + /// Scancode of the key being pressed. + scancode scancode; + + /// Bit mask containing the active modifier keys. + std::uint16_t modifiers; + + /// `true` if the key press was generated by a key repeat, `false` otherwise. + bool repeat; +}; + +/** + * Event generated when a keyboard key has been released. + */ +struct key_released_event +{ + /// Keyboard that generated the event. + keyboard* keyboard; + + /// Scancode of the key being released. + scancode scancode; + + /// Bit mask containing the active modifier keys. + std::uint16_t modifiers; +}; + +} // namespace input + +#endif // ANTKEEPER_INPUT_KEYBOARD_EVENTS_HPP diff --git a/src/engine/input/keyboard.cpp b/src/engine/input/keyboard.cpp new file mode 100644 index 0000000..ad9d793 --- /dev/null +++ b/src/engine/input/keyboard.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include + +namespace input { + +void keyboard::press(scancode scancode, std::uint16_t modifiers, bool repeat) +{ + key_pressed_publisher.publish({this, scancode, modifiers, repeat}); +} + +void keyboard::release(scancode scancode, std::uint16_t modifiers) +{ + key_released_publisher.publish({this, scancode, modifiers}); +} + +} // namespace input diff --git a/src/engine/input/keyboard.hpp b/src/engine/input/keyboard.hpp new file mode 100644 index 0000000..cbf28ff --- /dev/null +++ b/src/engine/input/keyboard.hpp @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_INPUT_KEYBOARD_HPP +#define ANTKEEPER_INPUT_KEYBOARD_HPP + +#include +#include +#include +#include +#include + +namespace input { + +/** + * A virtual keyboard which generates keyboard-related input events. + */ +class keyboard: public device +{ +public: + /** + * Constructs a keyboard input device. + */ + keyboard() = default; + + /// Destructs a keyboard input device. + virtual ~keyboard() = default; + + /** + * Simulates a key press. + * + * @param scancode Scancode of the key to press. + * @param modifiers Bit mask containing the active modifier keys. + * @param repeat `true` if the key press is from a key repeat, `false` otherwise. + */ + void press(scancode scancode, std::uint16_t modifiers = modifier_key::none, bool repeat = false); + + /** + * Simulates a key release. + * + * @param scancode Scancode of the key to release. + * @param modifiers Bit mask containing the active modifier keys. + */ + void release(scancode scancode, std::uint16_t modifiers = modifier_key::none); + + /// Returns the channel through which key pressed events are published. + [[nodiscard]] inline ::event::channel& get_key_pressed_channel() noexcept + { + return key_pressed_publisher.channel(); + } + + /// Returns the channel through which key released events are published. + [[nodiscard]] inline ::event::channel& get_key_released_channel() noexcept + { + return key_released_publisher.channel(); + } + + /// Returns device_type::keyboard. + [[nodiscard]] inline virtual constexpr device_type get_device_type() const noexcept + { + return device_type::keyboard; + } + +private: + ::event::publisher key_pressed_publisher; + ::event::publisher key_released_publisher; +}; + +} // namespace input + +#endif // ANTKEEPER_INPUT_KEYBOARD_HPP diff --git a/src/engine/input/mapper.cpp b/src/engine/input/mapper.cpp new file mode 100644 index 0000000..40fb343 --- /dev/null +++ b/src/engine/input/mapper.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include + +namespace input { + +void mapper::connect(::event::queue& queue) +{ + subscriptions.emplace_back(queue.subscribe(std::bind_front(&mapper::handle_gamepad_axis_moved, this))); + subscriptions.emplace_back(queue.subscribe(std::bind_front(&mapper::handle_gamepad_button_pressed, this))); + subscriptions.emplace_back(queue.subscribe(std::bind_front(&mapper::handle_key_pressed, this))); + subscriptions.emplace_back(queue.subscribe(std::bind_front(&mapper::handle_mouse_button_pressed, this))); + subscriptions.emplace_back(queue.subscribe(std::bind_front(&mapper::handle_mouse_moved, this))); + subscriptions.emplace_back(queue.subscribe(std::bind_front(&mapper::handle_mouse_scrolled, this))); +} + +void mapper::disconnect() +{ + subscriptions.clear(); +} + +void mapper::handle_gamepad_axis_moved(const gamepad_axis_moved_event& event) +{ + if (std::abs(event.position) > 0.5f) + { + gamepad_axis_mapped_publisher.publish({gamepad_axis_mapping(event.gamepad, event.axis, std::signbit(event.position))}); + } +} + +void mapper::handle_gamepad_button_pressed(const gamepad_button_pressed_event& event) +{ + gamepad_button_mapped_publisher.publish({gamepad_button_mapping(event.gamepad, event.button)}); +} + +void mapper::handle_key_pressed(const key_pressed_event& event) +{ + if (!event.repeat) + { + key_mapped_publisher.publish({key_mapping(event.keyboard, event.scancode)}); + } +} + +void mapper::handle_mouse_button_pressed(const mouse_button_pressed_event& event) +{ + mouse_button_mapped_publisher.publish({mouse_button_mapping(event.mouse, event.button)}); +} + +void mapper::handle_mouse_moved(const mouse_moved_event& event) +{ + if (event.difference.x()) + { + mouse_motion_mapped_publisher.publish({mouse_motion_mapping(event.mouse, mouse_motion_axis::x, std::signbit(static_cast(event.difference.x())))}); + } + + if (event.difference.y()) + { + mouse_motion_mapped_publisher.publish({mouse_motion_mapping(event.mouse, mouse_motion_axis::y, std::signbit(static_cast(event.difference.y())))}); + } +} + +void mapper::handle_mouse_scrolled(const mouse_scrolled_event& event) +{ + if (event.velocity.x()) + { + mouse_scroll_mapped_publisher.publish({mouse_scroll_mapping(event.mouse, mouse_scroll_axis::x, std::signbit(event.velocity.x()))}); + } + + if (event.velocity.y()) + { + mouse_scroll_mapped_publisher.publish({mouse_scroll_mapping(event.mouse, mouse_scroll_axis::y, std::signbit(event.velocity.y()))}); + } +} + +} // namespace input diff --git a/src/engine/input/mapper.hpp b/src/engine/input/mapper.hpp new file mode 100644 index 0000000..ac96d90 --- /dev/null +++ b/src/engine/input/mapper.hpp @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_INPUT_MAPPER_HPP +#define ANTKEEPER_INPUT_MAPPER_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace input { + +/** + * Listens for input events and generates corresponding input mappings. + */ +class mapper +{ +public: + /** + * Connects the input event signals of an event queue to the mapper. + * + * @param queue Event queue to connect. + */ + void connect(::event::queue& queue); + + /** + * Disconnects all input event signals from the mapper. + */ + void disconnect(); + + /// Returns the channel through which gamepad axis mapped events are published. + [[nodiscard]] inline ::event::channel& get_gamepad_axis_mapped_channel() noexcept + { + return gamepad_axis_mapped_publisher.channel(); + } + + /// Returns the channel through which gamepad button mapped events are published. + [[nodiscard]] inline ::event::channel& get_gamepad_button_mapped_channel() noexcept + { + return gamepad_button_mapped_publisher.channel(); + } + + /// Returns the channel through which key mapped events are published. + [[nodiscard]] inline ::event::channel& get_key_mapped_channel() noexcept + { + return key_mapped_publisher.channel(); + } + + /// Returns the channel through which mouse button mapped events are published. + [[nodiscard]] inline ::event::channel& get_mouse_button_mapped_channel() noexcept + { + return mouse_button_mapped_publisher.channel(); + } + + /// Returns the channel through which mouse motion mapped events are published. + [[nodiscard]] inline ::event::channel& get_mouse_motion_mapped_channel() noexcept + { + return mouse_motion_mapped_publisher.channel(); + } + + /// Returns the channel through which mouse scroll mapped events are published. + [[nodiscard]] inline ::event::channel& get_mouse_scroll_mapped_channel() noexcept + { + return mouse_scroll_mapped_publisher.channel(); + } + +private: + void handle_gamepad_axis_moved(const gamepad_axis_moved_event& event); + void handle_gamepad_button_pressed(const gamepad_button_pressed_event& event); + void handle_key_pressed(const key_pressed_event& event); + void handle_mouse_button_pressed(const mouse_button_pressed_event& event); + void handle_mouse_moved(const mouse_moved_event& event); + void handle_mouse_scrolled(const mouse_scrolled_event& event); + + std::vector> subscriptions; + ::event::publisher gamepad_axis_mapped_publisher; + ::event::publisher gamepad_button_mapped_publisher; + ::event::publisher key_mapped_publisher; + ::event::publisher mouse_button_mapped_publisher; + ::event::publisher mouse_motion_mapped_publisher; + ::event::publisher mouse_scroll_mapped_publisher; +}; + +} // namespace input + +#endif // ANTKEEPER_INPUT_MAPPER_HPP diff --git a/src/engine/input/mapping-events.hpp b/src/engine/input/mapping-events.hpp new file mode 100644 index 0000000..cbe0346 --- /dev/null +++ b/src/engine/input/mapping-events.hpp @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_INPUT_MAPPING_EVENTS_HPP +#define ANTKEEPER_INPUT_MAPPING_EVENTS_HPP + +#include + +namespace input { + +/** + * Event generated when a gamepad axis mapping has been generated. + */ +struct gamepad_axis_mapped_event +{ + /// Gamepad axis mapping that was generated. + gamepad_axis_mapping mapping; +}; + +/** + * Event generated when a gamepad button mapping has been generated. + */ +struct gamepad_button_mapped_event +{ + /// Gamepad button mapping that was generated. + gamepad_button_mapping mapping; +}; + +/** + * Event generated when a key mapping has been generated. + */ +struct key_mapped_event +{ + /// Key mapping that was generated. + key_mapping mapping; +}; + +/** + * Event generated when a mouse button mapping has been generated. + */ +struct mouse_button_mapped_event +{ + /// Mouse button mapping that was generated. + mouse_button_mapping mapping; +}; + +/** + * Event generated when a mouse motion mapping has been generated. + */ +struct mouse_motion_mapped_event +{ + /// Mouse motion mapping that was generated. + mouse_motion_mapping mapping; +}; + +/** + * Event generated when a mouse scroll mapping has been generated. + */ +struct mouse_scroll_mapped_event +{ + /// Mouse scroll mapping that was generated. + mouse_scroll_mapping mapping; +}; + +} // namespace input + +#endif // ANTKEEPER_INPUT_MAPPING_EVENTS_HPP diff --git a/src/input/mapping-type.hpp b/src/engine/input/mapping-type.hpp similarity index 100% rename from src/input/mapping-type.hpp rename to src/engine/input/mapping-type.hpp diff --git a/src/engine/input/mapping.cpp b/src/engine/input/mapping.cpp new file mode 100644 index 0000000..03302dd --- /dev/null +++ b/src/engine/input/mapping.cpp @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include +#include +#include + +namespace input { + +gamepad_axis_mapping::gamepad_axis_mapping(input::gamepad* gamepad, gamepad_axis axis, bool direction): + gamepad(gamepad), + axis(axis), + direction(direction) +{} + +gamepad_button_mapping::gamepad_button_mapping(input::gamepad* gamepad, gamepad_button button): + gamepad(gamepad), + button(button) +{} + +key_mapping::key_mapping(input::keyboard* keyboard, input::scancode scancode, std::uint16_t modifiers, bool repeat): + keyboard(keyboard), + scancode(scancode), + repeat(repeat), + modifiers(modifiers) +{} + +mouse_button_mapping::mouse_button_mapping(input::mouse* mouse, mouse_button button): + mouse(mouse), + button(button) +{} + +mouse_motion_mapping::mouse_motion_mapping(input::mouse* mouse, mouse_motion_axis axis, bool direction): + mouse(mouse), + axis(axis), + direction(direction) +{} + +mouse_scroll_mapping::mouse_scroll_mapping(input::mouse* mouse, mouse_scroll_axis axis, bool direction): + mouse(mouse), + axis(axis), + direction(direction) +{} + +} // namespace input + +/** + * Serializes an input mapping. + * + * @param[in] mapping Input mapping to serialize. + * @param[in,out] ctx Serialize context. + * + * @throw serialize_error Write error. + */ +/// @{ +template <> +void serializer::serialize(const input::gamepad_axis_mapping& mapping, serialize_context& ctx) +{ + ctx.write8(reinterpret_cast(&mapping.axis), 1); + const std::uint8_t direction = mapping.direction; + ctx.write8(reinterpret_cast(&direction), 1); +} + +template <> +void serializer::serialize(const input::gamepad_button_mapping& mapping, serialize_context& ctx) +{ + ctx.write8(reinterpret_cast(&mapping.button), 1); +} + +template <> +void serializer::serialize(const input::key_mapping& mapping, serialize_context& ctx) +{ + ctx.write16(reinterpret_cast(&mapping.scancode), 1); + ctx.write16(reinterpret_cast(&mapping.modifiers), 1); + const std::uint8_t repeat = mapping.repeat; + ctx.write8(reinterpret_cast(&repeat), 1); +} + +template <> +void serializer::serialize(const input::mouse_button_mapping& mapping, serialize_context& ctx) +{ + ctx.write8(reinterpret_cast(&mapping.button), 1); +} + +template <> +void serializer::serialize(const input::mouse_motion_mapping& mapping, serialize_context& ctx) +{ + ctx.write8(reinterpret_cast(&mapping.axis), 1); + const std::uint8_t direction = mapping.direction; + ctx.write8(reinterpret_cast(&direction), 1); +} + +template <> +void serializer::serialize(const input::mouse_scroll_mapping& mapping, serialize_context& ctx) +{ + ctx.write8(reinterpret_cast(&mapping.axis), 1); + const std::uint8_t direction = mapping.direction; + ctx.write8(reinterpret_cast(&direction), 1); +} +/// @} + +/** + * Deserializes an input mapping. + * + * @param[out] mapping Input mapping to deserialize. + * @param[in,out] ctx Deserialize context. + * + * @throw deserialize_error Read error. + */ +/// @{ +template <> +void deserializer::deserialize(input::gamepad_axis_mapping& mapping, deserialize_context& ctx) +{ + mapping.gamepad = nullptr; + + ctx.read8(reinterpret_cast(&mapping.axis), 1); + std::uint8_t direction = 0; + ctx.read8(reinterpret_cast(&direction), 1); + mapping.direction = direction; +} + +template <> +void deserializer::deserialize(input::gamepad_button_mapping& mapping, deserialize_context& ctx) +{ + mapping.gamepad = nullptr; + + ctx.read8(reinterpret_cast(&mapping.button), 1); +} + +template <> +void deserializer::deserialize(input::key_mapping& mapping, deserialize_context& ctx) +{ + mapping.keyboard = nullptr; + + ctx.read16(reinterpret_cast(&mapping.scancode), 1); + ctx.read16(reinterpret_cast(&mapping.modifiers), 1); + std::uint8_t repeat = 0; + ctx.read8(reinterpret_cast(&repeat), 1); + mapping.repeat = repeat; +} + +template <> +void deserializer::deserialize(input::mouse_button_mapping& mapping, deserialize_context& ctx) +{ + mapping.mouse = nullptr; + + ctx.read8(reinterpret_cast(&mapping.button), 1); +} + +template <> +void deserializer::deserialize(input::mouse_motion_mapping& mapping, deserialize_context& ctx) +{ + mapping.mouse = nullptr; + + ctx.read8(reinterpret_cast(&mapping.axis), 1); + std::uint8_t direction = 0; + ctx.read8(reinterpret_cast(&direction), 1); + mapping.direction = direction; +} + +template <> +void deserializer::deserialize(input::mouse_scroll_mapping& mapping, deserialize_context& ctx) +{ + mapping.mouse = nullptr; + + ctx.read8(reinterpret_cast(&mapping.axis), 1); + std::uint8_t direction = 0; + ctx.read8(reinterpret_cast(&direction), 1); + mapping.direction = direction; +} +/// @} diff --git a/src/engine/input/mapping.hpp b/src/engine/input/mapping.hpp new file mode 100644 index 0000000..03df301 --- /dev/null +++ b/src/engine/input/mapping.hpp @@ -0,0 +1,278 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_INPUT_MAPPING_HPP +#define ANTKEEPER_INPUT_MAPPING_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace input { + +class control; +class gamepad; +class keyboard; +class mouse; + +/** + * Abstract base class for input mappings. + */ +class mapping +{ +public: + /** + * Constructs an input mapping. + */ + mapping() = default; + + /// Destructs an input mapping. + virtual ~mapping() = default; + + /// Returns the input mapping type. + [[nodiscard]] virtual constexpr mapping_type get_mapping_type() const noexcept = 0; +}; + +/** + * Maps a direction along a gamepad axis to a control input value. + */ +class gamepad_axis_mapping: public mapping +{ +public: + /** + * Constructs a gamepad axis mapping. + * + * @param gamepad Pointer to the gamepad to map, or `nullptr` if input from any gamepad will be mapped. + * @param axis Gamepad axis to map. + * @param direction Sign bit of the direction to map. + */ + /// @{ + gamepad_axis_mapping(input::gamepad* gamepad, gamepad_axis axis, bool direction); + gamepad_axis_mapping() = default; + /// @} + + /// Destructs a gamepad axis mapping. + virtual ~gamepad_axis_mapping() = default; + + /// Returns mapping_type::gamepad_axis. + [[nodiscard]] inline virtual constexpr mapping_type get_mapping_type() const noexcept + { + return mapping_type::gamepad_axis; + } + + /// Pointer to the mapped gamepad, or `nullptr` if input from any gamepad is accepted. + input::gamepad* gamepad; + + /// Mapped gamepad axis. + gamepad_axis axis; + + /// Sign bit of the mapped direction. + bool direction; +}; + +/** + * Maps a gamepad button to a control input value. + */ +class gamepad_button_mapping: public mapping +{ +public: + /** + * Constructs a gamepad button mapping. + * + * @param gamepad Pointer to the gamepad to map, or `nullptr` if input from any gamepad will be mapped. + * @param button Gamepad button to map. + */ + /// @{ + gamepad_button_mapping(input::gamepad* gamepad, gamepad_button button); + gamepad_button_mapping() = default; + /// @} + + /// Destructs a gamepad button mapping. + virtual ~gamepad_button_mapping() = default; + + /// Returns mapping_type::gamepad_button. + [[nodiscard]] inline virtual constexpr mapping_type get_mapping_type() const noexcept + { + return mapping_type::gamepad_button; + } + + /// Pointer to the mapped gamepad, or `nullptr` if input from any gamepad is accepted. + input::gamepad* gamepad; + + /// Mapped gamepad button. + gamepad_button button; +}; + +/** + * Maps a keyboard key to a control input value. + */ +class key_mapping: public mapping +{ +public: + /** + * Constructs a key mapping. + * + * @param keyboard Pointer to the keyboard to map, or `nullptr` if input from any keyboard will be mapped. + * @param scancode Scancode of the key to map. + * @param repeat `false` if the mapping should ignore key repeats, `true` otherwise. + * @param modifiers Modifier keys bitmask. + */ + /// @{ + key_mapping(input::keyboard* keyboard, input::scancode scancode, std::uint16_t modifiers = 0, bool repeat = false); + key_mapping() = default; + /// @} + + /// Destructs a keyboard key mapping. + virtual ~key_mapping() = default; + + /// Returns mapping_type::key. + [[nodiscard]] inline virtual constexpr mapping_type get_mapping_type() const noexcept + { + return mapping_type::key; + } + + /// Pointer to the mapped keyboard, or `nullptr` if input from any keyboard is accepted. + input::keyboard* keyboard; + + /// Scancode of the mapped key. + scancode scancode; + + /// Modifier keys bitbask. + std::uint16_t modifiers; + + /// `false` if the mapping ignores key repeats, `true` otherwise. + bool repeat; +}; + +/** + * Maps a mouse button to a control input value. + */ +class mouse_button_mapping: public mapping +{ +public: + /** + * Constructs a mouse button mapping. + * + * @param mouse Pointer to the mouse to map, or `nullptr` if input from any mouse will be mapped. + * @param button Mouse button to map. + */ + /// @{ + mouse_button_mapping(input::mouse* mouse, mouse_button button); + mouse_button_mapping() = default; + /// @} + + /// Destructs a mouse button mapping. + virtual ~mouse_button_mapping() = default; + + /// Returns mapping_type::mouse_button. + [[nodiscard]] inline virtual constexpr mapping_type get_mapping_type() const noexcept + { + return mapping_type::mouse_button; + } + + /// Pointer to the mapped mouse, or `nullptr` if input from any mouse is accepted. + input::mouse* mouse; + + /// Mapped mouse button. + mouse_button button; +}; + +/** + * Maps a direction along a mouse motion axis to a control input value. + */ +class mouse_motion_mapping: public mapping +{ +public: + /** + * Constructs a mouse motion mapping. + * + * @param mouse Pointer to the mouse to map, or `nullptr` if input from any mouse will be mapped. + * @param axis Mouse motion axis to map. + * @param direction Sign bit of the direction to map. + */ + /// @{ + mouse_motion_mapping(input::mouse* mouse, mouse_motion_axis axis, bool direction); + mouse_motion_mapping() = default; + /// @} + + /// Destructs a mouse motion mapping. + virtual ~mouse_motion_mapping() = default; + + /// Returns mapping_type::mouse_motion. + [[nodiscard]] inline virtual constexpr mapping_type get_mapping_type() const noexcept + { + return mapping_type::mouse_motion; + } + + /// Pointer to the mapped mouse, or `nullptr` if input from any mouse is accepted. + input::mouse* mouse; + + /// Mapped mouse motion axis. + mouse_motion_axis axis; + + /// Sign bit of the mapped direction. + bool direction; +}; + +/** + * Maps a direction along a mouse scroll axis to a control input value. + */ +class mouse_scroll_mapping: public mapping +{ +public: + /** + * Constructs a mouse scroll mapping. + * + * @param control Control to which input will be mapped. + * @param mouse Pointer to the mouse to map, or `nullptr` if input from any mouse will be mapped. + * @param axis Mouse scroll axis to map. + * @param direction Sign bit of the direction to map. + */ + /// @{ + mouse_scroll_mapping(input::mouse* mouse, mouse_scroll_axis axis, bool direction); + mouse_scroll_mapping() = default; + /// @} + + /// Destructs a mouse scroll mapping. + virtual ~mouse_scroll_mapping() = default; + + /// Returns mapping_type::mouse_scroll. + [[nodiscard]] inline virtual constexpr mapping_type get_mapping_type() const noexcept + { + return mapping_type::mouse_scroll; + } + + /// Pointer to the mapped mouse, or `nullptr` if input from any mouse is accepted. + input::mouse* mouse; + + /// Mapped mouse scroll axis. + mouse_scroll_axis axis; + + /// Sign bit of the mapped direction. + bool direction; +}; + +} // namespace input + +#endif // ANTKEEPER_INPUT_MAPPING_HPP diff --git a/src/input/modifier-key.hpp b/src/engine/input/modifier-key.hpp similarity index 100% rename from src/input/modifier-key.hpp rename to src/engine/input/modifier-key.hpp diff --git a/src/input/mouse-button.hpp b/src/engine/input/mouse-button.hpp similarity index 100% rename from src/input/mouse-button.hpp rename to src/engine/input/mouse-button.hpp diff --git a/src/engine/input/mouse-events.hpp b/src/engine/input/mouse-events.hpp new file mode 100644 index 0000000..7d29271 --- /dev/null +++ b/src/engine/input/mouse-events.hpp @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_INPUT_MOUSE_EVENTS_HPP +#define ANTKEEPER_INPUT_MOUSE_EVENTS_HPP + +#include +#include +#include +#include +#include + +namespace input { + +class mouse; + +/** + * Event generated when a mouse has been moved. + */ +struct mouse_moved_event +{ + /// Mouse that generated the event. + mouse* mouse; + + /// Mouse position, in pixels, relative to the window. + math::vector position; + + /// Relative movement of the mouse, in pixels. + math::vector difference; +}; + +/** + * Event generated when a mouse button has been pressed. + */ +struct mouse_button_pressed_event +{ + /// Mouse that generated the event. + mouse* mouse; + + /// Mouse position, in pixels, relative to the window, when the button was pressed. + math::vector position; + + /// Mouse button being pressed. + mouse_button button; +}; + +/** + * Event generated when a mouse button has been released. + */ +struct mouse_button_released_event +{ + /// Mouse that generated the event. + mouse* mouse; + + /// Mouse position, in pixels, relative to the window, when the button was released. + math::vector position; + + /// Mouse button being released. + mouse_button button; +}; + +/** + * Event generated when a mouse has been scrolled. + */ +struct mouse_scrolled_event +{ + /// Mouse that generated the event. + mouse* mouse; + + /// Mouse position, in pixels, relative to the window, when the mouse was scrolled. + math::vector position; + + /// Scroll velocity. + math::vector velocity; +}; + +} // namespace input + +#endif // ANTKEEPER_INPUT_MOUSE_EVENTS_HPP diff --git a/src/input/mouse-motion-axis.hpp b/src/engine/input/mouse-motion-axis.hpp similarity index 100% rename from src/input/mouse-motion-axis.hpp rename to src/engine/input/mouse-motion-axis.hpp diff --git a/src/input/mouse-scroll-axis.hpp b/src/engine/input/mouse-scroll-axis.hpp similarity index 100% rename from src/input/mouse-scroll-axis.hpp rename to src/engine/input/mouse-scroll-axis.hpp diff --git a/src/engine/input/mouse.cpp b/src/engine/input/mouse.cpp new file mode 100644 index 0000000..8c665fb --- /dev/null +++ b/src/engine/input/mouse.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include + +namespace input { + +void mouse::press(mouse_button button) +{ + button_pressed_publisher.publish({this, position, button}); +} + +void mouse::release(mouse_button button) +{ + button_released_publisher.publish({this, position, button}); +} + +void mouse::move(const math::vector& position, const math::vector& difference) +{ + this->position = position; + moved_publisher.publish({this, position, difference}); +} + +void mouse::scroll(const math::vector& velocity) +{ + scrolled_publisher.publish({this, position, velocity}); +} + +} // namespace input diff --git a/src/engine/input/mouse.hpp b/src/engine/input/mouse.hpp new file mode 100644 index 0000000..9371cb8 --- /dev/null +++ b/src/engine/input/mouse.hpp @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_INPUT_MOUSE_HPP +#define ANTKEEPER_INPUT_MOUSE_HPP + +#include +#include +#include +#include +#include +#include + +namespace input { + +/** + * A virtual mouse which generates mouse-related input events. + */ +class mouse: public device +{ +public: + /** + * Constructs a mouse input device. + */ + mouse() = default; + + /// Destructs a mouse input device. + virtual ~mouse() = default; + + /** + * Simulates a mouse button press. + * + * @param button Button to press. + */ + void press(mouse_button button); + + /** + * Simulates a mouse button release. + * + * @param button Button to release. + */ + void release(mouse_button button); + + /** + * Simulates mouse movement. + * + * @param position Mouse position, in pixels, relative to the window. + * @param difference Relative movement of the mouse, in pixels. + */ + void move(const math::vector& position, const math::vector& difference); + + /** + * Simulates mouse scrolling. + * + * @param velocity Scroll velocity. + */ + void scroll(const math::vector& velocity); + + /// Returns the current mouse position, in pixels, relative to the window. + [[nodiscard]] inline const math::vector& get_position() const noexcept + { + return position; + } + + /// Returns the channel through which mouse button pressed events are published. + [[nodiscard]] inline ::event::channel& get_button_pressed_channel() noexcept + { + return button_pressed_publisher.channel(); + } + + /// Returns the channel through which mouse button released events are published. + [[nodiscard]] inline ::event::channel& get_button_released_channel() noexcept + { + return button_released_publisher.channel(); + } + + /// Returns the channel through which mouse moved events are published. + [[nodiscard]] inline ::event::channel& get_moved_channel() noexcept + { + return moved_publisher.channel(); + } + + /// Returns the channel through which mouse scrolled events are published. + [[nodiscard]] inline ::event::channel& get_scrolled_channel() noexcept + { + return scrolled_publisher.channel(); + } + + /// Returns device_type::mouse. + [[nodiscard]] inline virtual constexpr device_type get_device_type() const noexcept + { + return device_type::mouse; + } + +private: + math::vector position; + + ::event::publisher button_pressed_publisher; + ::event::publisher button_released_publisher; + ::event::publisher moved_publisher; + ::event::publisher scrolled_publisher; +}; + +} // namespace input + +#endif // ANTKEEPER_INPUT_MOUSE_HPP diff --git a/src/input/scancode.hpp b/src/engine/input/scancode.hpp similarity index 100% rename from src/input/scancode.hpp rename to src/engine/input/scancode.hpp diff --git a/src/engine/math/angles.hpp b/src/engine/math/angles.hpp new file mode 100644 index 0000000..7beb126 --- /dev/null +++ b/src/engine/math/angles.hpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_MATH_ANGLES_HPP +#define ANTKEEPER_MATH_ANGLES_HPP + +#include +#include + +namespace math { + +/** + * Converts an angle from radians to degrees. + * + * @param radians Angle in radians. + * + * @return Angle in degrees. + */ +template +[[nodiscard]] inline constexpr T degrees(T radians) noexcept +{ + return radians * rad2deg; +} + +/** + * Converts an angle given in degrees to radians. + * + * @param radians Angle in radians. + * @return Angle in degrees. + */ +template +[[nodiscard]] inline constexpr T radians(T degrees) noexcept +{ + return degrees * deg2rad; +} + +/** + * Wraps an angle to [-180, 180]. + * + * @param degrees Angle in degrees. + * @return Wrapped angle. + */ +template +[[nodiscard]] inline constexpr T wrap_degrees(T degrees) +{ + return std::remainder(degrees, T(360)); +} + +/** + * Wraps an angle to [-pi, pi]. + * + * @param radians Angle in radians. + * @return Wrapped angle. + */ +template +[[nodiscard]] inline constexpr T wrap_radians(T radians) +{ + return std::remainder(radians, two_pi); +} + +} // namespace math + +#endif // ANTKEEPER_MATH_ANGLES_HPP diff --git a/src/math/compile.hpp b/src/engine/math/compile.hpp similarity index 100% rename from src/math/compile.hpp rename to src/engine/math/compile.hpp diff --git a/src/engine/math/glsl.hpp b/src/engine/math/glsl.hpp new file mode 100644 index 0000000..3ae76c9 --- /dev/null +++ b/src/engine/math/glsl.hpp @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_MATH_GLSL_HPP +#define ANTKEEPER_MATH_GLSL_HPP + +#include +#include +#include + +namespace math { +namespace glsl { + +/** + * Linear algebra data types with GLSL naming conventions. + */ +namespace types +{ + /// @name Vector types + /// @{ + + /** + * *n*-dimensional vector of booleans. + * + * @tparam N Number of elements + */ + /// @{ + template + using bvec = math::vector; + using bvec2 = bvec<2>; + using bvec3 = bvec<3>; + using bvec4 = bvec<4>; + /// @} + + /** + * *n*-dimensional vector of signed integers. + * + * @tparam N Number of elements + */ + /// @{ + template + using ivec = math::vector; + using ivec2 = ivec<2>; + using ivec3 = ivec<3>; + using ivec4 = ivec<4>; + /// @} + + /** + * *n*-dimensional vector of unsigned integers. + * + * @tparam N Number of elements + */ + /// @{ + template + using uvec = math::vector; + using uvec2 = uvec<2>; + using uvec3 = uvec<3>; + using uvec4 = uvec<4>; + /// @} + + /** + * *n*-dimensional vector of floating-point numbers. + * + * @tparam N Number of elements + */ + /// @{ + template + using fvec = math::vector; + using fvec2 = fvec<2>; + using fvec3 = fvec<3>; + using fvec4 = fvec<4>; + using vec2 = fvec2; + using vec3 = fvec3; + using vec4 = fvec4; + /// @} + + /** + * *n*-dimensional vector of double-precision floating-point numbers. + * + * @tparam N Number of elements + */ + /// @{ + template + using dvec = math::vector; + using dvec2 = dvec<2>; + using dvec3 = dvec<3>; + using dvec4 = dvec<4>; + /// @} + + /// @} + + /// @name Matrix types + /// @{ + + /** + * *n* by *m* matrix of floating-point numbers. + * + * @tparam N Number of columns. + * @tparam M Number of rows. + */ + /// @{ + template + using fmat = math::matrix; + using fmat2x2 = fmat<2, 2>; + using fmat2x3 = fmat<2, 3>; + using fmat2x4 = fmat<2, 4>; + using fmat3x2 = fmat<3, 2>; + using fmat3x3 = fmat<3, 3>; + using fmat3x4 = fmat<3, 4>; + using fmat4x2 = fmat<4, 2>; + using fmat4x3 = fmat<4, 3>; + using fmat4x4 = fmat<4, 4>; + using mat2x2 = fmat2x2; + using mat2x3 = fmat2x3; + using mat2x4 = fmat2x4; + using mat3x2 = fmat3x2; + using mat3x3 = fmat3x3; + using mat3x4 = fmat3x4; + using mat4x2 = fmat4x2; + using mat4x3 = fmat4x3; + using mat4x4 = fmat4x4; + /// @} + + /** + * *n* by *n* square matrix of floating-point numbers. + * + * @tparam N Number of columns and rows. + */ + /// @{ + using fmat2 = fmat2x2; + using fmat3 = fmat3x3; + using fmat4 = fmat4x4; + using mat2 = fmat2; + using mat3 = fmat3; + using mat4 = fmat4; + /// @} + + /** + * *n* by *m* matrix of double-precision floating-point numbers. + * + * @tparam N Number of columns. + * @tparam M Number of rows. + */ + /// @{ + template + using dmat = math::matrix; + using dmat2x2 = dmat<2, 2>; + using dmat2x3 = dmat<2, 3>; + using dmat2x4 = dmat<2, 4>; + using dmat3x2 = dmat<3, 2>; + using dmat3x3 = dmat<3, 3>; + using dmat3x4 = dmat<3, 4>; + using dmat4x2 = dmat<4, 2>; + using dmat4x3 = dmat<4, 3>; + using dmat4x4 = dmat<4, 4>; + /// @} + + /** + * *n* by *n* square matrix of double-precision floating-point numbers. + * + * @tparam N Number of columns and rows. + */ + /// @{ + using dmat2 = dmat2x2; + using dmat3 = dmat3x3; + using dmat4 = dmat4x4; + /// @} + + /// @} + + /// @name Quaternion types + /// @{ + + /** + * Quaternion with floating-point scalars. + * + * @tparam T Scalar type. + */ + /// @{ + using fquat = math::quaternion; + using quat = fquat; + /// @} + + /** + * Quaternion with double-precision floating-point scalars. + * + * @tparam T Scalar type. + */ + using dquat = math::quaternion; + + /// @} + +} // namespace types + +// Bring GLSL types into glsl namespace +using namespace types; + +} // namespace glsl +} // namespace math + +#endif // ANTKEEPER_MATH_GLSL_HPP diff --git a/src/engine/math/hash/hash.hpp b/src/engine/math/hash/hash.hpp new file mode 100644 index 0000000..ee125ce --- /dev/null +++ b/src/engine/math/hash/hash.hpp @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_MATH_HASH_HPP +#define ANTKEEPER_MATH_HASH_HPP + +namespace math { + +/// Hash functions. +namespace hash {} + +} // namespace math + +#include +#include + +#endif // ANTKEEPER_MATH_HASH_HPP diff --git a/src/math/hash/make-uint.hpp b/src/engine/math/hash/make-uint.hpp similarity index 100% rename from src/math/hash/make-uint.hpp rename to src/engine/math/hash/make-uint.hpp diff --git a/src/engine/math/hash/pcg.hpp b/src/engine/math/hash/pcg.hpp new file mode 100644 index 0000000..70283ed --- /dev/null +++ b/src/engine/math/hash/pcg.hpp @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_MATH_HASH_PCG_HPP +#define ANTKEEPER_MATH_HASH_PCG_HPP + +#include +#include +#include +#include + +namespace math { +namespace hash { + +/// @private +template +constexpr T pcg_multiplier = 0; +template <> +constexpr std::uint8_t pcg_multiplier = 141U; +template <> +constexpr std::uint16_t pcg_multiplier = 12829U; +template <> +constexpr std::uint32_t pcg_multiplier = 747796405UL; +template <> +constexpr std::uint64_t pcg_multiplier = 6364136223846793005ULL; + +/// @private +template +constexpr T pcg_increment = 0; +template <> +constexpr std::uint8_t pcg_increment = 77U; +template <> +constexpr std::uint16_t pcg_increment = 47989U; +template <> +constexpr std::uint32_t pcg_increment = 2891336453UL; +template <> +constexpr std::uint64_t pcg_increment = 1442695040888963407ULL; + +/// @private +template +constexpr T mcg_multiplier = 0; +template <> +constexpr std::uint8_t mcg_multiplier = 217U; +template <> +constexpr std::uint16_t mcg_multiplier = 62169U; +template <> +constexpr std::uint32_t mcg_multiplier = 277803737UL; +template <> +constexpr std::uint64_t mcg_multiplier = 12605985483714917081ULL; + +/// @private +template +[[nodiscard]] constexpr T pcg_uint(T x) noexcept +{ + static_assert(std::is_integral::value && std::is_unsigned::value); + static_assert(sizeof(T) <= 8); + + x = x * pcg_multiplier + pcg_increment; + x = (x ^ (x >> ((x >> ((sizeof(T) * 8) - (sizeof(T) + 1))) + (sizeof(T) + 1)))) * mcg_multiplier; + return x ^ (x >> ((sizeof(T) * 16 + 2) / 3)); +} + +/// @private +template +[[nodiscard]] inline constexpr vector pcg_uvec1(vector x) noexcept +{ + static_assert(std::is_integral::value && std::is_unsigned::value); + static_assert(sizeof(T) <= 8); + + x[0] = T(x[0]); + + return x; +} + +/// @private +template +[[nodiscard]] constexpr vector pcg_uvec2(vector x) noexcept +{ + static_assert(std::is_integral::value && std::is_unsigned::value); + static_assert(sizeof(T) <= 8); + + x = x * pcg_multiplier + pcg_increment; + + x[0] += x[1] * pcg_multiplier; + x[1] += x[0] * pcg_multiplier; + + x[0] ^= x[0] >> (sizeof(T) * 4); + x[1] ^= x[1] >> (sizeof(T) * 4); + + x[0] += x[1] * pcg_multiplier; + x[1] += x[0] * pcg_multiplier; + + x[0] ^= x[0] >> (sizeof(T) * 4); + x[1] ^= x[1] >> (sizeof(T) * 4); + + return x; +} + +/// @private +template +[[nodiscard]] constexpr vector pcg_uvec3(vector x) noexcept +{ + static_assert(std::is_integral::value && std::is_unsigned::value); + static_assert(sizeof(T) <= 8); + + x = x * pcg_multiplier + pcg_increment; + + x[0] += x[1] * x[2]; + x[1] += x[2] * x[0]; + x[2] += x[0] * x[1]; + + x[0] ^= x[0] >> (sizeof(T) * 4); + x[1] ^= x[1] >> (sizeof(T) * 4); + x[2] ^= x[2] >> (sizeof(T) * 4); + + x[0] += x[1] * x[2]; + x[1] += x[2] * x[0]; + x[2] += x[0] * x[1]; + + return x; +} + +/// @private +template +[[nodiscard]] constexpr vector pcg_uvec4(vector x) noexcept +{ + static_assert(std::is_integral::value && std::is_unsigned::value); + static_assert(sizeof(T) <= 8); + + x = x * pcg_multiplier + pcg_increment; + + x[0] += x[1] * x[3]; + x[1] += x[2] * x[0]; + x[2] += x[0] * x[1]; + x[3] += x[1] * x[2]; + + x[0] ^= x[0] >> (sizeof(T) * 4); + x[1] ^= x[1] >> (sizeof(T) * 4); + x[2] ^= x[2] >> (sizeof(T) * 4); + x[3] ^= x[3] >> (sizeof(T) * 4); + + x[0] += x[1] * x[3]; + x[1] += x[2] * x[0]; + x[2] += x[0] * x[1]; + x[3] += x[1] * x[2]; + + return x; +} + +/** + * PCG hash function. + * + * @param x Input value. + * + * @return Unsigned pseudorandom output value. + * + * @warning Floating point and signed input values will be converted to unsigned integers via `static_cast`. + * @warning Vectors with more than 4 elements are not supported. + * + * @see https://en.wikipedia.org/wiki/Permuted_congruential_generator + * @see O'Neill, M.E. (2014). PCG : A Family of Simple Fast Space-Efficient Statistically Good Algorithms for Random Number Generation. + * @see Mark Jarzynski and Marc Olano, Hash Functions for GPU Rendering, Journal of Computer Graphics Techniques (JCGT), vol. 9, no. 3, 21-38, 2020. + */ +/// @{ +[[nodiscard]] inline constexpr std::uint8_t pcg(std::uint8_t x) noexcept +{ + return pcg_uint(x); +} + +[[nodiscard]] inline constexpr std::uint16_t pcg(std::uint16_t x) noexcept +{ + return pcg_uint(x); +} + +[[nodiscard]] inline constexpr std::uint32_t pcg(std::uint32_t x) noexcept +{ + return pcg_uint(x); +} + +[[nodiscard]] inline constexpr std::uint64_t pcg(std::uint64_t x) noexcept +{ + return pcg_uint(x); +} + +[[nodiscard]] inline constexpr std::uint8_t pcg(std::int8_t x) noexcept +{ + return pcg_uint(static_cast(x)); +} + +[[nodiscard]] inline constexpr std::uint16_t pcg(std::int16_t x) noexcept +{ + return pcg_uint(static_cast(x)); +} + +[[nodiscard]] inline constexpr std::uint32_t pcg(std::int32_t x) noexcept +{ + return pcg_uint(static_cast(x)); +} + +[[nodiscard]] inline constexpr std::uint64_t pcg(std::int64_t x) noexcept +{ + return pcg_uint(static_cast(x)); +} + +[[nodiscard]] inline constexpr std::uint32_t pcg(float x) noexcept +{ + return pcg_uint(static_cast(x)); +} + +[[nodiscard]] inline constexpr std::uint64_t pcg(double x) noexcept +{ + return pcg_uint(static_cast(x)); +} + +template +[[nodiscard]] inline constexpr vector, N> pcg(const vector& x) noexcept +{ + static_assert(N > 0 && N < 5, "PCG hash only supports vectors with 1-4 elements."); + + if constexpr (N == 1) + return pcg_uvec1>(vector, N>(x)); + else if constexpr (N == 2) + return pcg_uvec2>(vector, N>(x)); + else if constexpr (N == 3) + return pcg_uvec3>(vector, N>(x)); + else + return pcg_uvec4>(vector, N>(x)); +} +/// @} + +} // namespace hash +} // namespace math + +#endif // ANTKEEPER_MATH_HASH_PCG_HPP diff --git a/src/engine/math/interpolation.hpp b/src/engine/math/interpolation.hpp new file mode 100644 index 0000000..4333187 --- /dev/null +++ b/src/engine/math/interpolation.hpp @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_MATH_INTERPOLATION_HPP +#define ANTKEEPER_MATH_INTERPOLATION_HPP + +#include +#include + +namespace math { + +/** + * Linearly interpolates between @p x and @p y. + * + * Requires the following operators to be defined: + * + * T operator+(const T&, const T&); + * T operator-(const T&, const T&); + * T operator*(const T&, S); + * + * @tparam T Value type. + * @tparam S Scalar type. + */ +template +[[nodiscard]] constexpr T lerp(const T& x, const T& y, S a) +{ + return x + (y - x) * a; +} + +/** + * Linearly interpolates between two angles, @p x and @p y. + * + * @tparam T Value type. + * @param x Start angle, in radians. + * @param y End angle, in radians. + * @param a Interpolation ratio. + * @return Interpolated angle, in radians. + */ +template +[[nodiscard]] constexpr T lerp_angle(T x, T y, T a) +{ + return wrap_radians(x + wrap_radians(y - x) * a); +} + +/** + * Logarithmically interpolates between @p x and @p y. + * + * @warning Undefined behavior when @p x is zero. + * + * @tparam T Value type. + * @tparam S Scalar type. + */ +template +[[nodiscard]] T log_lerp(const T& x, const T& y, S a) +{ + //return std::exp(linear(std::log(x), std::log(y), a)); + return x * std::pow(y / x, a); +} + +} // namespace math + +#endif // ANTKEEPER_MATH_INTERPOLATION_HPP diff --git a/src/engine/math/linear-algebra.hpp b/src/engine/math/linear-algebra.hpp new file mode 100644 index 0000000..c3ef4a4 --- /dev/null +++ b/src/engine/math/linear-algebra.hpp @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_MATH_LINEAR_ALGEBRA_HPP +#define ANTKEEPER_MATH_LINEAR_ALGEBRA_HPP + +#include +#include +#include + +#endif // ANTKEEPER_MATH_LINEAR_ALGEBRA_HPP diff --git a/src/math/literals.hpp b/src/engine/math/literals.hpp similarity index 100% rename from src/math/literals.hpp rename to src/engine/math/literals.hpp diff --git a/src/math/map.hpp b/src/engine/math/map.hpp similarity index 100% rename from src/math/map.hpp rename to src/engine/math/map.hpp diff --git a/src/engine/math/math.hpp b/src/engine/math/math.hpp new file mode 100644 index 0000000..8a8620f --- /dev/null +++ b/src/engine/math/math.hpp @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_MATH_HPP +#define ANTKEEPER_MATH_HPP + +/// Mathematical functions and data types. +namespace math {} + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#endif // ANTKEEPER_MATH_HPP diff --git a/src/engine/math/matrix.hpp b/src/engine/math/matrix.hpp new file mode 100644 index 0000000..101ca66 --- /dev/null +++ b/src/engine/math/matrix.hpp @@ -0,0 +1,1381 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_MATH_MATRIX_HPP +#define ANTKEEPER_MATH_MATRIX_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace math { + +/** + * *n* by *m* column-major matrix. + * + * @tparam T Element type. + * @tparam N Number of columns. + * @tparam M Number of rows. + * + * @see https://en.wikipedia.org/wiki/Row-_and_column-major_order + */ +template +struct matrix +{ + /// Element type. + typedef T element_type; + + /// Number of columns. + static constexpr std::size_t column_count = N; + + /// Number of rows. + static constexpr std::size_t row_count = M; + + /// Number of elements. + static constexpr std::size_t element_count = column_count * row_count; + + /// Matrix column vector data type. + typedef vector column_vector_type; + + /// Matrix row vector data type. + typedef vector row_vector_type; + + /// Array of matrix column vectors. + column_vector_type columns[column_count]; + + /// @name Conversion + /// @{ + + /// @private + template + [[nodiscard]] inline constexpr matrix type_cast(std::index_sequence) const noexcept + { + return {vector(columns[I])...}; + } + + /** + * Type-casts the elements of this matrix using `static_cast`. + * + * @tparam U Target element type. + * + * @return Matrix containing the type-casted elements. + */ + template + [[nodiscard]] inline constexpr explicit operator matrix() const noexcept + { + return type_cast(std::make_index_sequence{}); + } + + /// @private + template + [[nodiscard]] inline constexpr matrix size_cast(std::index_sequence) const noexcept + { + if constexpr (O == M) + return {((I < N) ? columns[I] : matrix::identity()[I]) ...}; + else + return {((I < N) ? vector(columns[I]) : matrix::identity()[I]) ...}; + } + + /** + * Size-casts this matrix to a matrix with different dimensions. Casting to greater dimensions causes new elements to be set to identity matrix elements. + * + * @tparam P Target number of columns. + * @tparam O Target number of rows. + * + * @return *p* by *o* matrix. + */ + template + [[nodiscard]] inline constexpr explicit operator matrix() const noexcept + { + return size_cast(std::make_index_sequence

{}); + } + + /// @} + + /// @name Column access + /// @{ + + /** + * Returns a reference to the column vector at a given index. + * + * @param i Index of a column vector. + * + * @return Reference to the column vector at index @p i. + */ + /// @{ + [[nodiscard]] inline constexpr column_vector_type& operator[](std::size_t i) noexcept + { + return columns[i]; + } + [[nodiscard]] inline constexpr const column_vector_type& operator[](std::size_t i) const noexcept + { + return columns[i]; + } + [[nodiscard]] inline constexpr column_vector_type& column(std::size_t i) noexcept + { + return columns[i]; + } + [[nodiscard]] inline constexpr const column_vector_type& column(std::size_t i) const noexcept + { + return columns[i]; + } + /// @} + + /** + * Returns a reference to the first column vector. + */ + /// @{ + [[nodiscard]] inline constexpr column_vector_type& front() noexcept + { + return columns[0]; + } + [[nodiscard]] inline constexpr const column_vector_type& front() const noexcept + { + return columns[0]; + } + /// @} + + /** + * Returns a reference to the last column vector. + */ + /// @{ + [[nodiscard]] inline constexpr column_vector_type& back() noexcept + { + return columns[column_count - 1]; + } + [[nodiscard]] inline constexpr const column_vector_type& back() const noexcept + { + return columns[column_count - 1]; + } + /// @} + + /// @} + + /// @name Element access + /// @{ + + /** + * Returns a reference to the element at a given column-major index. + * + * @param i Column-major index of a matrix element. + * + * @return Reference to the element at column-major index @p i. + */ + /// @{ + [[nodiscard]] inline constexpr T& element(std::size_t i) noexcept + { + return columns[i / row_count][i % row_count]; + } + [[nodiscard]] inline constexpr const T& element(std::size_t i) const noexcept + { + return columns[i / row_count][i % row_count]; + } + /// @} + + /** + * Returns a pointer to the first element. + * + * @warning If matrix::element_type is not a POD type, elements may not be stored contiguously. + */ + /// @{ + [[nodiscard]] inline constexpr element_type* data() noexcept + { + return &columns[0][0]; + }; + [[nodiscard]] inline constexpr const element_type* data() const noexcept + { + return &columns[0][0]; + }; + /// @} + + /// @} + + /// @name Iterators + /// @{ + + /** + * Returns an iterator to the first column vector. + */ + /// @{ + [[nodiscard]] inline constexpr column_vector_type* begin() noexcept + { + return columns; + } + [[nodiscard]] inline constexpr const column_vector_type* begin() const noexcept + { + return columns; + } + [[nodiscard]] inline constexpr const column_vector_type* cbegin() const noexcept + { + return columns; + } + /// @} + + /** + * Returns an iterator to the column vector following the last column vector. + */ + /// @{ + [[nodiscard]] inline constexpr column_vector_type* end() noexcept + { + return columns + column_count; + } + [[nodiscard]] inline constexpr const column_vector_type* end() const noexcept + { + return columns + column_count; + } + [[nodiscard]] inline constexpr const column_vector_type* cend() const noexcept + { + return columns + column_count; + } + /// @} + + /** + * Returns a reverse iterator to the first column vector of the reversed matrix. + */ + /// @{ + [[nodiscard]] inline constexpr std::reverse_iterator rbegin() noexcept + { + return std::reverse_iterator(columns + column_count); + } + [[nodiscard]] inline constexpr std::reverse_iterator rbegin() const noexcept + { + return std::reverse_iterator(columns + column_count); + } + [[nodiscard]] inline constexpr std::reverse_iterator crbegin() const noexcept + { + return std::reverse_iterator(columns + column_count); + } + /// @} + + /** + * Returns a reverse iterator to the column vector following the last column vector of the reversed matrix. + */ + /// @{ + [[nodiscard]] inline constexpr std::reverse_iterator rend() noexcept + { + return std::reverse_iterator(columns); + } + [[nodiscard]] inline constexpr std::reverse_iterator rend() const noexcept + { + return std::reverse_iterator(columns); + } + [[nodiscard]] inline constexpr std::reverse_iterator crend() const noexcept + { + return std::reverse_iterator(columns); + } + /// @} + + /// @} + + /// @name Capacity + /// @{ + + /** + * Returns the number of elements in the matrix. + */ + [[nodiscard]] inline constexpr std::size_t size() const noexcept + { + return element_count; + }; + + /// @} + + /// @name Constants + /// @{ + + /** + * Returns a zero matrix, where every element is equal to zero. + */ + [[nodiscard]] static constexpr matrix zero() noexcept + { + return {}; + } + + /// @private + template + [[nodiscard]] static inline constexpr matrix one(std::index_sequence) noexcept + { + //return {column_vector_type::one() ...}; + + // MSVC bug workaround (I must be referenced for parameter pack expansion) + return {(I ? column_vector_type::one() : column_vector_type::one()) ...}; + } + + /** + * Returns a matrix of ones, where every element is equal to one. + */ + [[nodiscard]] static constexpr matrix one() noexcept + { + return one(std::make_index_sequence{}); + } + + /// @private + template + [[nodiscard]] static inline constexpr column_vector_type identity_column(std::size_t i, std::index_sequence) noexcept + { + return {(I == i ? T{1} : T{0}) ...}; + } + + /// @private + template + [[nodiscard]] static inline constexpr matrix identity(std::index_sequence) noexcept + { + return {identity_column(I, std::make_index_sequence{}) ...}; + } + + /** + * Returns an identity matrix, with ones on the main diagonal and zeros elsewhere. + */ + [[nodiscard]] static constexpr matrix identity() noexcept + { + return identity(std::make_index_sequence{}); + } + + /// @} +}; + +/// 2x2 matrix. +template +using matrix2 = matrix; + +/// 2x2 matrix. +template +using matrix2x2 = matrix; + +/// 3x3 matrix. +template +using matrix3 = matrix; + +/// 3x3 matrix. +template +using matrix3x3 = matrix; + +/// 4x4 matrix. +template +using matrix4 = matrix; + +/// 4x4 matrix. +template +using matrix4x4 = matrix; + +/** + * Adds two matrices. + * + * @param a First matrix. + * @param b Second matrix. + * + * @return Sum of the two matrices. + */ +template +[[nodiscard]] constexpr matrix add(const matrix& a, const matrix& b) noexcept; + +/** + * Adds a matrix and a scalar. + * + * @param a Matrix. + * @param b scalar. + * + * @return Sum of the matrix and scalar. + */ +template +[[nodiscard]] constexpr matrix add(const matrix& a, T b) noexcept; + +/** + * Calculates the determinant of a square matrix. + * + * @param m Matrix of which to take the determinant. + * + * @return Determinant of @p m. + * + * @warning Currently only implemented for 2x2, 3x3, and 4x4 matrices. + */ +template +[[nodiscard]] constexpr T determinant(const matrix& m) noexcept; + +/** + * Performs a component-wise multiplication of two matrices. + * + * @param x First matrix multiplicand. + * @param y Second matrix multiplicand. + * + * @return Product of the component-wise multiplcation. + */ +template +[[nodiscard]] constexpr matrix componentwise_mul(const matrix& a, const matrix& b) noexcept; + +/** + * Divides a matrix by a matrix. + * + * @param a First matrix. + * @param b Second matrix. + * + * @return Result of the division. + */ +template +[[nodiscard]] constexpr matrix div(const matrix& a, const matrix& b) noexcept; + +/** + * Divides a matrix by a scalar. + * + * @param a Matrix. + * @param b Scalar. + * + * @return Result of the division. + */ +template +[[nodiscard]] constexpr matrix div(const matrix& a, T b) noexcept; + +/** + * Divides a scalar by a matrix. + * + * @param a Scalar. + * @param b Matrix. + * + * @return Result of the division. + */ +template +[[nodiscard]] constexpr matrix div(T a, const matrix& b) noexcept; + +/** + * Extracts the Ith column from a matrix. + * + * @tparam I Index of a column. + * @tparam T Element type. + * @tparam N Number of columns. + * @tparam M Number of rows. + * + * @param m Matrix from which to extract a column. + * + * @return Reference to the Ith column of @p m. + */ +/// @{ +template +[[nodiscard]] constexpr typename matrix::column_vector_type& get(math::matrix& m) noexcept; +template +[[nodiscard]] constexpr typename matrix::column_vector_type&& get(math::matrix&& m) noexcept; +template +[[nodiscard]] constexpr const typename matrix::column_vector_type& get(const math::matrix& m) noexcept; +template +[[nodiscard]] constexpr const typename matrix::column_vector_type&& get(const math::matrix&& m) noexcept; +/// @} + +/** + * Calculates the inverse of a square matrix. + * + * @param m Square matrix. + * + * @return Inverse of matrix @p m. + * + * @warning Currently only implemented for 2x2, 3x3, and 4x4 matrices. + */ +template +[[nodiscard]] constexpr matrix inverse(const matrix& m) noexcept; + +/** + * Creates a viewing transformation matrix. + * + * @param position Position of the view point. + * @param target Position of the target. + * @param up Normalized direction of the up vector. + * + * @return Viewing transformation matrix. + */ +template +[[nodiscard]] constexpr matrix look_at(const vector& position, const vector& target, vector up); + +/** + * Multiplies two matrices + * + * @tparam T Matrix element type. + * @tparam N Number of columns in matrix @p a, and rows in matrix @p b. + * @tparam M Number of rows in matrix @p a. + * @tparam P Number of columns in matrix @p b. + * + * @param a First matrix. + * @param b Second matrix. + * + * @return Product of `a * b`. + */ +template +[[nodiscard]] constexpr matrix mul(const matrix& a, const matrix& b) noexcept; + +/** + * Multiplies a matrix by a scalar. + * + * @param a Matrix. + * @param b Scalar. + * + * @return Product of the matrix and the scalar. + */ +template +[[nodiscard]] constexpr matrix mul(const matrix& a, T b) noexcept; + +/** + * Calculates the product of a matrix and a row vector. + * + * @param a Matrix. + * @param b Row vector + * + * @return Product of the matrix and the row vector. + */ +template +[[nodiscard]] constexpr typename matrix::column_vector_type mul(const matrix& a, const typename matrix::row_vector_type& b) noexcept; + +/** + * Calculates the product of a column vector and a matrix. + * + * @param a Column vector. + * @param b Matrix. + * + * @return Product of the column vector and the matrix. + */ +template +[[nodiscard]] constexpr typename matrix::row_vector_type mul(const typename matrix::column_vector_type& a, const matrix& b) noexcept; + +/** + * Constructs a rotation matrix. + * + * @param angle Angle of rotation, in radians. + * @param axis Axis of rotation. + * + * @return Rotation matrix. + */ +template +[[nodiscard]] matrix rotate(T angle, const vector& axis); + +/** + * Produces a matrix which rotates Cartesian coordinates about the x-axis by a given angle. + * + * @param angle Angle of rotation, in radians. + * + * @return Rotation matrix. + */ +template +[[nodiscard]] matrix3 rotate_x(T angle); + +/** + * Produces a matrix which rotates Cartesian coordinates about the y-axis by a given angle. + * + * @param angle Angle of rotation, in radians. + * + * @return Rotation matrix. + */ +template +[[nodiscard]] matrix3 rotate_y(T angle); + +/** + * Produces a matrix which rotates Cartesian coordinates about the z-axis by a given angle. + * + * @param angle Angle of rotation, in radians. + * + * @return Rotation matrix. + */ +template +[[nodiscard]] matrix3 rotate_z(T angle); + +/** + * Scales a matrix. + * + * @param m Matrix to scale. + * @param v Scale vector. + * + * @return Scaled matrix. + */ +template +[[nodiscard]] constexpr matrix scale(const matrix& m, const vector& v); + +/** + * Subtracts a matrix from another matrix. + * + * @param a First matrix. + * @param b Second matrix. + * + * @return Difference between the two matrices. + */ +template +[[nodiscard]] constexpr matrix sub(const matrix& a, const matrix& b) noexcept; + +/** + * Subtracts a scalar from matrix. + * + * @param a Matrix. + * @param b Scalar. + * + * @return Difference between the matrix and scalar. + */ +template +[[nodiscard]] constexpr matrix sub(const matrix& a, T b) noexcept; + +/** + * Subtracts a matrix from a scalar. + * + * @param a Scalar. + * @param b Matrix. + * + * @return Difference between the scalar and matrix. + */ +template +[[nodiscard]] constexpr matrix sub(T a, const matrix& b) noexcept; + +/** + * Calculates the trace of a square matrix. + * + * @param m Square matrix. + * + * @return Sum of elements on the main diagonal. + */ +template +[[nodiscard]] constexpr T trace(const matrix& m) noexcept; + +/** + * Translates a matrix. + * + * @param m Matrix to translate. + * @param v Translation vector. + * + * @return Translated matrix. + */ +template +[[nodiscard]] constexpr matrix translate(const matrix& m, const vector& v); + +/** + * Calculates the transpose of a matrix. + * + * @param m Matrix to transpose. + * + * @return Transposed matrix. + */ +template +[[nodiscard]] constexpr matrix transpose(const matrix& m) noexcept; + +/// @private +template +inline constexpr matrix add(const matrix& a, const matrix& b, std::index_sequence) noexcept +{ + return {(a[I] + b[I]) ...}; +} + +template +constexpr matrix add(const matrix& a, const matrix& b) noexcept +{ + return add(a, b, std::make_index_sequence{}); +} + +/// @private +template +inline constexpr matrix add(const matrix& a, T b, std::index_sequence) noexcept +{ + return {(a[I] + b) ...}; +} + +template +constexpr matrix add(const matrix& a, T b) noexcept +{ + return add(a, b, std::make_index_sequence{}); +} + +/// @private +template +constexpr T determinant(const matrix& m) noexcept +{ + return + m[0][0] * m[1][1] - + m[0][1] * m[1][0]; +} + +/// @private +template +constexpr T determinant(const matrix& m) noexcept +{ + return + m[0][0] * m[1][1] * m[2][2] + + m[0][1] * m[1][2] * m[2][0] + + m[0][2] * m[1][0] * m[2][1] - + m[0][0] * m[1][2] * m[2][1] - + m[0][1] * m[1][0] * m[2][2] - + m[0][2] * m[1][1] * m[2][0]; +} + +/// @private +template +constexpr T determinant(const matrix& m) noexcept +{ + return + m[0][3] * m[1][2] * m[2][1] * m[3][0] - m[0][2] * m[1][3] * m[2][1] * m[3][0] - + m[0][3] * m[1][1] * m[2][2] * m[3][0] + m[0][1] * m[1][3] * m[2][2] * m[3][0] + + m[0][2] * m[1][1] * m[2][3] * m[3][0] - m[0][1] * m[1][2] * m[2][3] * m[3][0] - + m[0][3] * m[1][2] * m[2][0] * m[3][1] + m[0][2] * m[1][3] * m[2][0] * m[3][1] + + m[0][3] * m[1][0] * m[2][2] * m[3][1] - m[0][0] * m[1][3] * m[2][2] * m[3][1] - + m[0][2] * m[1][0] * m[2][3] * m[3][1] + m[0][0] * m[1][2] * m[2][3] * m[3][1] + + m[0][3] * m[1][1] * m[2][0] * m[3][2] - m[0][1] * m[1][3] * m[2][0] * m[3][2] - + m[0][3] * m[1][0] * m[2][1] * m[3][2] + m[0][0] * m[1][3] * m[2][1] * m[3][2] + + m[0][1] * m[1][0] * m[2][3] * m[3][2] - m[0][0] * m[1][1] * m[2][3] * m[3][2] - + m[0][2] * m[1][1] * m[2][0] * m[3][3] + m[0][1] * m[1][2] * m[2][0] * m[3][3] + + m[0][2] * m[1][0] * m[2][1] * m[3][3] - m[0][0] * m[1][2] * m[2][1] * m[3][3] - + m[0][1] * m[1][0] * m[2][2] * m[3][3] + m[0][0] * m[1][1] * m[2][2] * m[3][3]; +} + +/// @private +template +inline constexpr matrix componentwise_mul(const matrix& a, const matrix& b, std::index_sequence) noexcept +{ + return {(a[I] * b[I]) ...}; +} + +template +constexpr matrix componentwise_mul(const matrix& a, const matrix& b) noexcept +{ + return componentwise_mul(a, b, std::make_index_sequence{}); +} + +/// @private +template +inline constexpr matrix div(const matrix& a, const matrix& b, std::index_sequence) noexcept +{ + return {(a[I] / b[I]) ...}; +} + +template +constexpr matrix div(const matrix& a, const matrix& b) noexcept +{ + return div(a, b, std::make_index_sequence{}); +} + +/// @private +template +inline constexpr matrix div(const matrix& a, T b, std::index_sequence) noexcept +{ + return {(a[I] / b) ...}; +} + +template +constexpr matrix div(const matrix& a, T b) noexcept +{ + return div(a, b, std::make_index_sequence{}); +} + +/// @private +template +inline constexpr matrix div(T a, const matrix& b, std::index_sequence) noexcept +{ + return {(a / b[I]) ...}; +} + +template +constexpr matrix div(T a, const matrix& b) noexcept +{ + return div(a, b, std::make_index_sequence{}); +} + +template +inline constexpr typename matrix::column_vector_type& get(math::matrix& m) noexcept +{ + static_assert(I < N); + return m.columns[I]; +} + +template +inline constexpr typename matrix::column_vector_type&& get(math::matrix&& m) noexcept +{ + static_assert(I < N); + return std::move(m.columns[I]); +} + +template +inline constexpr const typename matrix::column_vector_type& get(const math::matrix& m) noexcept +{ + static_assert(I < N); + return m.columns[I]; +} + +template +inline constexpr const typename matrix::column_vector_type&& get(const math::matrix&& m) noexcept +{ + static_assert(I < N); + return std::move(m.columns[I]); +} + +/// @private +template +constexpr matrix inverse(const matrix& m) noexcept +{ + const T inv_det = T{1} / determinant(m); + + return + { + m[1][1] * inv_det, + -m[0][1] * inv_det, + -m[1][0] * inv_det, + m[0][0] * inv_det + }; +} + +/// @private +template +constexpr matrix inverse(const matrix& m) noexcept +{ + const T inv_det = T{1} / determinant(m); + + return + { + (m[1][1] * m[2][2] - m[1][2] * m[2][1]) * inv_det, + (m[0][2] * m[2][1] - m[0][1] * m[2][2]) * inv_det, + (m[0][1] * m[1][2] - m[0][2] * m[1][1]) * inv_det, + + (m[1][2] * m[2][0] - m[1][0] * m[2][2]) * inv_det, + (m[0][0] * m[2][2] - m[0][2] * m[2][0]) * inv_det, + (m[0][2] * m[1][0] - m[0][0] * m[1][2]) * inv_det, + + (m[1][0] * m[2][1] - m[1][1] * m[2][0]) * inv_det, + (m[0][1] * m[2][0] - m[0][0] * m[2][1]) * inv_det, + (m[0][0] * m[1][1] - m[0][1] * m[1][0]) * inv_det + }; +} + +/// @private +template +constexpr matrix inverse(const matrix& m) noexcept +{ + const T inv_det = T{1} / determinant(m); + + return + { + (m[1][2] * m[2][3] * m[3][1] - m[1][3] * m[2][2] * m[3][1] + m[1][3] * m[2][1] * m[3][2] - m[1][1] * m[2][3] * m[3][2] - m[1][2] * m[2][1] * m[3][3] + m[1][1] * m[2][2] * m[3][3]) * inv_det, + (m[0][3] * m[2][2] * m[3][1] - m[0][2] * m[2][3] * m[3][1] - m[0][3] * m[2][1] * m[3][2] + m[0][1] * m[2][3] * m[3][2] + m[0][2] * m[2][1] * m[3][3] - m[0][1] * m[2][2] * m[3][3]) * inv_det, + (m[0][2] * m[1][3] * m[3][1] - m[0][3] * m[1][2] * m[3][1] + m[0][3] * m[1][1] * m[3][2] - m[0][1] * m[1][3] * m[3][2] - m[0][2] * m[1][1] * m[3][3] + m[0][1] * m[1][2] * m[3][3]) * inv_det, + (m[0][3] * m[1][2] * m[2][1] - m[0][2] * m[1][3] * m[2][1] - m[0][3] * m[1][1] * m[2][2] + m[0][1] * m[1][3] * m[2][2] + m[0][2] * m[1][1] * m[2][3] - m[0][1] * m[1][2] * m[2][3]) * inv_det, + + (m[1][3] * m[2][2] * m[3][0] - m[1][2] * m[2][3] * m[3][0] - m[1][3] * m[2][0] * m[3][2] + m[1][0] * m[2][3] * m[3][2] + m[1][2] * m[2][0] * m[3][3] - m[1][0] * m[2][2] * m[3][3]) * inv_det, + (m[0][2] * m[2][3] * m[3][0] - m[0][3] * m[2][2] * m[3][0] + m[0][3] * m[2][0] * m[3][2] - m[0][0] * m[2][3] * m[3][2] - m[0][2] * m[2][0] * m[3][3] + m[0][0] * m[2][2] * m[3][3]) * inv_det, + (m[0][3] * m[1][2] * m[3][0] - m[0][2] * m[1][3] * m[3][0] - m[0][3] * m[1][0] * m[3][2] + m[0][0] * m[1][3] * m[3][2] + m[0][2] * m[1][0] * m[3][3] - m[0][0] * m[1][2] * m[3][3]) * inv_det, + (m[0][2] * m[1][3] * m[2][0] - m[0][3] * m[1][2] * m[2][0] + m[0][3] * m[1][0] * m[2][2] - m[0][0] * m[1][3] * m[2][2] - m[0][2] * m[1][0] * m[2][3] + m[0][0] * m[1][2] * m[2][3]) * inv_det, + + (m[1][1] * m[2][3] * m[3][0] - m[1][3] * m[2][1] * m[3][0] + m[1][3] * m[2][0] * m[3][1] - m[1][0] * m[2][3] * m[3][1] - m[1][1] * m[2][0] * m[3][3] + m[1][0] * m[2][1] * m[3][3]) * inv_det, + (m[0][3] * m[2][1] * m[3][0] - m[0][1] * m[2][3] * m[3][0] - m[0][3] * m[2][0] * m[3][1] + m[0][0] * m[2][3] * m[3][1] + m[0][1] * m[2][0] * m[3][3] - m[0][0] * m[2][1] * m[3][3]) * inv_det, + (m[0][1] * m[1][3] * m[3][0] - m[0][3] * m[1][1] * m[3][0] + m[0][3] * m[1][0] * m[3][1] - m[0][0] * m[1][3] * m[3][1] - m[0][1] * m[1][0] * m[3][3] + m[0][0] * m[1][1] * m[3][3]) * inv_det, + (m[0][3] * m[1][1] * m[2][0] - m[0][1] * m[1][3] * m[2][0] - m[0][3] * m[1][0] * m[2][1] + m[0][0] * m[1][3] * m[2][1] + m[0][1] * m[1][0] * m[2][3] - m[0][0] * m[1][1] * m[2][3]) * inv_det, + + (m[1][2] * m[2][1] * m[3][0] - m[1][1] * m[2][2] * m[3][0] - m[1][2] * m[2][0] * m[3][1] + m[1][0] * m[2][2] * m[3][1] + m[1][1] * m[2][0] * m[3][2] - m[1][0] * m[2][1] * m[3][2]) * inv_det, + (m[0][1] * m[2][2] * m[3][0] - m[0][2] * m[2][1] * m[3][0] + m[0][2] * m[2][0] * m[3][1] - m[0][0] * m[2][2] * m[3][1] - m[0][1] * m[2][0] * m[3][2] + m[0][0] * m[2][1] * m[3][2]) * inv_det, + (m[0][2] * m[1][1] * m[3][0] - m[0][1] * m[1][2] * m[3][0] - m[0][2] * m[1][0] * m[3][1] + m[0][0] * m[1][2] * m[3][1] + m[0][1] * m[1][0] * m[3][2] - m[0][0] * m[1][1] * m[3][2]) * inv_det, + (m[0][1] * m[1][2] * m[2][0] - m[0][2] * m[1][1] * m[2][0] + m[0][2] * m[1][0] * m[2][1] - m[0][0] * m[1][2] * m[2][1] - m[0][1] * m[1][0] * m[2][2] + m[0][0] * m[1][1] * m[2][2]) * inv_det + }; +} + +template +constexpr matrix look_at(const vector& position, const vector& target, vector up) +{ + vector forward = normalize(sub(target, position)); + vector right = normalize(cross(forward, up)); + up = cross(right, forward); + + matrix m = + {{ + {right[0], up[0], -forward[0], T(0)}, + {right[1], up[1], -forward[1], T(0)}, + {right[2], up[2], -forward[2], T(0)}, + {T(0), T(0), T(0), T(1)} + }}; + + return translate(m, negate(position)); +} + +template +constexpr matrix mul(const matrix& a, const matrix& b) noexcept +{ + matrix c = matrix::zero(); + + for (std::size_t i = 0; i < P; ++i) + { + for (std::size_t j = 0; j < M; ++j) + { + for (std::size_t k = 0; k < N; ++k) + { + c[i][j] += a[k][j] * b[i][k]; + } + } + } + + return c; +} + +/// @private +template +inline constexpr matrix mul(const matrix& a, T b, std::index_sequence) noexcept +{ + return {(a[I] * b) ...}; +} + +template +constexpr matrix mul(const matrix& a, T b) noexcept +{ + return mul(a, b, std::make_index_sequence{}); +} + +/// @private +template +inline constexpr typename matrix::column_vector_type mul(const matrix& a, const typename matrix::row_vector_type& b, std::index_sequence) noexcept +{ + return ((a[I] * b[I]) + ...); +} + +template +constexpr typename matrix::column_vector_type mul(const matrix& a, const typename matrix::row_vector_type& b) noexcept +{ + return mul(a, b, std::make_index_sequence{}); +} + +/// @private +template +inline constexpr typename matrix::row_vector_type mul(const typename matrix::column_vector_type& a, const matrix& b, std::index_sequence) noexcept +{ + return {dot(a, b[I]) ...}; +} + +template +constexpr typename matrix::row_vector_type mul(const typename matrix::column_vector_type& a, const matrix& b) noexcept +{ + return mul(a, b, std::make_index_sequence{}); +} + +template +matrix rotate(T angle, const vector& axis) +{ + const T c = std::cos(angle); + const T s = std::sin(angle); + const vector temp = mul(axis, T(1) - c); + + matrix rotation; + rotation[0][0] = axis[0] * temp[0] + c; + rotation[0][1] = axis[1] * temp[0] + axis[2] * s; + rotation[0][2] = axis[2] * temp[0] - axis[1] * s; + rotation[1][0] = axis[0] * temp[1] - axis[2] * s; + rotation[1][1] = axis[1] * temp[1] + c; + rotation[1][2] = axis[2] * temp[1] + axis[0] * s; + rotation[2][0] = axis[0] * temp[2] + axis[1] * s; + rotation[2][1] = axis[1] * temp[2] - axis[0] * s; + rotation[2][2] = axis[2] * temp[2] + c; + + return rotation; +} + +template +matrix3 rotate_x(T angle) +{ + const T c = std::cos(angle); + const T s = std::sin(angle); + + return matrix3 + { + T(1), T(0), T(0), + T(0), c, s, + T(0), -s, c + }; +} + +template +matrix3 rotate_y(T angle) +{ + const T c = std::cos(angle); + const T s = std::sin(angle); + + return matrix3 + { + c, T(0), -s, + T(0), T(1), T(0), + s, T(0), c + }; +} + +template +matrix3 rotate_z(T angle) +{ + const T c = std::cos(angle); + const T s = std::sin(angle); + + return matrix3 + { + c, s, T(0), + -s, c, T(0), + T(0), T(0), T(1) + }; +} + +template +constexpr matrix scale(const matrix& m, const vector& v) +{ + return mul(m, matrix + {{ + {v[0], T(0), T(0), T(0)}, + {T(0), v[1], T(0), T(0)}, + {T(0), T(0), v[2], T(0)}, + {T(0), T(0), T(0), T(1)} + }}); +} + +/// @private +template +inline constexpr matrix sub(const matrix& a, const matrix& b, std::index_sequence) noexcept +{ + return {(a[I] - b[I]) ...}; +} + +template +constexpr matrix sub(const matrix& a, const matrix& b) noexcept +{ + return sub(a, b, std::make_index_sequence{}); +} + +/// @private +template +inline constexpr matrix sub(const matrix& a, T b, std::index_sequence) noexcept +{ + return {(a[I] - b) ...}; +} + +template +constexpr matrix sub(const matrix& a, T b) noexcept +{ + return sub(a, b, std::make_index_sequence{}); +} + +/// @private +template +inline constexpr matrix sub(T a, const matrix& b, std::index_sequence) noexcept +{ + return {(a - b[I]) ...}; +} + +template +constexpr matrix sub(T a, const matrix& b) noexcept +{ + return sub(a, b, std::make_index_sequence{}); +} + +/// @private +template +inline constexpr T trace(const matrix& m, std::index_sequence) noexcept +{ + return ((m[I][I]) + ...); +} + +template +constexpr T trace(const matrix& m) noexcept +{ + return trace(m, std::make_index_sequence{}); +} + +template +constexpr matrix translate(const matrix& m, const vector& v) +{ + return mul(m, matrix + {{ + {T(1), T(0), T(0), T(0)}, + {T(0), T(1), T(0), T(0)}, + {T(0), T(0), T(1), T(0)}, + {v[0], v[1], v[2], T(1)} + }}); +} + +/// @private +template +inline constexpr typename matrix::column_vector_type transpose_column(const matrix& m, std::size_t i, std::index_sequence) noexcept +{ + return {m[I][i] ...}; +} + +/// @private +template +inline constexpr matrix transpose(const matrix& m, std::index_sequence) noexcept +{ + return {transpose_column(m, I, std::make_index_sequence{}) ...}; +} + +template +constexpr matrix transpose(const matrix& m) noexcept +{ + return transpose(m, std::make_index_sequence{}); +} + +namespace operators { + +/// @copydoc add(const matrix&, const matrix&) +template +inline constexpr matrix operator+(const matrix& a, const matrix& b) noexcept +{ + return add(a, b); +} + +/// @copydoc add(const matrix&, T) +/// @{ +template +inline constexpr matrix operator+(const matrix& a, T b) noexcept +{ + return add(a, b); +} +template +inline constexpr matrix operator+(T a, const matrix& b) noexcept +{ + return add(b, a); +} +/// @} + +/// @copydoc div(const matrix&, const matrix&) +template +inline constexpr matrix operator/(const matrix& a, const matrix& b) noexcept +{ + return div(a, b); +} + +/// @copydoc div(const matrix&, T) +template +inline constexpr matrix operator/(const matrix& a, T b) noexcept +{ + return div(a, b); +} + +/// @copydoc div(T, const matrix&) +template +inline constexpr matrix operator/(T a, const matrix& b) noexcept +{ + return div(a, b); +} + +/// @copydoc mul(const matrix&, const matrix&) +template +inline constexpr matrix operator*(const matrix& a, const matrix& b) noexcept +{ + return mul(a, b); +} + +/// @copydoc mul(const matrix&, T) +/// @{ +template +inline constexpr matrix operator*(const matrix& a, T b) noexcept +{ + return mul(a, b); +} +template +inline constexpr matrix operator*(T a, const matrix& b) noexcept +{ + return mul(b, a); +} +/// @} + +/// @copydoc mul(const matrix&, const typename matrix::row_vector_type&) +template +inline constexpr typename matrix::column_vector_type operator*(const matrix& a, const typename matrix::row_vector_type& b) noexcept +{ + return mul(a, b); +} + +/// @copydoc mul(const typename matrix::column_vector_type&, const matrix&) +template +inline constexpr typename matrix::row_vector_type operator*(const typename matrix::column_vector_type& a, const matrix& b) noexcept +{ + return mul(a, b); +} + +/// @copydoc sub(const matrix&, const matrix&) +template +inline constexpr matrix operator-(const matrix& a, const matrix& b) noexcept +{ + return sub(a, b); +} + +/// @copydoc sub(const matrix&, T) +template +inline constexpr matrix operator-(const matrix& a, T b) noexcept +{ + return sub(a, b); +} + +/// @copydoc sub(T, const matrix&) +template +inline constexpr matrix operator-(T a, const matrix& b) noexcept +{ + return sub(a, b); +} + +/** + * Adds two values and stores the result in the first value. + * + * @param a First value. + * @param b Second value. + * + * @return Reference to the first value. + */ +/// @{ +template +inline constexpr matrix& operator+=(matrix& a, const matrix& b) noexcept +{ + return (a = a + b); +} +template +inline constexpr matrix& operator+=(matrix& a, T b) noexcept +{ + return (a = a + b); +} +/// @} + +/** + * Subtracts the first value by the second value and stores the result in the first value. + * + * @param a First value. + * @param b Second value. + * + * @return Reference to the first value. + */ +/// @{ +template +inline constexpr matrix& operator-=(matrix& a, const matrix& b) noexcept +{ + return (a = a - b); +} +template +inline constexpr matrix& operator-=(matrix& a, T b) noexcept +{ + return (a = a - b); +} +/// @} + +/** + * Multiplies two values and stores the result in the first value. + * + * @param a First value. + * @param b Second value. + * + * @return Reference to the first value. + */ +/// @{ +template +inline constexpr matrix& operator*=(matrix& a, const matrix& b) noexcept +{ + return (a = a * b); +} +template +inline constexpr matrix& operator*=(matrix& a, T b) noexcept +{ + return (a = a * b); +} +/// @} + +/** + * Divides the first value by the second value and stores the result in the first value. + * + * @param a First value. + * @param b Second value. + * + * @return Reference to the first value. + */ +/// @{ +template +inline constexpr matrix& operator/=(matrix& a, const matrix& b) noexcept +{ + return (a = a / b); +} +template +inline constexpr matrix& operator/=(matrix& a, T b) noexcept +{ + return (a = a / b); +} +/// @} + +/** + * Writes the elements of a matrix to an output stream, with each element delimeted by a space. + * + * @param os Output stream. + * @param m Matrix. + * + * @return Output stream. + */ +template +std::ostream& operator<<(std::ostream& os, const matrix& m) +{ + for (std::size_t i = 0; i < m.size(); ++i) + { + if (i) + os << ' '; + os << m.element(i); + } + + return os; +} + +/** + * Reads the elements of a matrix from an input stream, with each element delimeted by a space. + * + * @param is Input stream. + * @param m Matrix. + * + * @return Input stream. + */ +template +std::istream& operator>>(std::istream& is, matrix& m) +{ + for (std::size_t i = 0; i < m.size(); ++i) + is >> m.element(i); + + return is; +} + +} // namespace operators + +} // namespace math + +using namespace math::operators; + +// Structured binding support +namespace std +{ + /** + * Provides access to the number of columns in a math::matrix as a compile-time constant expression. + * + * @tparam T Element type. + * @tparam N Number of columns. + * @tparam M Number of rows. + */ + template + struct tuple_size> + { + /// Number of columns in the matrix. + static constexpr std::size_t value = math::matrix::column_count; + }; + + /** + * Provides compile-time indexed access to the type of the columns in a math::matrix using a tuple-like interface. + * + * @tparam I Index of a column. + * @tparam T Element type. + * @tparam N Number of columns. + * @tparam M Number of rows. + */ + template + struct tuple_element> + { + /// Type of columns in the matrix. + using type = math::matrix::column_vector_type; + }; +} + +#endif // ANTKEEPER_MATH_MATRIX_HPP diff --git a/src/math/moving-average.hpp b/src/engine/math/moving-average.hpp similarity index 100% rename from src/math/moving-average.hpp rename to src/engine/math/moving-average.hpp diff --git a/src/engine/math/noise/fbm.hpp b/src/engine/math/noise/fbm.hpp new file mode 100644 index 0000000..60df5b4 --- /dev/null +++ b/src/engine/math/noise/fbm.hpp @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_MATH_NOISE_FBM_HPP +#define ANTKEEPER_MATH_NOISE_FBM_HPP + +#include +#include +#include +#include +#include + +namespace math { +namespace noise { + +/** + * Fractional Brownian motion (fBm). + * + * @tparam T Real type. + * @tparam N Number of dimensions. + * + * @param position Input position. + * @param octaves Number of octaves. + * @param lacunarity Frequency multiplier. + * @param gain Amplitude multiplier. + * @param noise Noise function. + * @param hash Hash function. + * + */ +template +[[nodiscard]] T fbm +( + vector position, + std::size_t octaves, + T lacunarity, + T gain, + T (*noise)(const vector&, vector, N> (*)(const vector&)) = &simplex, + vector, N> (*hash)(const vector&) = &hash::pcg +) +{ + T amplitude{1}; + T value{0}; + + while (octaves--) + { + value += noise(position, hash) * amplitude; + position *= lacunarity; + amplitude *= gain; + } + + return value; +} + +} // namespace noise +} // namespace math + +#endif // ANTKEEPER_MATH_NOISE_FBM_HPP diff --git a/src/engine/math/noise/noise.hpp b/src/engine/math/noise/noise.hpp new file mode 100644 index 0000000..49bbc46 --- /dev/null +++ b/src/engine/math/noise/noise.hpp @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_MATH_NOISE_HPP +#define ANTKEEPER_MATH_NOISE_HPP + +namespace math { + +/// Noise functions. +namespace noise {} + +} // namespace math + +#include +#include +#include + +#endif // ANTKEEPER_MATH_NOISE_HPP diff --git a/src/engine/math/noise/simplex.hpp b/src/engine/math/noise/simplex.hpp new file mode 100644 index 0000000..69d5820 --- /dev/null +++ b/src/engine/math/noise/simplex.hpp @@ -0,0 +1,222 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_MATH_NOISE_SIMPLEX_HPP +#define ANTKEEPER_MATH_NOISE_SIMPLEX_HPP + +#include +#include +#include +#include +#include + +namespace math { +namespace noise { + +/** + * Number of corners in an *n*-dimensional simplex lattice cell. + * + * @private + */ +template +constexpr std::size_t simplex_corner_count = std::size_t(2) << std::max(0, N - 1); + +/** + * Number of edges in an *n*-dimensional simplex lattice cell. + * + * @private + */ +template +constexpr std::size_t simplex_edge_count = (N > 1) ? N * simplex_corner_count : 2; + +/** + * Returns the simplex lattice cell corner vector for a given dimension and index. + * + * @private + */ +template +[[nodiscard]] constexpr vector make_simplex_corner(std::size_t i, std::index_sequence) +{ + return {((i >> I) % 2) * T{2} - T{1}...}; +} + +/** + * Builds an array of simplex lattice cell corner vectors for a given dimension. + * + * @private + */ +template +[[nodiscard]] constexpr std::array, simplex_corner_count> make_simplex_corners(std::index_sequence) +{ + return {make_simplex_corner(I, std::make_index_sequence{})...}; +} + +/** + * Array of simplex lattice cell corner vectors for a given dimension. + * + * @private + */ +template +constexpr auto simplex_corners = make_simplex_corners(std::make_index_sequence>{}); + +/** + * Returns the simplex lattice cell edge vector for a given dimension and index. + * + * @private + */ +template +[[nodiscard]] constexpr vector make_simplex_edge(std::size_t i, std::index_sequence) +{ + std::size_t j = i / (simplex_edge_count / N); + + return + { + I < j ? + simplex_corners[i % simplex_corner_count][I] + : + I > j ? + simplex_corners[i % simplex_corner_count][I - 1] + : + T{0} + ... + }; +} + +/** + * Builds an array of simplex lattice cell edge vectors for a given dimension. + * + * @private + */ +template +[[nodiscard]] constexpr std::array, simplex_edge_count> make_simplex_edges(std::index_sequence) +{ + if constexpr (N == 1) + return std::array, simplex_edge_count>{vector{T{1}}, vector{T{-1}}}; + else + return {make_simplex_edge(I, std::make_index_sequence{})...}; +} + +/** + * Array of simplex lattice cell edge vectors for a given dimension. + * + * @private + */ +template +constexpr auto simplex_edges = make_simplex_edges(std::make_index_sequence>{}); + +/** + * *n*-dimensional simplex noise. + * + * @tparam T Real type. + * @tparam N Number of dimensions. + * + * @param position Input position. + * @param hash Hash function. + * + * @return Noise value, on `[-1, 1]`. + * + * @see https://en.wikipedia.org/wiki/Simplex_noise + * @see https://catlikecoding.com/unity/tutorials/pseudorandom-noise/simplex-noise/ + * @see https://briansharpe.wordpress.com/2012/01/13/simplex-noise/ + * @see https://briansharpe.wordpress.com/2011/11/14/two-useful-interpolation-functions-for-noise-development/ + * @see https://math.stackexchange.com/questions/474638/radius-and-amplitude-of-kernel-for-simplex-noise/1901116 + */ +template +[[nodiscard]] T simplex +( + const vector& position, + vector, N> (*hash)(const vector&) = &hash::pcg +) +{ + // Skewing (F) and unskewing (G) factors + static const T f = (std::sqrt(static_cast(N + 1)) - T{1}) / static_cast(N); + static const T g = f / (T{1} + f * static_cast(N)); + + // Kernel radius set to the height of the equilateral triangle, `sqrt(0.5)` + constexpr T sqr_kernel_radius = T{0.5}; + + /** + * C2-continuous kernel falloff function. + * + * @param sqr_distance Squared distance from the kernel center. + * + * @return Kernel strength at the given distance. + */ + auto falloff = [sqr_kernel_radius](T sqr_distance) constexpr + { + sqr_distance = sqr_kernel_radius - sqr_distance; + return sqr_distance * sqr_distance * sqr_distance; + }; + + // Normalization factor when using corner gradient vectors + // @see https://math.stackexchange.com/questions/474638/radius-and-amplitude-of-kernel-for-simplex-noise/1901116 + static const T corner_normalization = T{1} / ((static_cast(N) / std::sqrt(static_cast(N + 1))) * falloff(static_cast(N) / (T{4} * static_cast(N + 1)))); + + // Adjust normalization factor for difference in length between corner gradient vectors and edge gradient vectors + static const T edge_normalization = corner_normalization * (std::sqrt(static_cast(N)) / length(simplex_edges[0])); + + // Skew input position to get the origin vertex of the unit hypercube cell to which they belong + const vector origin_vertex = floor(position + sum(position) * f); + + // Displacement vector from origin vertex position to input position + const vector dx = position - origin_vertex + sum(origin_vertex) * g; + + // Find axis traversal order + vector axis_order; + for (std::size_t i = 0; i < N; ++i) + axis_order[i] = i; + std::sort + ( + axis_order.begin(), + axis_order.end(), + [&dx](std::size_t lhs, std::size_t rhs) + { + return dx[lhs] > dx[rhs]; + } + ); + + T n = T{0}; + vector current_vertex = origin_vertex; + for (std::size_t i = 0; i <= N; ++i) + { + if (i) + ++current_vertex[axis_order[i - 1]]; + + // Calculate displacement vector from current vertex to input position + const vector d = dx - (current_vertex - origin_vertex) + g * static_cast(i); + + // Calculate falloff + T t = falloff(sqr_length(d)); + if (t > T{0}) + { + const hash::make_uint_t gradient_index = hash(current_vertex)[0] % simplex_edges.size(); + const vector& gradient = simplex_edges[gradient_index]; + + n += dot(d, gradient) * t; + } + } + + // Normalize value + return n * edge_normalization; +} + +} // namespace noise +} // namespace math + +#endif // ANTKEEPER_MATH_NOISE_SIMPLEX_HPP diff --git a/src/engine/math/noise/voronoi.hpp b/src/engine/math/noise/voronoi.hpp new file mode 100644 index 0000000..a7f494d --- /dev/null +++ b/src/engine/math/noise/voronoi.hpp @@ -0,0 +1,429 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_MATH_NOISE_VORONOI_HPP +#define ANTKEEPER_MATH_NOISE_VORONOI_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace math { +namespace noise { + +/// Voronoi functions. +namespace voronoi { + +/** + * Number of Voronoi cells to search. + * + * @tparam N Number of dimensions. + * + * @private + */ +template +constexpr std::size_t kernel_size = 4 << std::max(0, (2 * (N - 1))); + +/** + * Generates an kernel offset vector for a given index. + * + * @tparam T Real type. + * @tparam N Number of dimensions. + * @tparam I Index sequence. + * + * @param i Index of a kernel offset vector. + * + * @return Kernel offset vector. + * + * @private + */ +template +[[nodiscard]] constexpr vector kernel_offset(std::size_t i, std::index_sequence) +{ + return {static_cast((I ? (i / (2 << std::max(0, 2 * I - 1))) : i) % 4)...}; +} + +/** + * Generates a Voronoi search kernel. + * + * @tparam T Real type. + * @tparam N Number of dimensions. + * @tparam I Index sequence. + * + * @return Voronoi search kernel. + * + * @private + */ +template +[[nodiscard]] constexpr std::array, kernel_size> generate_kernel(std::index_sequence) +{ + return {kernel_offset(I, std::make_index_sequence{})...}; +} + +/** + * *n*-dimensional search kernel. + * + * @tparam T Real type. + * @tparam N Number of dimensions. + * + * @private + */ +template +constexpr auto kernel = generate_kernel(std::make_index_sequence>{}); + +/** + * Finds the Voronoi cell (F1) containing the input position. + * + * @tparam T Real type. + * @tparam N Number of dimensions. + * + * @param position Input position. + * @param randomness Degree of randomness, on `[0, 1]`. + * @param tiling Distance at which the Voronoi pattern should repeat. A value of `0` indicates no repetition. + * @param hash Hash function. + * + * @return Tuple containing the square Euclidean distance from @p position to the F1 cell, the displacement vector from the input position to the F1 cell center, and a hash value indicating the ID of the F1 cell. + */ +template +[[nodiscard]] std::tuple +< + // F1 square distance to center + T, + + // F1 position to center displacement + vector, + + // F1 hash + hash::make_uint_t +> +f1 +( + const vector& position, + T randomness = T{1}, + const vector& tiling = vector::zero(), + vector, N> (*hash)(const vector&) = &hash::pcg +) +{ + // Calculate factor which scales hash value onto `[0, 1]`, modulated by desired randomness + T hash_scale = (T{1} / static_cast(std::numeric_limits>::max())) * randomness; + + // Get integer and fractional parts + vector position_i = floor(position - T{1.5}); + vector position_f = position - position_i; + + // Find the F1 cell + T f1_sqr_distance = std::numeric_limits::infinity(); + vector f1_displacement; + hash::make_uint_t f1_hash; + for (std::size_t i = 0; i < kernel_size; ++i) + { + // Get kernel offset for current cell + const vector& offset_i = kernel[i]; + + // Calculate hash input position, tiling where specified + vector hash_position = position_i + offset_i; + for (std::size_t j = 0; j < N; ++j) + { + if (tiling[j]) + { + hash_position[j] = std::fmod(hash_position[j], tiling[j]); + if (hash_position[j] < T{0}) + hash_position[j] += tiling[j]; + } + } + + // Calculate hash values for the hash position + vector, N> hash_i = hash(hash_position); + + // Convert hash values to pseudorandom fractional offset + vector offset_f = vector(hash_i) * hash_scale; + + // Calculate displacement from input position to cell center + vector displacement = (offset_i + offset_f) - position_f; + + // Calculate square distance to the current cell center + T sqr_distance = sqr_length(displacement); + + // Update F1 cell + if (sqr_distance < f1_sqr_distance) + { + f1_sqr_distance = sqr_distance; + f1_displacement = displacement; + f1_hash = hash_i[0]; + } + } + + return + { + f1_sqr_distance, + f1_displacement, + f1_hash + }; +} + +/** + * Finds the Voronoi cell (F1) containing the input position, along with the distance to the nearest edge. + * + * @tparam T Real type. + * @tparam N Number of dimensions. + * + * @param position Input position. + * @param randomness Degree of randomness, on `[0, 1]`. + * @param tiling Distance at which the Voronoi pattern should repeat. A value of `0` indicates no repetition. + * @param hash Hash function. + * + * @return Tuple containing the square Euclidean distance from @p position to the F1 cell center, the displacement vector from the input position to the F1 cell center, a hash value indicating the ID of the F1 cell, and the square Euclidean distance from @p position to the nearest edge. + */ +template +[[nodiscard]] std::tuple +< + // F1 square distance to center + T, + + // F1 position to center displacement + vector, + + // F1 hash + hash::make_uint_t, + + // Edge square distance + T +> +f1_edge +( + const vector& position, + T randomness = T{1}, + const vector& tiling = vector::zero(), + vector, N> (*hash)(const vector&) = &hash::pcg +) +{ + // Calculate factor which scales hash value onto `[0, 1]`, modulated by desired randomness + T hash_scale = (T{1} / static_cast(std::numeric_limits>::max())) * randomness; + + // Get integer and fractional parts + vector position_i = floor(position - T{1.5}); + vector position_f = position - position_i; + + // Find F1 cell + T f1_sqr_distance_center = std::numeric_limits::infinity(); + vector displacement_cache[kernel_size]; + std::size_t f1_i = 0; + hash::make_uint_t f1_hash; + for (std::size_t i = 0; i < kernel_size; ++i) + { + // Get kernel offset for current cell + const vector& offset_i = kernel[i]; + + // Calculate hash input position, tiling where specified + vector hash_position = position_i + offset_i; + for (std::size_t j = 0; j < N; ++j) + { + if (tiling[j]) + { + hash_position[j] = std::fmod(hash_position[j], tiling[j]); + if (hash_position[j] < T{0}) + hash_position[j] += tiling[j]; + } + } + + // Calculate hash values for the hash position + vector, N> hash_i = hash(hash_position); + + // Convert hash values to pseudorandom fractional offset + vector offset_f = vector(hash_i) * hash_scale; + + // Calculate and cache displacement from input position to cell center + displacement_cache[i] = (offset_i + offset_f) - position_f; + + // Calculate square distance to the current cell center + T sqr_distance = sqr_length(displacement_cache[i]); + + // Update F1 cell + if (sqr_distance < f1_sqr_distance_center) + { + f1_sqr_distance_center = sqr_distance; + f1_i = i; + f1_hash = hash_i[0]; + } + } + + // Get displacement vector from input position to the F1 cell center + const vector& f1_displacement = displacement_cache[f1_i]; + + // Find distance to the closest edge + T edge_sqr_distance_edge = std::numeric_limits::infinity(); + for (std::size_t i = 0; i < kernel_size; ++i) + { + // Skip F1 cell + if (i == f1_i) + continue; + + // Fetch cached displacement vector for current cell + const vector& displacement = displacement_cache[i]; + + // Find midpoint between the displacement vectors + const vector midpoint = (f1_displacement + displacement) * T{0.5}; + + // Calculate direction from the F1 cell to current cell + const vector direction = normalize(displacement - f1_displacement); + + // Calculate square distance to the edge + const T sqr_distance = dot(midpoint, direction); + + // Update minimum edge distance if closer than the nearest edge + if (sqr_distance < edge_sqr_distance_edge) + edge_sqr_distance_edge = sqr_distance; + } + + return + { + f1_sqr_distance_center, + f1_displacement, + f1_hash, + edge_sqr_distance_edge + }; +} + +/** + * Finds the Voronoi cell (F1) containing the input position, as well as the nearest neighboring cell (F2). + * + * @tparam T Real type. + * @tparam N Number of dimensions. + * + * @param position Input position. + * @param randomness Degree of randomness, on `[0, 1]`. + * @param tiling Distance at which the Voronoi pattern should repeat. A value of `0` indicates no repetition. + * @param hash Hash function. + * + * @return Tuple containing the square Euclidean distances, displacement vectors from the input position to the cell centers, and hash values indicating the cell IDs, for both the F1 and F2 cells. + */ +template +[[nodiscard]] std::tuple +< + // F1 square distance to center + T, + + // F1 position to center displacement + vector, + + // F1 hash + hash::make_uint_t, + + // F2 square distance to center + T, + + // F2 position to center displacement + vector, + + // F2 hash + hash::make_uint_t +> +f1_f2 +( + const vector& position, + T randomness = T{1}, + const vector& tiling = vector::zero(), + vector, N> (*hash)(const vector&) = &hash::pcg +) +{ + // Calculate factor which scales hash value onto `[0, 1]`, modulated by desired randomness + T hash_scale = (T{1} / static_cast(std::numeric_limits>::max())) * randomness; + + // Get integer and fractional parts + vector position_i = floor(position - T{1.5}); + vector position_f = position - position_i; + + // Find the F1 and F2 cells + T f1_sqr_distance_center = std::numeric_limits::infinity(); + vector f1_displacement = {0, 0}; + hash::make_uint_t f1_hash = 0; + T f2_sqr_distance_center = std::numeric_limits::infinity(); + vector f2_displacement = {0, 0}; + hash::make_uint_t f2_hash = 0; + for (std::size_t i = 0; i < kernel_size; ++i) + { + // Get kernel offset for current cell + const vector& offset_i = kernel[i]; + + // Calculate hash input position, tiling where specified + vector hash_position = position_i + offset_i; + for (std::size_t j = 0; j < N; ++j) + { + if (tiling[j]) + { + hash_position[j] = std::fmod(hash_position[j], tiling[j]); + if (hash_position[j] < T{0}) + hash_position[j] += tiling[j]; + } + } + + // Calculate hash values for the hash position + vector, N> hash_i = hash(hash_position); + + // Convert hash values to pseudorandom fractional offset + vector offset_f = vector(hash_i) * hash_scale; + + // Calculate displacement from input position to cell center + vector displacement = (offset_i + offset_f) - position_f; + + // Calculate square distance to the current cell center + T sqr_distance = sqr_length(displacement); + + // Update F1 and F2 cells + if (sqr_distance < f1_sqr_distance_center) + { + f2_sqr_distance_center = f1_sqr_distance_center; + f2_displacement = f1_displacement; + f2_hash = f1_hash; + + f1_sqr_distance_center = sqr_distance; + f1_displacement = displacement; + f1_hash = hash_i[0]; + } + else if (sqr_distance < f2_sqr_distance_center) + { + f2_sqr_distance_center = sqr_distance; + f2_displacement = displacement; + f2_hash = hash_i[0]; + } + } + + return + { + f1_sqr_distance_center, + f1_displacement, + f1_hash, + f2_sqr_distance_center, + f2_displacement, + f2_hash, + }; +} + +} // namespace voronoi + +} // namespace noise +} // namespace math + +#endif // ANTKEEPER_MATH_NOISE_VORONOI_HPP diff --git a/src/math/numbers.hpp b/src/engine/math/numbers.hpp similarity index 100% rename from src/math/numbers.hpp rename to src/engine/math/numbers.hpp diff --git a/src/math/operators.hpp b/src/engine/math/operators.hpp similarity index 100% rename from src/math/operators.hpp rename to src/engine/math/operators.hpp diff --git a/src/engine/math/polynomial.hpp b/src/engine/math/polynomial.hpp new file mode 100644 index 0000000..564c0cc --- /dev/null +++ b/src/engine/math/polynomial.hpp @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_MATH_POLYNOMIAL_HPP +#define ANTKEEPER_MATH_POLYNOMIAL_HPP + +#include +#include + +namespace math { + +/// Polynomial functions. +namespace polynomial { + +/** + * Evaluates a polynomial using Horner's method. + * + * @param first,last Range of polynomial coefficients, in descending order of degree. + * @param x Variable value. + * @return Evaluation of P(x). + * + * @see https://en.wikipedia.org/wiki/Horner%27s_method + */ +template +[[nodiscard]] constexpr T horner(InputIt first, InputIt last, T x) +{ + T y = *first; + for (++first; first != last; ++first) + y = y * x + *first; + return y; +} + +/** Chebychev polynomials. + * + * @see https://en.wikipedia.org/wiki/Chebyshev_polynomials + */ +namespace chebyshev { + + /** + * Evaluates a Chebyshev polynomial. + * + * @param[in] first,last Range of Chebychev polynomial coefficients. + * @param[in] x Value on the interval `[-1, 1]`. + * + * @return Evaluated value. + */ + template + [[nodiscard]] T evaluate(InputIt first, InputIt last, T x) + { + T y = *(first++); + y += *(first++) * x; + + T n2 = T(1), n1 = x, n0; + x *= T(2); + + for (; first != last; n2 = n1, n1 = n0) + { + n0 = x * n1 - n2; + y += *(first++) * n0; + } + + return y; + } + + /** + * Evaluates a Chebyshev polynomial. + * + * @param first,last Range of Chebychev polynomial coefficients. + * @param min,max Domain of the approximated function. + * @param x Value on the interval `[min, max]`. + * + * @return Evaluated value. + */ + template + [[nodiscard]] T evaluate(InputIt first, InputIt last, T min, T max, T x) + { + return evaluate(first, last, math::map(x, min, max, T(-1), T(1))); + } + +} // namespace chebyshev + +} // namespace polynomial +} // namespace math + +#endif // ANTKEEPER_MATH_POLYNOMIAL_HPP diff --git a/src/engine/math/projection.hpp b/src/engine/math/projection.hpp new file mode 100644 index 0000000..531c1fc --- /dev/null +++ b/src/engine/math/projection.hpp @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_MATH_PROJECTION_HPP +#define ANTKEEPER_MATH_PROJECTION_HPP + +#include +#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 +[[nodiscard]] 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 +[[nodiscard]] T vertical_fov(T h, T r) +{ + return T{2} * std::atan(std::tan(h * T{0.5}) / r); +} + +/** + * Creates an orthographic projection matrix which will transform the near and far clipping planes to `[-1, 1]`, respectively. + * + * @param left Signed distance to the left clipping plane. + * @param right Signed distance to the right clipping plane. + * @param bottom Signed distance to the bottom clipping plane. + * @param top Signed distance to the top clipping plane. + * @param z_near Signed distance to the near clipping plane. + * @param z_far Signed distance to the far clipping plane. + * + * @return Orthographic projection matrix. + */ +template +[[nodiscard]] constexpr matrix ortho(T left, T right, T bottom, T top, T z_near, T z_far) noexcept +{ + return + {{ + {T(2) / (right - left), T(0), T(0), T(0)}, + {T(0), T(2) / (top - bottom), T(0), T(0)}, + {T(0), T(0), T(-2) / (z_far - z_near), T(0)}, + {-((right + left) / (right - left)), -((top + bottom) / (top - bottom)), -((z_far + z_near) / (z_far - z_near)), T(1)} + }}; +} + +/** + * Creates an orthographic projection matrix which will transform the near and far clipping planes to `[0, 1]`, respectively. + * + * @param left Signed distance to the left clipping plane. + * @param right Signed distance to the right clipping plane. + * @param bottom Signed distance to the bottom clipping plane. + * @param top Signed distance to the top clipping plane. + * @param z_near Signed distance to the near clipping plane. + * @param z_far Signed distance to the far clipping plane. + * + * @return Orthographic projection matrix. + */ +template +[[nodiscard]] constexpr matrix ortho_half_z(T left, T right, T bottom, T top, T z_near, T z_far) noexcept +{ + return + {{ + {T(2) / (right - left), T(0), T(0), T(0)}, + {T(0), T(2) / (top - bottom), T(0), T(0)}, + {T(0), T(0), T(-1) / (z_far - z_near), T(0)}, + {-((right + left) / (right - left)), -((top + bottom) / (top - bottom)), -z_near / (z_far - z_near), T(1)} + }}; +} + +/** + * Creates a perspective projection matrix which will transform the near and far clipping planes to `[-1, 1]`, respectively. + * + * @param vertical_fov Vertical field of view angle, in radians. + * @param aspect_ratio Aspect ratio which determines the horizontal field of view. + * @param z_near Distance to the near clipping plane. + * @param z_far Distance to the far clipping plane. + * + * @return Perspective projection matrix. + */ +template +[[nodiscard]] matrix perspective(T vertical_fov, T aspect_ratio, T z_near, T z_far) +{ + T half_fov = vertical_fov * T(0.5); + T f = std::cos(half_fov) / std::sin(half_fov); + + return + {{ + {f / aspect_ratio, T(0), T(0), T(0)}, + {T(0), f, T(0), T(0)}, + {T(0), T(0), (z_far + z_near) / (z_near - z_far), T(-1)}, + {T(0), T(0), (T(2) * z_far * z_near) / (z_near - z_far), T(0)} + }}; +} + +/** + * Creates a perspective projection matrix which will transform the near and far clipping planes to `[0, 1]`, respectively. + * + * @param vertical_fov Vertical field of view angle, in radians. + * @param aspect_ratio Aspect ratio which determines the horizontal field of view. + * @param z_near Distance to the near clipping plane. + * @param z_far Distance to the far clipping plane. + * + * @return Perspective projection matrix. + */ +template +[[nodiscard]] matrix perspective_half_z(T vertical_fov, T aspect_ratio, T z_near, T z_far) +{ + T half_fov = vertical_fov * T(0.5); + T f = std::cos(half_fov) / std::sin(half_fov); + + return + {{ + {f / aspect_ratio, T(0), T(0), T(0)}, + {T(0), f, T(0), T(0)}, + {T(0), T(0), z_far / (z_near - z_far), T(-1)}, + {T(0), T(0), -(z_far * z_near) / (z_far - z_near), T(0)} + }}; +} + +} // namespace math + +#endif // ANTKEEPER_MATH_PROJECTION_HPP diff --git a/src/math/quadrature.hpp b/src/engine/math/quadrature.hpp similarity index 100% rename from src/math/quadrature.hpp rename to src/engine/math/quadrature.hpp diff --git a/src/engine/math/quaternion.hpp b/src/engine/math/quaternion.hpp new file mode 100644 index 0000000..0d65aa1 --- /dev/null +++ b/src/engine/math/quaternion.hpp @@ -0,0 +1,969 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_MATH_QUATERNION_HPP +#define ANTKEEPER_MATH_QUATERNION_HPP + +#include +#include +#include +#include +#include +#include + +namespace math { + +/** + * Quaternion composed of a real scalar part and imaginary vector part. + * + * @tparam T Scalar type. + */ +template +struct quaternion +{ + /// Scalar type. + typedef T scalar_type; + + /// Vector type. + typedef vector vector_type; + + /// Rotation matrix type. + typedef matrix matrix_type; + + /// Quaternion real part. + scalar_type r; + + /// Quaternion imaginary part. + vector_type i; + + /// Returns a reference to the quaternion real part. + /// @{ + [[nodiscard]] inline constexpr scalar_type& w() noexcept + { + return r; + } + [[nodiscard]] inline constexpr const scalar_type& w() const noexcept + { + return r; + } + /// @} + + /// Returns a reference to the first element of the quaternion imaginary part. + /// @{ + [[nodiscard]] inline constexpr scalar_type& x() noexcept + { + return i.x(); + } + [[nodiscard]] inline constexpr const scalar_type& x() const noexcept + { + return i.x(); + } + /// @} + + /// Returns a reference to the second element of the quaternion imaginary part. + /// @{ + [[nodiscard]] inline constexpr scalar_type& y() noexcept + { + return i.y(); + } + [[nodiscard]] inline constexpr const scalar_type& y() const noexcept + { + return i.y(); + } + /// @} + + /// Returns a reference to the third element of the quaternion imaginary part. + /// @{ + [[nodiscard]] inline constexpr scalar_type& z() noexcept + { + return i.z(); + } + [[nodiscard]] inline constexpr const scalar_type& z() const noexcept + { + return i.z(); + } + /// @} + + /** + * Returns a quaternion representing a rotation about the x-axis. + * + * @param angle Angle of rotation, in radians. + * + * @return Quaternion representing an x-axis rotation. + */ + [[nodiscard]] static quaternion rotate_x(scalar_type angle) + { + return {std::cos(angle * T(0.5)), std::sin(angle * T(0.5)), T(0), T(0)}; + } + + /** + * Returns a quaternion representing a rotation about the y-axis. + * + * @param angle Angle of rotation, in radians. + * + * @return Quaternion representing an y-axis rotation. + */ + [[nodiscard]] static quaternion rotate_y(scalar_type angle) + { + return {std::cos(angle * T(0.5)), T(0), std::sin(angle * T(0.5)), T(0)}; + } + + /** + * Returns a quaternion representing a rotation about the z-axis. + * + * @param angle Angle of rotation, in radians. + * @return Quaternion representing an z-axis rotation. + */ + [[nodiscard]] static quaternion rotate_z(scalar_type angle) + { + return {std::cos(angle * T(0.5)), T(0), T(0), std::sin(angle * T(0.5))}; + } + + /** + * Type-casts the quaternion scalars using `static_cast`. + * + * @tparam U Target scalar type. + * + * @return Type-casted quaternion. + */ + template + [[nodiscard]] inline constexpr explicit operator quaternion() const noexcept + { + return {static_cast(r), vector(i)}; + } + + /** + * Constructs a matrix representing the rotation described the quaternion. + * + * @return Rotation matrix. + */ + [[nodiscard]] constexpr explicit operator matrix_type() const noexcept + { + const T xx = x() * x(); + const T xy = x() * y(); + const T xz = x() * z(); + const T xw = x() * w(); + const T yy = y() * y(); + const T yz = y() * z(); + const T yw = y() * w(); + const T zz = z() * z(); + const T zw = z() * w(); + + return + { + T(1) - (yy + zz) * T(2), (xy + zw) * T(2), (xz - yw) * T(2), + (xy - zw) * T(2), T(1) - (xx + zz) * T(2), (yz + xw) * T(2), + (xz + yw) * T(2), (yz - xw) * T(2), T(1) - (xx + yy) * T(2) + }; + } + + /** + * Casts the quaternion to a 4-element vector, with the real part as the first element and the imaginary part as the following three elements. + * + * @return Vector containing the real and imaginary parts of the quaternion. + */ + [[nodiscard]] inline constexpr explicit operator vector() const noexcept + { + return {r, i[0], i[1], i[2]}; + } + + /// Returns a zero quaternion, where every scalar is equal to zero. + [[nodiscard]] static constexpr quaternion zero() noexcept + { + return {}; + } + + /// Returns a rotation identity quaternion. + [[nodiscard]] static constexpr quaternion identity() noexcept + { + return {T{1}, vector_type::zero()}; + } +}; + +/** + * Adds two quaternions. + * + * @param a First quaternion. + * @param b Second quaternion. + * + * @return Sum of the two quaternions. + */ +template +[[nodiscard]] constexpr quaternion add(const quaternion& a, const quaternion& b) noexcept; + +/** + * Adds a quaternion and a scalar. + * + * @param a First value. + * @param b Second second value. + * + * @return Sum of the quaternion and scalar. + */ +template +[[nodiscard]] constexpr quaternion add(const quaternion& a, T b) noexcept; + +/** + * Calculates the conjugate of a quaternion. + * + * @param q Quaternion from which the conjugate will be calculated. + * + * @return Conjugate of the quaternion. + */ +template +[[nodiscard]] constexpr quaternion conjugate(const quaternion& q) noexcept; + +/** + * Calculates the dot product of two quaternions. + * + * @param a First quaternion. + * @param b Second quaternion. + * + * @return Dot product of the two quaternions. + */ +template +[[nodiscard]] constexpr T dot(const quaternion& a, const quaternion& b) noexcept; + +/** + * Divides a quaternion by another quaternion. + * + * @param a First value. + * @param b Second value. + * + * @return Result of the division. + */ +template +[[nodiscard]] constexpr quaternion div(const quaternion& a, const quaternion& b) noexcept; + +/** + * Divides a quaternion by a scalar. + * + * @param a Quaternion. + * @param b Scalar. + * + * @return Result of the division. + */ +template +[[nodiscard]] constexpr quaternion div(const quaternion& a, T b) noexcept; + +/** + * Divides a scalar by a quaternion. + * + * @param a Scalar. + * @param b Quaternion. + * + * @return Result of the division. + */ +template +[[nodiscard]] constexpr quaternion div(T a, const quaternion& b) noexcept; + +/** + * Calculates the inverse length of a quaternion. + * + * @param q Quaternion to calculate the inverse length of. + * + * @return Inverse length of the quaternion. + */ +template +[[nodiscard]] T inv_length(const quaternion& q); + +/** + * Calculates the length of a quaternion. + * + * @param q Quaternion to calculate the length of. + * + * @return Length of the quaternion. + */ +template +[[nodiscard]] T length(const quaternion& q); + +/** + * Performs linear interpolation between two quaternions. + * + * @param a First quaternion. + * @param b Second quaternion. + * @param t Interpolation factor. + * + * @return Interpolated quaternion. + */ +template +[[nodiscard]] constexpr quaternion lerp(const quaternion& a, const quaternion& b, T t) noexcept; + +/** + * Creates a unit quaternion rotation using forward and up vectors. + * + * @param forward Unit forward vector. + * @param up Unit up vector. + * + * @return Unit rotation quaternion. + */ +template +[[nodiscard]] quaternion look_rotation(const vector& forward, vector up); + +/** + * Multiplies two quaternions. + * + * @param a First quaternion. + * @param b Second quaternion. + * + * @return Product of the two quaternions. + */ +template +[[nodiscard]] constexpr quaternion mul(const quaternion& a, const quaternion& b) noexcept; + +/** + * Multiplies a quaternion by a scalar. + * + * @param a First value. + * @param b Second value. + * + * @return Product of the quaternion and scalar. + */ +template +[[nodiscard]] constexpr quaternion mul(const quaternion& a, T b) noexcept; + +/** + * Calculates the product of a quaternion and a vector. + * + * @param a First value. + * @param b second value. + * + * @return Product of the quaternion and vector. + */ +/// @{ +template +[[nodiscard]] constexpr vector mul(const quaternion& a, const vector& b) noexcept; +template +[[nodiscard]] constexpr vector mul(const vector& a, const quaternion& b) noexcept; +/// @} + +/** + * Negates a quaternion. + * + * @param q Quaternion to negate. + * + * @return Negated quaternion. + */ +template +[[nodiscard]] constexpr quaternion negate(const quaternion& q) noexcept; + +/** + * Performs normalized linear interpolation between two quaternions. + * + * @param a First quaternion. + * @param b Second quaternion. + * @param t Interpolation factor. + * + * @return Interpolated quaternion. + */ +template +[[nodiscard]] quaternion nlerp(const quaternion& a, const quaternion& b, T t); + +/** + * Normalizes a quaternion. + * + * @param q Quaternion to normalize. + * + * @return Normalized quaternion. + */ +template +[[nodiscard]] quaternion normalize(const quaternion& q); + +/** + * Creates a rotation from an angle and axis. + * + * @param angle Angle of rotation (in radians). + * @param axis Axis of rotation + * + * @return Quaternion representing the rotation. + */ +template +[[nodiscard]] quaternion angle_axis(T angle, const vector& axis); + +/** + * Calculates the minimum rotation between two normalized direction vectors. + * + * @param source Normalized source direction. + * @param destination Normalized destination direction. + * + * @return Quaternion representing the minimum rotation between the source and destination vectors. + */ +template +[[nodiscard]] quaternion rotation(const vector& source, const vector& destination); + +/** + * Performs spherical linear interpolation between two quaternions. + * + * @param a First quaternion. + * @param b Second quaternion. + * @param t Interpolation factor. + * + * @return Interpolated quaternion. + */ +template +[[nodiscard]] quaternion slerp(const quaternion& a, const quaternion& b, T t, T error = T{1e-6}); + +/** + * Calculates the square length of a quaternion. The square length can be calculated faster than the length because a call to `std::sqrt` is saved. + * + * @param q Quaternion to calculate the square length of. + * + * @return Square length of the quaternion. + */ +template +[[nodiscard]] constexpr T sqr_length(const quaternion& q) noexcept; + +/** + * Subtracts a quaternion from another quaternion. + * + * @param a First quaternion. + * @param b Second quaternion. + * + * @return Difference between the quaternions. + */ +template +[[nodiscard]] constexpr quaternion sub(const quaternion& a, const quaternion& b) noexcept; + +/** + * Subtracts a quaternion and a scalar. + * + * @param a First value. + * @param b Second second. + * + * @return Difference between the quaternion and scalar. + */ +/// @{ +template +[[nodiscard]] constexpr quaternion sub(const quaternion& a, T b) noexcept; +template +[[nodiscard]] constexpr quaternion sub(T a, const quaternion& b) noexcept; +/// @} + +/** + * Decomposes a quaternion into swing and twist rotation components. + * + * @param[in] q Quaternion to decompose. + * @param[in] a Axis of twist rotation. + * @param[out] swing Swing rotation component. + * @param[out] twist Twist rotation component. + * @param[in] error Threshold at which a number is considered zero. + * + * @see https://www.euclideanspace.com/maths/geometry/rotations/for/decomposition/ + */ +template +void swing_twist(const quaternion& q, const vector& a, quaternion& qs, quaternion& qt, T error = T{1e-6}); + +/** + * Converts a 3x3 rotation matrix to a quaternion. + * + * @param m Rotation matrix. + * + * @return Unit quaternion representing the rotation described by @p m. + */ +template +[[nodiscard]] quaternion quaternion_cast(const matrix& m); + +template +inline constexpr quaternion add(const quaternion& a, const quaternion& b) noexcept +{ + return {a.r + b.r, a.i + b.i}; +} + +template +inline constexpr quaternion add(const quaternion& a, T b) noexcept +{ + return {a.r + b, a.i + b}; +} + +template +inline constexpr quaternion conjugate(const quaternion& q) noexcept +{ + return {q.r, -q.i}; +} + +template +inline constexpr T dot(const quaternion& a, const quaternion& b) noexcept +{ + return a.r * b.r + dot(a.i, b.i); +} + +template +inline constexpr quaternion div(const quaternion& a, const quaternion& b) noexcept +{ + return {a.r / b.r, a.i / b.i}; +} + +template +inline constexpr quaternion div(const quaternion& a, T b) noexcept +{ + return {a.r / b, a.i / b}; +} + +template +inline constexpr quaternion div(T a, const quaternion& b) noexcept +{ + return {a / b.r, a / b.i}; +} + +template +inline T inv_length(const quaternion& q) +{ + return T{1} / length(q); +} + +template +inline T length(const quaternion& q) +{ + return std::sqrt(sqr_length(q)); +} + +template +inline constexpr quaternion lerp(const quaternion& a, const quaternion& b, T t) noexcept +{ + return + { + (b.r - a.r) * t + a.r, + (b.i - a.i) * t + a.i + }; +} + +template +quaternion look_rotation(const vector& forward, vector up) +{ + vector right = normalize(cross(forward, up)); + up = cross(right, forward); + + matrix m = + { + right, + up, + -forward + }; + + // Convert to quaternion + return normalize(quaternion_cast(m)); +} + +template +constexpr quaternion mul(const quaternion& a, const quaternion& b) noexcept +{ + return + { + -a.x() * b.x() - a.y() * b.y() - a.z() * b.z() + a.w() * b.w(), + a.x() * b.w() + a.y() * b.z() - a.z() * b.y() + a.w() * b.x(), + -a.x() * b.z() + a.y() * b.w() + a.z() * b.x() + a.w() * b.y(), + a.x() * b.y() - a.y() * b.x() + a.z() * b.w() + a.w() * b.z() + }; +} + +template +inline constexpr quaternion mul(const quaternion& a, T b) noexcept +{ + return {a.r * b, a.i * b}; +} + +template +constexpr vector mul(const quaternion& a, const vector& b) noexcept +{ + return a.i * dot(a.i, b) * T(2) + b * (a.r * a.r - sqr_length(a.i)) + cross(a.i, b) * a.r * T(2); +} + +template +inline constexpr vector mul(const vector& a, const quaternion& b) noexcept +{ + return mul(conjugate(b), a); +} + +template +inline constexpr quaternion negate(const quaternion& q) noexcept +{ + return {-q.r, -q.i}; +} + +template +quaternion nlerp(const quaternion& a, const quaternion& b, T t) +{ + return normalize(add(mul(a, T(1) - t), mul(b, t * std::copysign(T(1), dot(a, b))))); +} + +template +inline quaternion normalize(const quaternion& q) +{ + return mul(q, inv_length(q)); +} + +template +quaternion angle_axis(T angle, const vector& axis) +{ + angle *= T{0.5}; + return {std::cos(angle), axis * std::sin(angle)}; +} + +template +quaternion rotation(const vector& source, const vector& destination) +{ + quaternion q = {dot(source, destination), cross(source, destination)}; + q.w() += length(q); + return normalize(q); +} + +template +quaternion slerp(const quaternion& a, const quaternion& b, T t, T error) +{ + T cos_theta = dot(a, b); + + if (cos_theta > T(1) - error) + return normalize(lerp(a, b, t)); + + cos_theta = std::max(T(-1), std::min(T(1), cos_theta)); + const T theta = std::acos(cos_theta) * t; + + quaternion c = normalize(sub(b, mul(a, cos_theta))); + + return add(mul(a, std::cos(theta)), mul(c, std::sin(theta))); +} + +template +inline constexpr T sqr_length(const quaternion& q) noexcept +{ + return q.r * q.r + sqr_length(q.i); +} + +template +inline constexpr quaternion sub(const quaternion& a, const quaternion& b) noexcept +{ + return {a.r - b.r, a.i - b.i}; +} + +template +inline constexpr quaternion sub(const quaternion& a, T b) noexcept +{ + return {a.r - b, a.i - b}; +} + +template +inline constexpr quaternion sub(T a, const quaternion& b) noexcept +{ + return {a - b.r, a - b.i}; +} + +template +void swing_twist(const quaternion& q, const vector& a, quaternion& qs, quaternion& qt, T error) +{ + if (sqr_length(q.i) > error) + { + qt = normalize(quaternion{q.w(), a * dot(a, q.i)}); + qs = mul(q, conjugate(qt)); + } + else + { + qt = angle_axis(pi, a); + + const vector qa = mul(q, a); + const vector sa = cross(a, qa); + if (sqr_length(sa) > error) + qs = angle_axis(std::acos(dot(a, qa)), sa); + else + qs = quaternion::identity(); + } +} + +template +quaternion quaternion_cast(const matrix& m) +{ + const T t = trace(m); + + if (t > T(0)) + { + T s = T(0.5) / std::sqrt(t + T(1)); + return + { + T(0.25) / s, + (m[1][2] - m[2][1]) * s, + (m[2][0] - m[0][2]) * s, + (m[0][1] - m[1][0]) * s + }; + } + else + { + if (m[0][0] > m[1][1] && m[0][0] > m[2][2]) + { + T s = T(2) * std::sqrt(T(1) + m[0][0] - m[1][1] - m[2][2]); + + return + { + (m[1][2] - m[2][1]) / s, + T(0.25) * s, + (m[1][0] + m[0][1]) / s, + (m[2][0] + m[0][2]) / s + }; + } + else if (m[1][1] > m[2][2]) + { + T s = T(2) * std::sqrt(T(1) + m[1][1] - m[0][0] - m[2][2]); + return + { + (m[2][0] - m[0][2]) / s, + (m[1][0] + m[0][1]) / s, + T(0.25) * s, + (m[2][1] + m[1][2]) / s + }; + } + else + { + T s = T(2) * std::sqrt(T(1) + m[2][2] - m[0][0] - m[1][1]); + return + { + (m[0][1] - m[1][0]) / s, + (m[2][0] + m[0][2]) / s, + (m[2][1] + m[1][2]) / s, + T(0.25) * s + }; + } + } +} + +namespace operators { + +/// @copydoc add(const quaternion&, const quaternion&) +template +inline constexpr quaternion operator+(const quaternion& a, const quaternion& b) noexcept +{ + return add(a, b); +} + +/// @copydoc add(const quaternion&, T) +/// @{ +template +inline constexpr quaternion operator+(const quaternion& a, T b) noexcept +{ + return add(a, b); +} +template +inline constexpr quaternion operator+(T a, const quaternion& b) noexcept +{ + return add(b, a); +} +/// @} + +/// @copydoc div(const quaternion&, const quaternion&) +template +inline constexpr quaternion operator/(const quaternion& a, const quaternion& b) noexcept +{ + return div(a, b); +} + +/// @copydoc div(const quaternion&, T) +template +inline constexpr quaternion operator/(const quaternion& a, T b) noexcept +{ + return div(a, b); +} + +/// @copydoc div(T, const quaternion&) +template +inline constexpr quaternion operator/(T a, const quaternion& b) noexcept +{ + return div(a, b); +} + +/// @copydoc mul(const quaternion&, const quaternion&) +template +inline constexpr quaternion operator*(const quaternion& a, const quaternion& b) noexcept +{ + return mul(a, b); +} + +/// @copydoc mul(const quaternion&, T) +/// @{ +template +inline constexpr quaternion operator*(const quaternion& a, T b) noexcept +{ + return mul(a, b); +} +template +inline constexpr quaternion operator*(T a, const quaternion& b) noexcept +{ + return mul(b, a); +} +/// @} + +/// @copydoc mul(const quaternion&, const vector&) +template +inline constexpr vector operator*(const quaternion& a, const vector& b) noexcept +{ + return mul(a, b); +} + +/// @copydoc mul(const vector&, const quaternion&) +template +inline constexpr vector operator*(const vector& a, const quaternion& b) noexcept +{ + return mul(a, b); +} + +/// @copydoc sub(const quaternion&, const quaternion&) +template +inline constexpr quaternion operator-(const quaternion& a, const quaternion& b) noexcept +{ + return sub(a, b); +} + +/// @copydoc sub(const quaternion&, T) +/// @{ +template +inline constexpr quaternion operator-(const quaternion& a, T b) noexcept +{ + return sub(a, b); +} +template +inline constexpr quaternion operator-(T a, const quaternion& b) noexcept +{ + return sub(a, b); +} +/// @} + +/// @copydoc negate(const quaternion&) +template +inline constexpr quaternion operator-(const quaternion& q) noexcept +{ + return negate(q); +} + +/** + * Adds two values and stores the result in the first value. + * + * @param a First value. + * @param b Second value. + * + * @return Reference to the first value. + */ +/// @{ +template +inline constexpr quaternion& operator+=(quaternion& a, const quaternion& b) noexcept +{ + return (a = a + b); +} +template +inline constexpr quaternion& operator+=(quaternion& a, T b) noexcept +{ + return (a = a + b); +} +/// @} + +/** + * Subtracts the first value by the second value and stores the result in the first value. + * + * @param a First value. + * @param b Second value. + * + * @return Reference to the first value. + */ +/// @{ +template +inline constexpr quaternion& operator-=(quaternion& a, const quaternion& b) noexcept +{ + return (a = a - b); +} +template +inline constexpr quaternion& operator-=(quaternion& a, T b) noexcept +{ + return (a = a - b); +} +/// @} + +/** + * Multiplies two values and stores the result in the first value. + * + * @param a First value. + * @param b Second value. + * + * @return Reference to the first value. + */ +/// @{ +template +inline constexpr quaternion& operator*=(quaternion& a, const quaternion& b) noexcept +{ + return (a = a * b); +} +template +inline constexpr quaternion& operator*=(quaternion& a, T b) noexcept +{ + return (a = a * b); +} +/// @} + +/** + * Divides the first value by the second value and stores the result in the first value. + * + * @param a First value. + * @param b Second value. + * + * @return Reference to the first value. + */ +/// @{ +template +inline constexpr quaternion& operator/=(quaternion& a, const quaternion& b) noexcept +{ + return (a = a / b); +} +template +inline constexpr quaternion& operator/=(quaternion& a, T b) noexcept +{ + return (a = a / b); +} +/// @} + +/** + * Writes the real and imaginary parts of a quaternion to an output stream, with each number delimeted by a space. + * + * @param os Output stream. + * @param q Quaternion. + * + * @return Output stream. + */ +template +std::ostream& operator<<(std::ostream& os, const math::quaternion& q) +{ + os << q.r << ' ' << q.i; + return os; +} + +/** + * Reads the real and imaginary parts of a quaternion from an input stream, with each number delimeted by a space. + * + * @param is Input stream. + * @param q Quaternion. + * + * @return Input stream. + */ +template +std::istream& operator>>(std::istream& is, const math::quaternion& q) +{ + is >> q.r; + is >> q.i; + return is; +} + +} // namespace operators + +} // namespace math + +using namespace math::operators; + +#endif // ANTKEEPER_MATH_QUATERNION_HPP diff --git a/src/math/random.hpp b/src/engine/math/random.hpp similarity index 100% rename from src/math/random.hpp rename to src/engine/math/random.hpp diff --git a/src/engine/math/se3.hpp b/src/engine/math/se3.hpp new file mode 100644 index 0000000..bec4f3e --- /dev/null +++ b/src/engine/math/se3.hpp @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_MATH_TRANSFORMATION_SE3_HPP +#define ANTKEEPER_MATH_TRANSFORMATION_SE3_HPP + +#include +#include + +namespace math { +namespace transformation { + +/** + * 3-dimensional Euclidean proper rigid transformation in SE(3). + * + * @tparam T Scalar type. + */ +template +struct se3 +{ +public: + /// Scalar type. + typedef T scalar_type; + + /// Vector type. + typedef math::vector3 vector_type; + + /// Quaternion type. + typedef math::quaternion quaternion_type; + + /// Transformation matrix type. + typedef math::matrix matrix_type; + + /// Vector representing the translation component of this SE(3) transformation. + vector_type t; + + /// Quaternion representing the rotation component of this SE(3) transformation. + quaternion_type r; + + /// Returns the inverse of this SE(3) transformation. + [[nodiscard]] se3 inverse() const; + + /// Returns a matrix representation of the SE(3) transformation. + [[nodiscard]] matrix_type matrix() const; + + /** + * Transforms a vector by this SE(3) transformation. + * + * @param x Untransformed vector. + * @return Transformed vector. + */ + [[nodiscard]] vector_type transform(const vector_type& x) const; + + /** + * Transforms an SE(3) transformation by this SE(3) transformation. + * + * @param x Other SE(3) transformation. + * @return Frame in this se3's space. + */ + [[nodiscard]] se3 transform(const se3& x) const; + + /// @copydoc se3::transform(const vector_type&) const + [[nodiscard]] vector_type operator*(const vector_type& x) const; + + /// @copydoc se3::transform(const se3&) const + [[nodiscard]] se3 operator*(const se3& x) const; +}; + +template +se3 se3::inverse() const +{ + const quaternion_type inverse_r = math::conjugate(r); + const vector_type inverse_t = -(inverse_r * t); + return se3{inverse_t, inverse_r}; +} + +template +typename se3::matrix_type se3::matrix() const +{ + matrix_type m = math::matrix(math::matrix(r)); + + m[3].x() = t.x(); + m[3].y() = t.y(); + m[3].z() = t.z(); + + return m; +} + +template +typename se3::vector_type se3::transform(const vector_type& x) const +{ + return r * x + t; +} + +template +se3 se3::transform(const se3& x) const +{ + return se3 + { + x.transform(t), + math::normalize(x.r * r) + }; +} + +template +typename se3::vector_type se3::operator*(const vector_type& x) const +{ + return transform(x); +} + +template +se3 se3::operator*(const se3& x) const +{ + return transform(x); +} + +} // namespace transformation +} // namespace math + +#endif // ANTKEEPER_MATH_TRANSFORMATION_SE3_HPP diff --git a/src/engine/math/transform-functions.hpp b/src/engine/math/transform-functions.hpp new file mode 100644 index 0000000..c6b16b4 --- /dev/null +++ b/src/engine/math/transform-functions.hpp @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_MATH_TRANSFORM_FUNCTIONS_HPP +#define ANTKEEPER_MATH_TRANSFORM_FUNCTIONS_HPP + +#include +#include + +namespace math { + +/** + * Calculates the inverse of a transform. + * + * @param t Transform of which to take the inverse. + */ +template +[[nodiscard]] transform inverse(const transform& t); + +/** + * Converts a transform to a transformation matrix. + * + * @param t Transform. + * @return Matrix representing the transformation described by `t`. + */ +template +[[nodiscard]] matrix matrix_cast(const transform& t); + +/** + * Multiplies two transforms. + * + * @param x First transform. + * @param y Second transform. + * @return Product of the two transforms. + */ +template +[[nodiscard]] transform mul(const transform& x, const transform& y); + +/** + * Multiplies a vector by a transform. + * + * @param t Transform. + * @param v Vector. + * @return Product of the transform and vector. + */ +template +[[nodiscard]] vector mul(const transform& t, const vector& v); + +template +transform inverse(const transform& t) +{ + transform inverse_t; + inverse_t.scale = {T(1) / t.scale.x(), T(1) / t.scale.y(), T(1) / t.scale.z()}; + inverse_t.rotation = conjugate(t.rotation); + inverse_t.translation = negate(mul(inverse_t.rotation, t.translation)); + return inverse_t; +} + +template +matrix matrix_cast(const transform& t) +{ + matrix transformation = matrix(matrix(t.rotation)); + transformation[3] = {t.translation[0], t.translation[1], t.translation[2], T(1)}; + return scale(transformation, t.scale); +} + +template +transform mul(const transform& x, const transform& y) +{ + return + { + mul(x, y.translation), + normalize(mul(x.rotation, y.rotation)), + mul(x.scale, y.scale) + }; +} + +template +vector mul(const transform& t, const vector& v) +{ + return add(t.translation, (mul(t.rotation, mul(v, t.scale)))); +} + +} // namespace math + +#endif // ANTKEEPER_MATH_TRANSFORM_FUNCTIONS_HPP + diff --git a/src/engine/math/transform-operators.hpp b/src/engine/math/transform-operators.hpp new file mode 100644 index 0000000..61c7b7d --- /dev/null +++ b/src/engine/math/transform-operators.hpp @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_MATH_TRANSFORM_OPERATORS_HPP +#define ANTKEEPER_MATH_TRANSFORM_OPERATORS_HPP + +#include +#include + +/// @copydoc math::mul(const math::transform&, const math::transform&) +template +[[nodiscard]] math::transform operator*(const math::transform& x, const math::transform& y); + +/// @copydoc math::mul(const math::transform&, const math::vector&) +template +[[nodiscard]] math::vector operator*(const math::transform& t, const math::vector& v); + +/** + * Multiplies two transforms and stores the result in the first transform. + * + * @param x First transform. + * @param y Second transform. + * @return Reference to the first transform. + */ +template +math::transform& operator*=(math::transform& x, const math::transform& y); + +template +inline math::transform operator*(const math::transform& x, const math::transform& y) +{ + return mul(x, y); +} + +template +inline math::vector operator*(const math::transform& t, const math::vector& v) +{ + return mul(t, v); +} + +template +inline math::transform& operator*=(math::transform& x, const math::vector& y) +{ + return (x = x * y); +} + +#endif // ANTKEEPER_MATH_TRANSFORM_OPERATORS_HPP + diff --git a/src/engine/math/transform-type.hpp b/src/engine/math/transform-type.hpp new file mode 100644 index 0000000..f0ef10f --- /dev/null +++ b/src/engine/math/transform-type.hpp @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_MATH_TRANSFORM_TYPE_HPP +#define ANTKEEPER_MATH_TRANSFORM_TYPE_HPP + +#include +#include + +namespace math { + +/** + * Represents 3D TRS transformation. + * + * @tparam T Scalar type. + */ +template +struct transform +{ + /// Translation vector + vector translation; + + /// Rotation quaternion + quaternion rotation; + + /// Scale vector + vector scale; + + /// Identity transform. + static const transform identity; +}; + +template +const transform transform::identity = +{ + vector::zero(), + quaternion::identity(), + vector::one() +}; + +} // namespace math + +#endif // ANTKEEPER_MATH_TRANSFORM_TYPE_HPP diff --git a/src/math/vector.hpp b/src/engine/math/vector.hpp similarity index 100% rename from src/math/vector.hpp rename to src/engine/math/vector.hpp diff --git a/src/physics/constants.hpp b/src/engine/physics/constants.hpp similarity index 100% rename from src/physics/constants.hpp rename to src/engine/physics/constants.hpp diff --git a/src/engine/physics/gas/atmosphere.hpp b/src/engine/physics/gas/atmosphere.hpp new file mode 100644 index 0000000..ed518f0 --- /dev/null +++ b/src/engine/physics/gas/atmosphere.hpp @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_PHYSICS_GAS_ATMOSPHERE_HPP +#define ANTKEEPER_PHYSICS_GAS_ATMOSPHERE_HPP + +#include +#include +#include +#include + +namespace physics { +namespace gas { + +/// Atmosphere-related functions. +namespace atmosphere { + +/** + * Calculates a particle polarizability factor. + * + * @param ior Atmospheric index of refraction. + * @param density Molecular number density, in mol/m-3. + * @return Polarizability factor. + * + * @see Elek, O., & Kmoch, P. (2010). Real-time spectral scattering in large-scale natural participating media. Proceedings of the 26th Spring Conference on Computer Graphics - SCCG ’10. doi:10.1145/1925059.1925074 + * @see Elek, Oskar. (2009). Rendering Parametrizable Planetary Atmospheres with Multiple Scattering in Real-Time. + */ +template +T polarization(T ior, T density) +{ + constexpr T k = T(2) * math::pi * math::pi; + const T ior2m1 = ior * ior - T(1); + const T num = k * ior2m1 * ior2m1; + const T den = T(3) * density * density; + return num / den; +} + +/** + * Calculates a wavelength-dependent scattering coefficient. + * + * @param density Molecular number density of the particles, in mol/m-3. + * @param polarization Particle polarizability factor. + * @param wavelength Wavelength of light, in meters. + * + * @return Scattering coefficient. + * + * @see atmosphere::polarization + * + * @see Elek, O., & Kmoch, P. (2010). Real-time spectral scattering in large-scale natural participating media. Proceedings of the 26th Spring Conference on Computer Graphics - SCCG ’10. doi:10.1145/1925059.1925074 + * @see Elek, Oskar. (2009). Rendering Parametrizable Planetary Atmospheres with Multiple Scattering in Real-Time. + */ +template +T scattering(T density, T polarization, T wavelength) +{ + const T wavelength2 = wavelength * wavelength; + return math::four_pi * (density / (wavelength2 * wavelength2)) * polarization; +} + +/** + * Calculates a wavelength-independent scattering coefficient. + * + * @param density Molecular number density of the particles, in mol/m-3. + * @param polarization Particle polarizability factor. + * + * @return Scattering coefficient. + * + * @see atmosphere::polarization + * + * @see Elek, O., & Kmoch, P. (2010). Real-time spectral scattering in large-scale natural participating media. Proceedings of the 26th Spring Conference on Computer Graphics - SCCG ’10. doi:10.1145/1925059.1925074 + * @see Elek, Oskar. (2009). Rendering Parametrizable Planetary Atmospheres with Multiple Scattering in Real-Time. + */ +template +T scattering(T density, T polarization) +{ + return math::four_pi * density * polarization; +} + +/** + * Calculates an absorption coefficient. + * + * @param scattering Scattering coefficient. + * @param albedo Single-scattering albedo. + * + * @return Absorption coefficient. + * + * @see https://en.wikipedia.org/wiki/Single-scattering_albedo + */ +template +T absorption(T scattering, T albedo) +{ + return scattering * (T(1) / albedo - T(1)); +} + +/** + * Calculates an extinction coefficient. + * + * @param scattering Scattering coefficient. + * @param albedo Single-scattering albedo. + * + * @return Extinction coefficient. + * + * @see https://en.wikipedia.org/wiki/Single-scattering_albedo + */ +template +T extinction(T scattering, T albedo) +{ + return scattering / albedo; +} + +/** + * Approximates the optical depth of exponentially-distributed atmospheric particles between two points using the trapezoidal rule. + * + * @param a Start point. + * @param b End point. + * @param r Radius of the planet. + * @param sh Scale height of the atmospheric particles. + * @param n Number of samples. + * @return Optical depth between @p a and @p b. + */ +template +T optical_depth_exp(const math::vector3& a, const math::vector3& b, T r, T sh, std::size_t n) +{ + sh = T(-1) / sh; + + const T h = math::length(b - a) / T(n); + + math::vector3 dy = (b - a) / T(n); + math::vector3 y = a + dy; + + T f_x = std::exp((math::length(a) - r) * sh); + T f_y = std::exp((math::length(y) - r) * sh); + T sum = (f_x + f_y); + + for (std::size_t i = 1; i < n; ++i) + { + f_x = f_y; + y += dy; + f_y = std::exp((math::length(y) - r) * sh); + sum += (f_x + f_y); + } + + return sum / T(2) * h; +} + +/** + * Approximates the optical depth of triangularly-distributed atmospheric particles between two points using the trapezoidal rule. + * + * @param p0 Start point. + * @param p1 End point. + * @param r Radius of the planet. + * @param a Distribution lower limit. + * @param b Distribution upper limit. + * @param c Distribution upper mode. + * @param n Number of samples. + * @return Optical depth between @p a and @p b. + */ +template +T optical_depth_tri(const math::vector3& p0, const math::vector3& p1, T r, T a, T b, T c, std::size_t n) +{ + a = T(1) / (a - c); + b = T(1) / (b - c); + + const T h = math::length(p1 - p0) / T(n); + + math::vector3 dy = (p1 - p0) / T(n); + math::vector3 y = p0 + dy; + + T z = math::length(p0) - r; + T f_x = std::max(T(0), std::max(T(0), c - z) * a - std::max(T(0), z - c) * b + T(1)); + + z = math::length(y) - r; + T f_y = std::max(T(0), std::max(T(0), c - z) * a - std::max(T(0), z - c) * b + T(1)); + T sum = (f_x + f_y); + + for (std::size_t i = 1; i < n; ++i) + { + f_x = f_y; + y += dy; + + z = math::length(y) - r; + f_y = std::max(T(0), std::max(T(0), c - z) * a - std::max(T(0), z - c) * b + T(1)); + + sum += (f_x + f_y); + } + + return sum / T(2) * h; +} + +/// Atmospheric density functions. +namespace density { + + /** + * Calculates the density of exponentially-distributed atmospheric particles at a given elevation. + * + * @param d0 Density at sea level. + * @param z Height above sea level. + * @param sh Scale height of the particle type. + * + * @return Particle density at elevation @p z. + * + * @see https://en.wikipedia.org/wiki/Barometric_formula + * @see https://en.wikipedia.org/wiki/Scale_height + */ + template + T exponential(T d0, T z, T sh) + { + return d0 * std::exp(-z / sh); + } + + /** + * Calculates the density of triangularly-distributed atmospheric particles at a given elevation. + * + * @param d0 Density at sea level. + * @param z Height above sea level. + * @param a Distribution lower limit. + * @param b Distribution upper limit. + * @param c Distribution mode. + * + * @return Particle density at elevation @p z. + * + * @see https://en.wikipedia.org/wiki/Triangular_distribution + */ + template + T triangular(T d0, T z, T a, T b, T c) + { + return d0 * max(T(0), max(T(0), c - z) / (a - c) - max(T(0), z - c) / (b - c) + T(1)); + } + +} // namespace density + +} // namespace atmosphere + +} // namespace gas +} // namespace physics + +#endif // ANTKEEPER_PHYSICS_GAS_ATMOSPHERE_HPP diff --git a/src/engine/physics/gas/gas.hpp b/src/engine/physics/gas/gas.hpp new file mode 100644 index 0000000..cf1d6ef --- /dev/null +++ b/src/engine/physics/gas/gas.hpp @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_PHYSICS_GAS_HPP +#define ANTKEEPER_PHYSICS_GAS_HPP + +namespace physics { + +/// Gas-related functions. +namespace light {} + +} // namespace physics + +#include +#include + +#endif // ANTKEEPER_PHYSICS_GAS_HPP diff --git a/src/engine/physics/gas/ozone.hpp b/src/engine/physics/gas/ozone.hpp new file mode 100644 index 0000000..98e4b3b --- /dev/null +++ b/src/engine/physics/gas/ozone.hpp @@ -0,0 +1,583 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_PHYSICS_GAS_OZONE_HPP +#define ANTKEEPER_PHYSICS_GAS_OZONE_HPP + +#include + +namespace physics { +namespace gas { + +/// Ozone-related constants and functions. +namespace ozone { + +/** + * Cross sections of ozone from wavelength 280nm to 780nm, in increments of 1nm, at a temperature of 293K, in m-2/molecule. + * + * @see https://www.iup.uni-bremen.de/gruppen/molspec/databases/referencespectra/o3spectra2011/index.html + */ +constexpr double cross_sections_280nm_780nm_293k[781 - 280] = +{ + 4.08020162329358e-22, + 3.71300005805609e-22, + 3.33329613050011e-22, + 3.08830771169272e-22, + 2.7952452827933e-22, + 2.54507006594824e-22, + 2.31059906830132e-22, + 2.06098663896454e-22, + 1.77641837487505e-22, + 1.60649162987982e-22, + 1.43573256268989e-22, + 1.28028940869356e-22, + 1.11222715354512e-22, + 1.00812840644175e-22, + 8.62757275283743e-23, + 7.74492225274189e-23, + 6.74998572150431e-23, + 5.92584261096169e-23, + 5.09573191001311e-23, + 4.58263122360992e-23, + 3.90608822487099e-23, + 3.4712884682176e-23, + 3.05004947986011e-23, + 2.61948471583207e-23, + 2.35950204633063e-23, + 2.01696279467763e-23, + 1.77716671613151e-23, + 1.57884103804348e-23, + 1.35332466460157e-23, + 1.23148992566335e-23, + 1.00674476685553e-23, + 9.14966132477946e-24, + 8.0373280016411e-24, + 6.82346538467894e-24, + 6.42747947320284e-24, + 5.12300259416845e-24, + 4.77060350699273e-24, + 4.0011431423959e-24, + 3.72184265884461e-24, + 2.71828160861414e-24, + 3.14980819763314e-24, + 2.01180905161078e-24, + 2.23741500460792e-24, + 2.04586872483057e-24, + 1.14774957440809e-24, + 1.7873766001915e-24, + 1.14326155695545e-24, + 7.61393733215954e-25, + 1.30841854222986e-24, + 6.50013011248201e-25, + 4.47253301018733e-25, + 7.22254319278391e-25, + 4.42923420258652e-25, + 3.22168537281097e-25, + 5.60700006481047e-25, + 2.51991724359347e-25, + 1.64975530971913e-25, + 2.69863618345909e-25, + 2.2962216595934e-25, + 1.2768604186372e-25, + 1.6428080690911e-25, + 1.15252741016694e-25, + 6.88123014503947e-26, + 9.01478526179051e-26, + 1.38671804168295e-25, + 7.28648586727799e-26, + 6.3468766437083e-26, + 4.1705109317344e-26, + 3.22298756116943e-26, + 2.52476541455397e-26, + 2.96595291121762e-26, + 2.10981495904366e-26, + 3.0696184107227e-26, + 2.38627682184272e-26, + 1.35645160962203e-26, + 1.14737436955784e-26, + 1.00293019429341e-26, + 1.42699666872085e-26, + 1.03892014298915e-26, + 7.46029500400895e-27, + 7.86729405487508e-27, + 6.49493671023213e-27, + 3.90586420068501e-27, + 3.3969327080211e-27, + 2.69156849765275e-27, + 4.89998022220222e-27, + 4.18363151355665e-27, + 2.41505691668684e-27, + 1.52926807176976e-27, + 1.74010836686791e-27, + 1.43997701486263e-27, + 1.61611242687813e-27, + 1.09444991940235e-27, + 8.68337506495441e-28, + 1.28660044051837e-27, + 1.07534571825705e-27, + 7.59223090396554e-28, + 6.75850905941831e-28, + 6.05594086115429e-28, + 7.40387066310015e-28, + 6.04999720618854e-28, + 5.17923583652293e-28, + 4.0858846895433e-28, + 6.47448369216067e-28, + 5.29992493931534e-28, + 5.63128808710364e-28, + 4.17695119955099e-28, + 5.15330384762416e-28, + 5.25185850011986e-28, + 6.17618171380047e-28, + 6.37119303086457e-28, + 6.8496876006444e-28, + 5.9229625229341e-28, + 5.99998795876176e-28, + 7.06136396740756e-28, + 9.93040926727483e-28, + 1.03668944015898e-27, + 9.60158757124091e-28, + 9.00247643555494e-28, + 1.01801330597124e-27, + 1.0893103182854e-27, + 1.30096252080506e-27, + 1.42018105948242e-27, + 1.53477820553937e-27, + 1.60452920542724e-27, + 1.45613806382105e-27, + 1.63671034304492e-27, + 1.88680147053984e-27, + 2.26659932677371e-27, + 2.57113808884621e-27, + 2.73584069155576e-27, + 2.84524835939992e-27, + 2.65294565386989e-27, + 2.52007318042733e-27, + 2.56347774832346e-27, + 2.73272493525925e-27, + 3.21972108535573e-27, + 3.64879308272645e-27, + 3.86875166703077e-27, + 3.77657059174972e-27, + 3.67917967079418e-27, + 3.84603321414093e-27, + 4.47110813921024e-27, + 5.19879478264214e-27, + 6.13707395634462e-27, + 6.88229484256763e-27, + 7.11935416561536e-27, + 7.15812015493665e-27, + 6.67142397990209e-27, + 6.35218112458219e-27, + 6.34510826220203e-27, + 6.90321809802859e-27, + 7.82871587803871e-27, + 8.52938477234511e-27, + 8.74335964163491e-27, + 8.6718822639457e-27, + 8.59495406258221e-27, + 8.90719500501358e-27, + 9.90176156668504e-27, + 1.14857395824068e-26, + 1.36017282525383e-26, + 1.53232356175871e-26, + 1.75024000506424e-26, + 1.8179765858316e-26, + 1.80631911188605e-26, + 1.70102948254892e-26, + 1.56536231218255e-26, + 1.51141962474665e-26, + 1.57847025841346e-26, + 1.72161452840856e-26, + 1.90909798194127e-26, + 1.96774621921165e-26, + 1.99812678813396e-26, + 1.96900102296014e-26, + 1.95126617395258e-26, + 2.06408915381352e-26, + 2.28866836725858e-26, + 2.57662977048169e-26, + 2.96637551360212e-26, + 3.34197426701549e-26, + 3.73735792971477e-26, + 4.03453827718193e-26, + 4.1291323324037e-26, + 4.07643587970195e-26, + 3.84067732691303e-26, + 3.6271000065179e-26, + 3.50502931147362e-26, + 3.48851626868318e-26, + 3.73778737646723e-26, + 3.97091132121154e-26, + 4.21773978585713e-26, + 4.21620738550362e-26, + 4.22087900183437e-26, + 4.27973060694892e-26, + 4.36010682588909e-26, + 4.60584575680881e-26, + 4.91428506793045e-26, + 5.4918846417406e-26, + 6.10573296817762e-26, + 6.83025566941932e-26, + 7.51469261186479e-26, + 8.08197962688924e-26, + 8.44082645474868e-26, + 8.4843465766735e-26, + 8.4126775729461e-26, + 8.14411830190209e-26, + 7.83636723569755e-26, + 7.60974711104334e-26, + 7.57877917471603e-26, + 7.77887132347866e-26, + 8.07522339055262e-26, + 8.32310316258138e-26, + 8.59015818152486e-26, + 8.67834106505007e-26, + 8.72244226406716e-26, + 8.84734167611993e-26, + 9.0711580597939e-26, + 9.51778147590566e-26, + 1.01490385328969e-25, + 1.09448341447976e-25, + 1.18257044868557e-25, + 1.28105938778111e-25, + 1.37732704252934e-25, + 1.47491161151436e-25, + 1.55090701035304e-25, + 1.60575752538508e-25, + 1.62886093543744e-25, + 1.62802718439262e-25, + 1.60288510229631e-25, + 1.57216917046401e-25, + 1.54475957021763e-25, + 1.534341089264e-25, + 1.5409967492982e-25, + 1.56495784865383e-25, + 1.60012763627009e-25, + 1.63952588489707e-25, + 1.67232066912218e-25, + 1.70167894480834e-25, + 1.7335194060265e-25, + 1.7602731663686e-25, + 1.79953556347172e-25, + 1.84084607422109e-25, + 1.89999243847493e-25, + 1.97490644310087e-25, + 2.05279021301286e-25, + 2.14427839223598e-25, + 2.24098369182092e-25, + 2.34362490982003e-25, + 2.44138366650567e-25, + 2.53826212075759e-25, + 2.62577562731292e-25, + 2.70621837640467e-25, + 2.76622780914432e-25, + 2.80661519633223e-25, + 2.82737499370866e-25, + 2.82968166962191e-25, + 2.82659484597549e-25, + 2.81717325255129e-25, + 2.82197485088881e-25, + 2.84600833360439e-25, + 2.88048754046166e-25, + 2.92686210579676e-25, + 2.98551267943544e-25, + 3.04464034622896e-25, + 3.09724266051229e-25, + 3.14551726028915e-25, + 3.18670379817661e-25, + 3.22330249380314e-25, + 3.25463784914917e-25, + 3.2854797958699e-25, + 3.31405330400124e-25, + 3.34398013095565e-25, + 3.38005272562664e-25, + 3.41218557614901e-25, + 3.45555599852459e-25, + 3.5043277615423e-25, + 3.55984911371566e-25, + 3.6264721972979e-25, + 3.70647322984569e-25, + 3.79188179306458e-25, + 3.88981479760571e-25, + 3.98973596432023e-25, + 4.08527421216539e-25, + 4.17464990288797e-25, + 4.25462181228461e-25, + 4.32317712812093e-25, + 4.38401384845366e-25, + 4.44216978945654e-25, + 4.49508440820886e-25, + 4.5516564927479e-25, + 4.60931329475278e-25, + 4.66173637960526e-25, + 4.70665064920011e-25, + 4.74051362440113e-25, + 4.75812011867871e-25, + 4.7570031038151e-25, + 4.73747927545327e-25, + 4.71027119364443e-25, + 4.66860282624977e-25, + 4.6288533265784e-25, + 4.57997871538082e-25, + 4.53548773884213e-25, + 4.49607272653461e-25, + 4.4568818824566e-25, + 4.42881930827398e-25, + 4.40157474149992e-25, + 4.38677564524713e-25, + 4.37182142194489e-25, + 4.37104565551326e-25, + 4.37919711899623e-25, + 4.39683352146027e-25, + 4.42484514000691e-25, + 4.47212673326828e-25, + 4.53157968174371e-25, + 4.60815452736499e-25, + 4.69376508835705e-25, + 4.78054316070552e-25, + 4.87030272266768e-25, + 4.95220907227656e-25, + 5.02898332230558e-25, + 5.09027352924287e-25, + 5.13210890748271e-25, + 5.15374454317407e-25, + 5.15075653533686e-25, + 5.13171378996911e-25, + 5.09303097809138e-25, + 5.03697164998998e-25, + 4.97218676726656e-25, + 4.89461758141549e-25, + 4.80937594526597e-25, + 4.72371968798565e-25, + 4.63859449875443e-25, + 4.55132403268817e-25, + 4.46787830232533e-25, + 4.38816245012175e-25, + 4.31406743009557e-25, + 4.24483372714822e-25, + 4.17474755876221e-25, + 4.11305082072427e-25, + 4.05554427636835e-25, + 4.0030863998631e-25, + 3.95393149188544e-25, + 3.90576318741963e-25, + 3.86085282288514e-25, + 3.81425489414328e-25, + 3.76584427585746e-25, + 3.71837025816073e-25, + 3.66911165810168e-25, + 3.61941817240908e-25, + 3.56533744970388e-25, + 3.51159798289664e-25, + 3.46140984744989e-25, + 3.41173597486151e-25, + 3.36073006491665e-25, + 3.30753716054224e-25, + 3.25252799457143e-25, + 3.19530424634748e-25, + 3.13651908668849e-25, + 3.07841534489121e-25, + 3.01880275032991e-25, + 2.95965464815758e-25, + 2.90194743931008e-25, + 2.84696394478385e-25, + 2.79488978476361e-25, + 2.74800171431914e-25, + 2.70053589638645e-25, + 2.65588371839819e-25, + 2.6091795190684e-25, + 2.56708069403319e-25, + 2.52375403058723e-25, + 2.48382556862202e-25, + 2.44458617524673e-25, + 2.40594587498642e-25, + 2.36627105488787e-25, + 2.32528309566254e-25, + 2.28436716651676e-25, + 2.24424313328781e-25, + 2.20515093141858e-25, + 2.16647251017334e-25, + 2.12914125517962e-25, + 2.09073684368918e-25, + 2.05335637747553e-25, + 2.01912040845123e-25, + 1.98301051757051e-25, + 1.94465669006103e-25, + 1.9057000954812e-25, + 1.86388414183128e-25, + 1.82241187234978e-25, + 1.78160123951627e-25, + 1.74179167768809e-25, + 1.70080577609513e-25, + 1.6615348247565e-25, + 1.62305192083274e-25, + 1.58738563014106e-25, + 1.55171430112041e-25, + 1.51949383874537e-25, + 1.48607225456117e-25, + 1.45360366466218e-25, + 1.42252183610792e-25, + 1.39462753606039e-25, + 1.36820899679147e-25, + 1.34377836247288e-25, + 1.3233906891166e-25, + 1.30492894377563e-25, + 1.28681831161393e-25, + 1.2663174075999e-25, + 1.2420229295933e-25, + 1.21412305909061e-25, + 1.18502869999655e-25, + 1.15382448376731e-25, + 1.12540171803974e-25, + 1.09558453899584e-25, + 1.06672010609186e-25, + 1.03896476876362e-25, + 1.01172047316729e-25, + 9.84602582159865e-26, + 9.58059205575365e-26, + 9.33099533469407e-26, + 9.08936155224937e-26, + 8.8491955813636e-26, + 8.64349280941688e-26, + 8.44550335501804e-26, + 8.25653238461191e-26, + 8.08238949115334e-26, + 7.94009406468206e-26, + 7.80797137891831e-26, + 7.67364989571709e-26, + 7.54965718671858e-26, + 7.43779079827743e-26, + 7.31964666857758e-26, + 7.2232944312979e-26, + 7.12771524608971e-26, + 7.0510155861502e-26, + 6.99911412369698e-26, + 6.96383131319201e-26, + 6.92914966812787e-26, + 6.85928437919081e-26, + 6.74428511228458e-26, + 6.59224480420957e-26, + 6.38840433633278e-26, + 6.15855599572905e-26, + 5.95772659493798e-26, + 5.76494205198861e-26, + 5.60397496087846e-26, + 5.46017309852595e-26, + 5.32285644696103e-26, + 5.22365816180628e-26, + 5.07899578121548e-26, + 4.93592723237266e-26, + 4.79667132541204e-26, + 4.68132550170927e-26, + 4.56404612656666e-26, + 4.48276475068268e-26, + 4.40173864033052e-26, + 4.35417448301629e-26, + 4.30681941574933e-26, + 4.28871372503407e-26, + 4.24230311436515e-26, + 4.22301315090103e-26, + 4.19685390394596e-26, + 4.18917236558952e-26, + 4.17687017488352e-26, + 4.22019512128238e-26, + 4.2557462015274e-26, + 4.31172890769302e-26, + 4.36741466155527e-26, + 4.41740590419353e-26, + 4.44874945269751e-26, + 4.42029497925774e-26, + 4.34317624813875e-26, + 4.21853858585038e-26, + 4.01907542789597e-26, + 3.80281792816081e-26, + 3.60902775684479e-26, + 3.41921953398109e-26, + 3.25291539681547e-26, + 3.10743399295997e-26, + 2.99157340432027e-26, + 2.89660087626517e-26, + 2.82185391364051e-26, + 2.76872520775773e-26, + 2.72983807771732e-26, + 2.65963957090044e-26, + 2.62737083974039e-26, + 2.56625477748644e-26, + 2.53980768456023e-26, + 2.49759827680602e-26, + 2.47017705195044e-26, + 2.47697548770545e-26, + 2.46973660414457e-26, + 2.47714280793403e-26, + 2.50190654239581e-26, + 2.56196471101148e-26, + 2.64525680175745e-26, + 2.72275203692747e-26, + 2.826728922873e-26, + 2.94507453632992e-26, + 3.05247618840877e-26, + 3.1005889405393e-26, + 3.1508637563266e-26, + 3.13148927506362e-26 +}; + +/** + * Returns the cross section of ozone at temperature 293k and a given wavelength in the visible spectrum. + * + * @param wavelength Wavelength, in nanometers, on `[280, 780)`. + * @return Ozone cross section at temperature 293k and the given wavelength, in m-2/molecule. + */ +template +constexpr T cross_section_293k(T wavelength) +{ + int i = static_cast(wavelength); + int j = static_cast(wavelength + T(1)); + + if (i < 280 || j > 780) + return T(0); + + const T x = static_cast(cross_sections_280nm_780nm_293k[i - 280]); + const T y = static_cast(cross_sections_280nm_780nm_293k[j - 280]); + + return math::lerp(x, y, wavelength - static_cast(i)); +} + +/** + * Calculates an ozone absorption coefficient (wavelength-dependent). + * + * @param cross_section Ozone cross section of a wavelength, in m-2/molecule. + * @param density Molecular number density of ozone, in mol/m-3. + * + * @return Ozone absorption coefficient. + * + * @see https://skyrenderer.blogspot.com/2012/10/ozone-absorption.html + */ +template +T absorption(T cross_section, T density) +{ + return cross_section * density; +} + +} // namespace ozone + +} // namespace gas +} // namespace physics + +#endif // ANTKEEPER_PHYSICS_GAS_OZONE_HPP diff --git a/src/engine/physics/light/blackbody.hpp b/src/engine/physics/light/blackbody.hpp new file mode 100644 index 0000000..cdf7596 --- /dev/null +++ b/src/engine/physics/light/blackbody.hpp @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_PHYSICS_LIGHT_BLACKBODY_HPP +#define ANTKEEPER_PHYSICS_LIGHT_BLACKBODY_HPP + +#include +#include + +namespace physics { +namespace light { + +/** + * Blackbody radiation functions. + * + * @see https://en.wikipedia.org/wiki/Stefan%E2%80%93Boltzmann_law + */ +namespace blackbody { + +/** + * Calculates the radiant exitance of a blackbody. + * + * @param t Temperature of the blackbody, in kelvin. + * @return Radiant exitance of the blackbody, in watt per square meter. + */ +template +T radiant_exitance(T t); + +/** + * Calculates the radiant flux of a blackbody. + * + * @param t Temperature of the blackbody, in kelvin. + * @param a Surface area of the blackbody, in square meters. + * @return Radiant flux of the blackbody, in watt. + */ +template +T radiant_flux(T t, T a); + +/** + * Calculates the radiant intensity of a blackbody. + * + * @param t Temperature of the blackbody, in kelvin. + * @param a Surface area of the blackbody, in square meters. + * @param omega Solid angle, in steradians. + * @return Radiant intensity of the blackbody, in watt per steradian. + */ +template +T radiant_intensity(T t, T a, T omega); + +/** + * Calculates the spectral exitance of a blackbody for the given wavelength. + * + * @param t Temperature of the blackbody, in kelvin. + * @param lambda Wavelength of light, in meters. + * @param c Speed of light in medium. + * @return Spectral exitance, in watt per square meter, per meter. + */ +template +T spectral_exitance(T t, T lambda, T c = constants::speed_of_light); + +/** + * Calculates the spectral flux of a blackbody for the given wavelength. + * + * @param t Temperature of the blackbody, in kelvin. + * @param a Surface area of the blackbody, in square meters. + * @param lambda Wavelength of light, in meters. + * @param c Speed of light in medium. + * @return Spectral flux of the blackbody, in watt per meter. + */ +template +T spectral_flux(T t, T a, T lambda, T c = constants::speed_of_light); + +/** + * Calculates the spectral intensity of a blackbody for the given wavelength. + * + * @param t Temperature of the blackbody, in kelvin. + * @param a Surface area of the blackbody, in square meters. + * @param lambda Wavelength of light, in meters. + * @param omega Solid angle, in steradians. + * @param c Speed of light in medium. + * @return Spectral intensity of the blackbody for the given wavelength, in watt per steradian per meter. + */ +template +T spectral_intensity(T t, T a, T lambda, T omega, T c = constants::speed_of_light); + +/** + * Calculates the spectral radiance of a blackbody for the given wavelength. + * + * @param t Temperature of the blackbody, in kelvin. + * @param lambda Wavelength of light, in meters. + * @param c Speed of light in medium. + * @return Spectral radiance, in watt per steradian per square meter per meter. + */ +template +T spectral_radiance(T t, T lambda, T c = constants::speed_of_light); + +template +T radiant_exitance(T t) +{ + const T tt = t * t; + return constants::stefan_boltzmann * (tt * tt); +} + +template +T radiant_flux(T t, T a) +{ + return a * radiant_exitance(t); +} + +template +T radiant_intensity(T t, T a, T omega) +{ + return radiant_flux(t, a) / omega; +} + +template +T spectral_exitance(T t, T lambda, T c) +{ + const T hc = constants::planck * c; + const T lambda2 = lambda * lambda; + + // First radiation constant (c1) + const T c1 = T(2) * math::pi * hc * c; + + // Second radiation constant (c2) + const T c2 = hc / constants::boltzmann; + + return (c1 / (lambda2 * lambda2 * lambda)) / std::expm1(c2 / (lambda * t)); +} + +template +T spectral_flux(T t, T a, T lambda, T c) +{ + return a * spectral_exitance(t, lambda, c); +} + +template +T spectral_intensity(T t, T a, T lambda, T omega, T c) +{ + return spectral_flux(t, a, lambda, c) / omega; +} + +template +T spectral_radiance(T t, T lambda, T c) +{ + const T hc = constants::planck * c; + const T lambda2 = lambda * lambda; + + // First radiation constant (c1L) + const T c1l = T(2) * hc * c; + + // Second radiation constant (c2) + const T c2 = hc / constants::boltzmann; + + return (c1l / (lambda2 * lambda2 * lambda)) / std::expm1(c2 / (lambda * t)); +} + +} // namespace blackbody + +} // namespace light +} // namespace physics + +#endif // ANTKEEPER_PHYSICS_LIGHT_BLACKBODY_HPP diff --git a/src/physics/light/exposure.hpp b/src/engine/physics/light/exposure.hpp similarity index 100% rename from src/physics/light/exposure.hpp rename to src/engine/physics/light/exposure.hpp diff --git a/src/engine/physics/light/light.hpp b/src/engine/physics/light/light.hpp new file mode 100644 index 0000000..f4e7cd4 --- /dev/null +++ b/src/engine/physics/light/light.hpp @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_PHYSICS_LIGHT_HPP +#define ANTKEEPER_PHYSICS_LIGHT_HPP + +namespace physics { + +/// Light-related functions. +namespace light {} + +} // namespace physics + +#include +#include +#include +#include +#include +#include + +#endif // ANTKEEPER_PHYSICS_LIGHT_HPP diff --git a/src/physics/light/luminosity.hpp b/src/engine/physics/light/luminosity.hpp similarity index 100% rename from src/physics/light/luminosity.hpp rename to src/engine/physics/light/luminosity.hpp diff --git a/src/engine/physics/light/phase.hpp b/src/engine/physics/light/phase.hpp new file mode 100644 index 0000000..1ef3781 --- /dev/null +++ b/src/engine/physics/light/phase.hpp @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_PHYSICS_LIGHT_PHASE_HPP +#define ANTKEEPER_PHYSICS_LIGHT_PHASE_HPP + +#include +#include + +namespace physics { +namespace light { + +/// Light-scattering phase functions. +namespace phase { + +/** + * Cornette-Shanks phase function. + * + * @param mu Cosine of the angle between the light and view directions. + * @param g Asymmetry factor, on [-1, 1]. Positive values cause forward scattering, negative values cause back scattering. + */ +template +T cornette_shanks(T mu, T g); + +/** + * Henyey–Greenstein phase function. + * + * @param mu Cosine of the angle between the light and view directions. + * @param g Asymmetry factor, on `[-1, 1]`. Positive values cause forward scattering, negative values cause back scattering. + * + * @see http://www.pbr-book.org/3ed-2018/Volume_Scattering/Phase_Functions.html + */ +template +T henyey_greenstein(T mu, T g); + +/** + * Isotropic phase function. + */ +template +constexpr T isotropic() +{ + return T(1) / (T(4) * math::pi); +} + +/** + * Rayleigh phase function. + * + * @param mu Cosine of the angle between the light and view directions. + */ +template +T rayleigh(T mu); + +template +T cornette_shanks(T mu, T g) +{ + static const T k = T(3) / (T(8) * math::pi); + const T gg = g * g; + const T num = (T(1) - gg) * (T(1) + mu * mu); + const T den = (T(2) + gg) * std::pow(T(1) + gg - T(2) * g * mu, T(1.5)); + return k * num / den; +} + +template +T henyey_greenstein(T mu, T g) +{ + const T gg = g * g; + return (T(1) - gg) / (T(4) * math::pi * std::pow(T(1) + gg - T(2) * g * mu, T(1.5))); +} + +template +T rayleigh(T mu) +{ + static const T k = T(3) / (T(16) * math::pi); + return k * (1.0 + mu * mu); +} + +} // namespace phase + +} // namespace light +} // namespace physics + +#endif // ANTKEEPER_PHYSICS_LIGHT_PHASE_HPP diff --git a/src/engine/physics/light/photometry.hpp b/src/engine/physics/light/photometry.hpp new file mode 100644 index 0000000..d243010 --- /dev/null +++ b/src/engine/physics/light/photometry.hpp @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_PHYSICS_LIGHT_PHOTOMETRY_HPP +#define ANTKEEPER_PHYSICS_LIGHT_PHOTOMETRY_HPP + +#include +#include + +namespace physics { +namespace light { + +/// Maximum luminous efficacy of an ideal monochromatic source, in lumen per watt. +template +constexpr T max_luminous_efficacy = T(683.002); + +/** + * Calculates the luminous efficiency of a light source. + * + * @param spd Unary function object that returns spectral radiance given a wavelength. + * @param lef Unary function object that returns luminous efficiency given a wavelength. + * @param first,last Range of sample wavelengths. + * @return Luminous efficiency, on `[0, 1]`. + * + * @see physics::light::blackbody::spectral_radiance + * @see physics::light::luminosity::photopic + */ +template +T luminous_efficiency(UnaryOp1 spd, UnaryOp2 lef, InputIt first, InputIt last) +{ + auto spd_lef = [spd, lef](T x) -> T + { + return spd(x) * lef(x); + }; + + const T num = math::quadrature::simpson(spd_lef, first, last); + const T den = math::quadrature::simpson(spd, first, last); + + return num / den; +} + +/** + * Calculates luminous efficacy given luminous efficiency. + * + * @param efficiency Luminous efficiency, on `[0, 1]`. + * @return Luminous flux, in lumens. + */ +template +T luminous_efficacy(T efficiency) +{ + return max_luminous_efficacy * efficiency; +} + +/** + * Converts watts (radiant flux) to lumens (luminous flux). + * + * @param radiant_flux Radiant flux, in watts. + * @param efficiency Luminous efficiency, on `[0, 1]`. + * @return Luminous flux, in lumens. + */ +template +T watts_to_lumens(T radiant_flux, T efficiency) +{ + return radiant_flux * luminous_efficacy(efficiency); +} + +} // namespace light +} // namespace physics + +#endif // ANTKEEPER_PHYSICS_LIGHT_PHOTOMETRY_HPP diff --git a/src/physics/light/refraction.hpp b/src/engine/physics/light/refraction.hpp similarity index 100% rename from src/physics/light/refraction.hpp rename to src/engine/physics/light/refraction.hpp diff --git a/src/physics/light/vmag.hpp b/src/engine/physics/light/vmag.hpp similarity index 100% rename from src/physics/light/vmag.hpp rename to src/engine/physics/light/vmag.hpp diff --git a/src/engine/physics/number-density.hpp b/src/engine/physics/number-density.hpp new file mode 100644 index 0000000..48fbfad --- /dev/null +++ b/src/engine/physics/number-density.hpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_PHYSICS_NUMBER_DENSITY_HPP +#define ANTKEEPER_PHYSICS_NUMBER_DENSITY_HPP + +#include + +namespace physics { + +/** + * Calculates the number density of a substance. + * + * @param c Molar concentration, in mol/m-3. + * @return Number density, in m-3. + * + * @see https://en.wikipedia.org/wiki/Number_density + */ +template +T number_density(T c) +{ + return physics::constants::avogadro * c; +} + +} // namespace physics + +#endif // ANTKEEPER_PHYSICS_NUMBER_DENSITY_HPP diff --git a/src/engine/physics/orbit/anomaly.hpp b/src/engine/physics/orbit/anomaly.hpp new file mode 100644 index 0000000..a92e0a9 --- /dev/null +++ b/src/engine/physics/orbit/anomaly.hpp @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_PHYSICS_ORBIT_ANOMALY_HPP +#define ANTKEEPER_PHYSICS_ORBIT_ANOMALY_HPP + +#include +#include + +namespace physics { +namespace orbit { + +/** + * Orbital anomaly functions. + */ +namespace anomaly { + +/** + * Derives the eccentric anomaly given eccentricity and true anomaly. + * + * @param ec Eccentricity (e). + * @param ta True anomaly (nu). + * @return Eccentric anomaly (E). + */ +template +T true_to_eccentric(T ec, T ta); + +/** + * Derives the mean anomaly given eccentricity and true anomaly. + * + * @param ec Eccentricity (e). + * @param ta True anomaly (nu). + * @return Mean anomaly (M). + */ +template +T true_to_mean(T ec, T ta); + +/** + * Derives the true anomaly given eccentricity and eccentric anomaly. + * + * @param ec Eccentricity (e). + * @param ea Eccentric anomaly (E). + * @return True anomaly (nu). + */ +template +T eccentric_to_true(T ec, T ea); + +/** + * Derives the mean anomaly given eccentricity and eccentric anomaly. + * + * @param ec Eccentricity (e). + * @param ea Eccentric anomaly (E). + * @return Mean anomaly (M). + */ +template +T eccentric_to_mean(T ec, T ea); + +/** + * Iteratively derives the eccentric anomaly given eccentricity and mean anomaly. + * + * @param ec Eccentricity (e). + * @param ma Mean anomaly (M). + * @param iterations Maximum number of iterations. + * @param tolerance Solution error tolerance. + * @return Eccentric anomaly (E). + * + * @see Murison, Marc. (2006). A Practical Method for Solving the Kepler Equation. 10.13140/2.1.5019.6808. + */ +template +T mean_to_eccentric(T ec, T ma, std::size_t iterations, T tolerance); + +/** + * Iteratively derives the true anomaly given eccentricity and mean anomaly. + * + * @param ec Eccentricity (e). + * @param ma Mean anomaly (M). + * @param iterations Maximum number of iterations. + * @param tolerance Solution error tolerance. + * @return True anomaly (nu). + */ +template +T mean_to_true(T ec, T ma, std::size_t iterations, T tolerance); + +template +T true_to_eccentric(T ec, T ta) +{ + // Parabolic orbit + if (ec == T(1)) + return std::tan(ta * T(0.5)); + + // Hyperbolic orbit + if (ec > T(1)) + return std::acosh((ec + std::cos(ta)) / (T(1) + ec * std::cos(ta))) * ((ta < T(0)) ? T(-1) : T(1)); + + // Elliptic orbit + return std::atan2(std::sqrt(T(1) - ec * ec) * std::sin(ta), std::cos(ta) + ec); +} + +template +T true_to_mean(T ec, T ta) +{ + return eccentric_to_mean(ec, true_to_eccentric(ec, ta)); +} + +template +T eccentric_to_true(T ec, T ea) +{ + // Parabolic orbit + if (ec == T(1)) + return std::atan(ea) * T(2); + + // Hyperbolic orbit + if (ec > T(1)) + return std::atan(std::sqrt((ec + T(1)) / (ec - T(1))) * std::tanh(ea * T(0.5))) * T(2); + + // Elliptic orbit + return std::atan2(sqrt(T(1) - ec * ec) * std::sin(ea), std::cos(ea) - ec); +} + +template +T eccentric_to_mean(T ec, T ea) +{ + // Parabolic orbit + if (ec == T(1)) + return (ea * ea * ea) / T(6) + ea * T(0.5); + + // Hyperbolic orbit + if (ec > T(1)) + return ec * std::sinh(ea) - ea; + + // Elliptic orbit + return ea - ec * std::sin(ea); +} + +template +T mean_to_eccentric(T ec, T ma, std::size_t iterations, T tolerance) +{ + // Wrap mean anomaly to `[-pi, pi]` + ma = std::remainder(ma, math::two_pi); + + // Third-order approximation of eccentric anomaly starting value, E0 + const T t33 = std::cos(ma); + const T t34 = ec * ec; + const T t35 = t34 * ec; + T ea0 = ma + (T(-0.5) * t35 + ec + (t34 + T(1.5) * t33 * t35) * t33) * std::sin(ma); + + // Iteratively converge E0 and E1 + for (std::size_t i = 0; i < iterations; ++i) + { + // Third-order approximation of eccentric anomaly, E1 + const T t1 = std::cos(ea0); + const T t2 = T(-1) + ec * t1; + const T t3 = std::sin(ea0); + const T t4 = ec * t3; + const T t5 = -ea0 + t4 + ma; + const T t6 = t5 / (T(0.5) * t5 * t4 / t2 + t2); + const T ea1 = ea0 - (t5 / ((T(0.5) * t3 - (T(1) / T(6)) * t1 * t6) * ec * t6 + t2)); + + // Determine solution error + const T error = std::abs(ea1 - ea0); + + // Set E0 to E1 + ea0 = ea1; + + // Break if solution is within error tolerance + if (error < tolerance) + break; + } + + return ea0; +} + +template +T mean_to_true(T ec, T ma, std::size_t iterations, T tolerance) +{ + eccentric_to_true(ec, mean_to_eccentric(ec, ma, iterations, tolerance)); +} + +} // namespace anomaly +} // namespace orbit +} // namespace physics + +#endif // ANTKEEPER_PHYSICS_ORBIT_ANOMALY_HPP diff --git a/src/engine/physics/orbit/elements.hpp b/src/engine/physics/orbit/elements.hpp new file mode 100644 index 0000000..8ba0ef6 --- /dev/null +++ b/src/engine/physics/orbit/elements.hpp @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_PHYSICS_ORBIT_ELEMENTS_HPP +#define ANTKEEPER_PHYSICS_ORBIT_ELEMENTS_HPP + +#include +#include +#include + +namespace physics { +namespace orbit { + +/** + * Set of six Keplerian elements required to uniquely identify an orbit. + * + * @tparam T Scalar type. + */ +template +struct elements +{ + /// Scalar type. + typedef T scalar_type; + + /// Eccentricity (e). + scalar_type ec; + + /// Semimajor axis (a). + scalar_type a; + + /// Inclination (i), in radians. + scalar_type in; + + /// Right ascension of the ascending node (OMEGA), in radians. + scalar_type om; + + /// Argument of periapsis (omega), in radians. + scalar_type w; + + /// Mean anomaly (M) at epoch, in radians. + scalar_type ma; +}; + +/** + * Calculates the period of an elliptical orbit according to Kepler's third law. + * + * @param a Semimajor axis (a). + * @param gm Standard gravitational parameter (GM). + * @return Orbital period (T). + */ +template +T period(T a, T gm); + +/** + * Calculates the mean motion (n) of an orbit. + * + * @param a Semimajor axis (a). + * @param gm Standard gravitational parameter (GM). + * @return Mean motion (n), in radians per unit time. + */ +template +T mean_motion(T a, T gm); + +/** + * Calculates the mean motion (n) of an orbit. + * + * @param t Orbital period (T). + * @return Mean motion (n), in radians per unit time. + */ +template +T mean_motion(T t); + +/** + * Derives the argument of the periapsis (omega) of an orbit, given the longitude of periapsis (pomega) and longitude of the ascending node (OMEGA). + * + * @param lp Longitude of the periapsis (pomega), in radians. + * @param om Right ascension of the ascending node (OMEGA), in radians. + * @return Argument of the periapsis (omega), in radians. + */ +template +T argument_periapsis(T om, T lp); + +/** + * Derives the longitude of the periapsis (pomega) of an orbit, given the argument of periapsis (omega) and longitude of the ascending node (OMEGA). + * + * @param w Argument of the periapsis (omega), in radians. + * @param om Right ascension of the ascending node (OMEGA), in radians. + * @return Longitude of the periapsis (pomega), in radians. + */ +template +T longitude_periapsis(T om, T w); + +/** + * Derives the semiminor axis (b) of an orbit, given the semimajor axis (a) and eccentricity (e). + * + * @param a Semimajor axis (a). + * @param ec Eccentricity (e). + * @return Semiminor axis (b). + */ +template +T semiminor_axis(T a, T ec); + +/** + * Derives the semi-latus rectum (l) of an orbit, given the semimajor axis (a) and eccentricity (e). + * + * @param a Semimajor axis (a). + * @param ec Eccentricity (e). + * @return Semi-latus rectum (l). + */ +template +T semilatus_rectum(T a, T ec); + +template +T period(T a, T gm) +{ + return math::two_pi * std::sqrt((a * a * a) / gm); +} + +template +T mean_motion(T a, T gm) +{ + return std::sqrt((a * a * a) / gm); +} + +template +T mean_motion(T t) +{ + return math::two_pi / t; +} + +template +T argument_periapsis(T om, T lp) +{ + return lp - om; +} + +template +T longitude_periapsis(T om, T w) +{ + return w + om; +} + +template +T semiminor_axis(T a, T ec) +{ + return a * std::sqrt(T(1) - ec * ec); +} + +template +T semilatus_rectum(T a, T ec) +{ + return a * (T(1) - ec * ec); +} + +} // namespace orbit +} // namespace physics + +#endif // ANTKEEPER_PHYSICS_ORBIT_ELEMENTS_HPP diff --git a/src/engine/physics/orbit/ephemeris.hpp b/src/engine/physics/orbit/ephemeris.hpp new file mode 100644 index 0000000..fac57f2 --- /dev/null +++ b/src/engine/physics/orbit/ephemeris.hpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_PHYSICS_ORBIT_EPHEMERIS_HPP +#define ANTKEEPER_PHYSICS_ORBIT_EPHEMERIS_HPP + +#include + +namespace physics { +namespace orbit { + +/** + * Table of orbital trajectories. + * + * @tparam t Real type. + */ +template +using ephemeris = std::vector>; + +} // namespace orbit +} // namespace physics + +#endif // ANTKEEPER_PHYSICS_ORBIT_EPHEMERIS_HPP diff --git a/src/engine/physics/orbit/frame.hpp b/src/engine/physics/orbit/frame.hpp new file mode 100644 index 0000000..cadf5b0 --- /dev/null +++ b/src/engine/physics/orbit/frame.hpp @@ -0,0 +1,406 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_PHYSICS_ORBIT_FRAME_HPP +#define ANTKEEPER_PHYSICS_ORBIT_FRAME_HPP + +#include +#include + +namespace physics { +namespace orbit { + +/// Orbital reference frames. +namespace frame { + +/// Perifocal (PQW) frame. +namespace pqw { + + /** + * Converts PQW coordinates from Cartesian to spherical. + * + * @param v PQW Cartesian coordinates. + * @return PQW spherical coordinates, in the ISO order of radial distance, inclination (radians), and true anomaly (radians). + */ + template + math::vector3 spherical(const math::vector3& v) + { + const T xx_yy = v.x() * v.x() + v.y() * v.y(); + + return math::vector3 + { + std::sqrt(xx_yy + v.z() * v.z()), + std::atan2(v.z(), std::sqrt(xx_yy)), + std::atan2(v.y(), v.x()) + }; + } + + /** + * Constructs spherical PQW coordinates from Keplerian orbital elements. + * + * @param ec Eccentricity (e). + * @param a Semimajor axis (a). + * @param ea Eccentric anomaly (E), in radians. + * @param b Semiminor axis (b). + * @return PQW spherical coordinates, in the ISO order of radial distance, inclination (radians), and true anomaly (radians). + */ + template + math::vector3 spherical(T ec, T a, T ea, T b) + { + const T x = a * (std::cos(ea) - ec); + const T y = b * std::sin(ea); + const T d = std::sqrt(x * x + y * y); + const T ta = std::atan2(y, x); + + return math::vector3{d, T(0), ta}; + } + + /** + * Constructs spherical PQW coordinates from Keplerian orbital elements. + * + * @param ec Eccentricity (e). + * @param a Semimajor axis (a). + * @param ea Eccentric anomaly (E), in radians. + * @return PQW spherical coordinates, in the ISO order of radial distance, inclination (radians), and true anomaly (radians). + */ + template + math::vector3 spherical(T ec, T a, T ea) + { + const T b = a * std::sqrt(T(1) - ec * ec); + return spherical(ec, a, ea, b); + } + + /** + * Converts PQW coordinates from spherical to Cartesian. + * + * @param PQW spherical coordinates, in the ISO order of radial distance, inclination (radians), and true anomaly (radians). + * @return PQW Cartesian coordinates. + */ + template + math::vector3 cartesian(const math::vector3& v) + { + const T x = v[0] * std::cos(v[1]); + + return math::vector3 + { + x * std::cos(v[2]), + x * std::sin(v[2]), + v[0] * std::sin(v[1]), + }; + } + + /** + * Constructs Cartesian PQW coordinates from Keplerian orbital elements. + * + * @param ec Eccentricity (e). + * @param a Semimajor axis (a). + * @param ea Eccentric anomaly (E), in radians. + * @param b Semiminor axis (b). + * @return PQW Cartesian coordinates. + */ + template + math::vector3 cartesian(T ec, T a, T ea, T b) + { + return cartesian(spherical(ec, a, ea, b)); + } + + /** + * Constructs Cartesian PQW coordinates from Keplerian orbital elements. + * + * @param ec Eccentricity (e). + * @param a Semimajor axis (a). + * @param ea Eccentric anomaly (E), in radians. + * @return PQW Cartesian coordinates. + */ + template + math::vector3 cartesian(T ec, T a, T ea) + { + return cartesian(spherical(ec, a, ea)); + } + + /** + * Constructs an SE(3) transformation from a PQW frame to a BCI frame. + * + * @param om Right ascension of the ascending node (OMEGA), in radians. + * @param in Orbital inclination (i), in radians. + * @param w Argument of periapsis (omega), in radians. + * @return PQW to BCI transformation. + */ + template + math::transformation::se3 to_bci(T om, T in, T w) + { + const math::quaternion r = math::normalize + ( + math::quaternion::rotate_z(om) * + math::quaternion::rotate_x(in) * + math::quaternion::rotate_z(w) + ); + + return math::transformation::se3{{T(0), T(0), T(0)}, r}; + } + +} // namespace pqw + +/// Body-centered inertial (BCI) frame. +namespace bci { + + /** + * Converts BCI coordinates from spherical to Cartesian. + * + * @param v BCI spherical coordinates, in the ISO order of radial distance, declination (radians), and right ascension (radians). + * @return BCI Cartesian coordinates. + */ + template + math::vector3 cartesian(const math::vector3& v) + { + const T x = v[0] * std::cos(v[1]); + + return math::vector3 + { + x * std::cos(v[2]), + x * std::sin(v[2]), + v[0] * std::sin(v[1]), + }; + } + + /** + * Converts BCI coordinates from Cartesian to spherical. + * + * @param v BCI Cartesian coordinates. + * @return BCI spherical coordinates, in the ISO order of radial distance, declination (radians), and right ascension (radians). + */ + template + math::vector3 spherical(const math::vector3& v) + { + const T xx_yy = v.x() * v.x() + v.y() * v.y(); + + return math::vector3 + { + std::sqrt(xx_yy + v.z() * v.z()), + std::atan2(v.z(), std::sqrt(xx_yy)), + std::atan2(v.y(), v.x()) + }; + } + + /** + * Constructs an SE(3) transformation from a BCI frame to a BCBF frame. + * + * @param ra Right ascension of the north pole, in radians. + * @param dec Declination of the north pole, in radians. + * @param w Location of the prime meridian, as a rotation about the north pole, in radians. + * + * @return BCI to BCBF transformation. + * + * @see Archinal, B.A., A’Hearn, M.F., Bowell, E. et al. Report of the IAU Working Group on Cartographic Coordinates and Rotational Elements: 2009. Celest Mech Dyn Astr 109, 101–135 (2011). https://doi.org/10.1007/s10569-010-9320-4 + */ + template + math::transformation::se3 to_bcbf(T ra, T dec, T w) + { + const math::quaternion r = math::normalize + ( + math::quaternion::rotate_z(-math::half_pi - ra) * + math::quaternion::rotate_x(dec - math::half_pi) * + math::quaternion::rotate_z(-w) + ); + + return math::transformation::se3{{T(0), T(0), T(0)}, r}; + } + + /** + * Constructs an SE(3) transformation from a BCI frame to a PQW frame. + * + * @param om Right ascension of the ascending node (OMEGA), in radians. + * @param in Orbital inclination (i), in radians. + * @param w Argument of periapsis (omega), in radians. + * @return BCI to PQW transformation. + */ + template + math::transformation::se3 to_pqw(T om, T in, T w) + { + const math::quaternion r = math::normalize + ( + math::quaternion::rotate_z(-w) * + math::quaternion::rotate_x(-in) * + math::quaternion::rotate_z(-om) + ); + + return math::transformation::se3{{T(0), T(0), T(0)}, r}; + } + +} // namespace bci + +/// Body-centered, body-fixed (BCBF) frame. +namespace bcbf { + + /** + * Converts BCBF coordinates from spherical to Cartesian. + * + * @param v BCBF spherical coordinates, in the ISO order of radial distance, latitude (radians), and longitude (radians). + * @return BCBF Cartesian coordinates. + */ + template + math::vector3 cartesian(const math::vector3& v) + { + const T x = v[0] * std::cos(v[1]); + + return math::vector3 + { + x * std::cos(v[2]), + x * std::sin(v[2]), + v[0] * std::sin(v[1]), + }; + } + + /** + * Converts BCBF coordinates from Cartesian to spherical. + * + * @param v BCBF Cartesian coordinates. + * @return BCBF spherical coordinates, in the ISO order of radial distance, latitude (radians), and longitude (radians). + */ + template + math::vector3 spherical(const math::vector3& v) + { + const T xx_yy = v.x() * v.x() + v.y() * v.y(); + + return math::vector3 + { + std::sqrt(xx_yy + v.z() * v.z()), + std::atan2(v.z(), std::sqrt(xx_yy)), + std::atan2(v.y(), v.x()) + }; + } + + /** + * Constructs an SE(3) transformation from a BCBF frame to a BCI frame. + * + * @param ra Right ascension of the north pole, in radians. + * @param dec Declination of the north pole, in radians. + * @param w Location of the prime meridian, as a rotation about the north pole, in radians. + * + * @return BCBF to BCI transformation. + * + * @see Archinal, B.A., A’Hearn, M.F., Bowell, E. et al. Report of the IAU Working Group on Cartographic Coordinates and Rotational Elements: 2009. Celest Mech Dyn Astr 109, 101–135 (2011). https://doi.org/10.1007/s10569-010-9320-4 + */ + template + math::transformation::se3 to_bci(T ra, T dec, T w) + { + const math::quaternion r = math::normalize + ( + math::quaternion::rotate_z(w) * + math::quaternion::rotate_x(math::half_pi - dec) * + math::quaternion::rotate_z(ra + math::half_pi) + + ); + + return math::transformation::se3{{T(0), T(0), T(0)}, r}; + } + + /** + * Constructs an SE(3) transformation from a BCBF frame to an ENU frame. + * + * @param distance Radial distance of the observer from the center of the body. + * @param latitude Latitude of the observer, in radians. + * @param longitude Longitude of the observer, in radians. + * @return BCBF to ENU transformation. + */ + template + math::transformation::se3 to_enu(T distance, T latitude, T longitude) + { + const math::vector3 t = {T(0), T(0), -distance}; + const math::quaternion r = math::normalize + ( + math::quaternion::rotate_x(-math::half_pi + latitude) * + math::quaternion::rotate_z(-longitude - math::half_pi) + ); + + return math::transformation::se3{t, r}; + } + +} // namespace bcbf + +/// East, North, Up (ENU) horizontal frame. +namespace enu { + + /** + * Converts ENU coordinates from spherical to Cartesian. + * + * @param v ENU spherical coordinates, in the ISO order of radial distance, elevation (radians), and azimuth (radians). + * @return ENU Cartesian coordinates. + */ + template + math::vector3 cartesian(const math::vector3& v) + { + const T x = v[0] * std::cos(v[1]); + const T y = math::half_pi - v[2]; + + return math::vector3 + { + x * std::cos(y), + x * std::sin(y), + v[0] * std::sin(v[1]), + }; + } + + /** + * Converts ENU coordinates from Cartesian to spherical. + * + * @param v ENU Cartesian coordinates. + * @return ENU spherical coordinates, in the ISO order of radial distance, elevation (radians), and azimuth (radians). + */ + template + math::vector3 spherical(const math::vector3& v) + { + const T xx_yy = v.x() * v.x() + v.y() * v.y(); + + return math::vector3 + { + std::sqrt(xx_yy + v.z() * v.z()), + std::atan2(v.z(), std::sqrt(xx_yy)), + math::half_pi - std::atan2(v.y(), v.x()) + }; + } + + /** + * Constructs an SE(3) transformation from an ENU frame to a BCBF frame. + * + * @param distance Radial distance of the observer from the center of the body. + * @param latitude Latitude of the observer, in radians. + * @param longitude Longitude of the observer, in radians. + * @return ENU to BCBF transformation. + */ + template + math::transformation::se3 to_bcbf(T distance, T latitude, T longitude) + { + const math::vector3 t = {T(0), T(0), distance}; + const math::quaternion r = math::normalize + ( + math::quaternion::rotate_z(longitude + math::half_pi) * + math::quaternion::rotate_x(math::half_pi - latitude) + ); + + return math::transformation::se3{r * t, r}; + } + +} // namespace enu + +} // namespace frame +} // namespace orbit +} // namespace physics + +#endif // ANTKEEPER_PHYSICS_ORBIT_FRAME_HPP diff --git a/src/engine/physics/orbit/orbit.hpp b/src/engine/physics/orbit/orbit.hpp new file mode 100644 index 0000000..d4fbfc3 --- /dev/null +++ b/src/engine/physics/orbit/orbit.hpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_PHYSICS_ORBIT_HPP +#define ANTKEEPER_PHYSICS_ORBIT_HPP + +namespace physics { + +/** + * Orbital mechanics. + * + * @see Curtis, H. D. (2005). *Orbital mechanics for engineering students*. Amsterdam: Elsevier Butterworth Heinemann. + */ +namespace orbit {} + +} // namespace physics + +#include +#include +#include +#include + +#endif // ANTKEEPER_PHYSICS_ORBIT_HPP diff --git a/src/engine/physics/orbit/state.hpp b/src/engine/physics/orbit/state.hpp new file mode 100644 index 0000000..109c97b --- /dev/null +++ b/src/engine/physics/orbit/state.hpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_PHYSICS_ORBIT_STATE_HPP +#define ANTKEEPER_PHYSICS_ORBIT_STATE_HPP + +#include + +namespace physics { +namespace orbit { + +/** + * Pair of orbital state Cartesian position (r) and velocity (v) vectors. + * + * @tparam T Scalar type. + */ +template +struct state +{ + /// Scalar type. + typedef T scalar_type; + + /// Vector type. + typedef math::vector3 vector_type; + + /// Cartesian orbital position vector (r). + vector_type r; + + /// Cartesian orbital velocity vector (v). + vector_type v; +}; + +} // namespace orbit +} // namespace physics + +#endif // ANTKEEPER_PHYSICS_ORBIT_STATE_HPP diff --git a/src/engine/physics/orbit/trajectory.hpp b/src/engine/physics/orbit/trajectory.hpp new file mode 100644 index 0000000..76a0a8c --- /dev/null +++ b/src/engine/physics/orbit/trajectory.hpp @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_PHYSICS_ORBIT_TRAJECTORY_HPP +#define ANTKEEPER_PHYSICS_ORBIT_TRAJECTORY_HPP + +#include +#include +#include + +namespace physics { +namespace orbit { + +/** + * Describes the trajectory of an orbit with Chebyshev polynomials. + * + * @tparam t Real type. + */ +template +struct trajectory +{ + /// Start time of the trajectory. + T t0; + + /// End time of the trajectory. + T t1; + + /// Time step duration. + T dt; + + /// Chebyshev polynomial degree. + std::size_t n; + + /// Chebyshev polynomial coefficients. + std::vector a; + + /** + * Calculates the Cartesian position of a trajectory at a given time. + * + * @param t Time, on `[t0, t1)`. + * @return Trajectory position at time @p t. + */ + math::vector position(T t) const; +}; + +template +math::vector trajectory::position(T t) const +{ + t -= t0; + std::size_t i = static_cast(t / dt); + + const T* ax = &a[i * n * 3]; + const T* ay = ax + n; + const T* az = ay + n; + + t = (t / dt - i) * T(2) - T(1); + + math::vector3 r; + r.x() = math::polynomial::chebyshev::evaluate(ax, ay, t); + r.y() = math::polynomial::chebyshev::evaluate(ay, az, t); + r.z() = math::polynomial::chebyshev::evaluate(az, az + n, t); + + return r; +} + +} // namespace orbit +} // namespace physics + +#endif // ANTKEEPER_PHYSICS_ORBIT_TRAJECTORY_HPP diff --git a/src/engine/physics/physics.hpp b/src/engine/physics/physics.hpp new file mode 100644 index 0000000..797ba84 --- /dev/null +++ b/src/engine/physics/physics.hpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_PHYSICS_HPP +#define ANTKEEPER_PHYSICS_HPP + +/// Physics +namespace physics {} + +#include +#include +#include +#include +#include +#include +#include +#include + +#endif // ANTKEEPER_PHYSICS_HPP diff --git a/src/engine/physics/planck.hpp b/src/engine/physics/planck.hpp new file mode 100644 index 0000000..faa0971 --- /dev/null +++ b/src/engine/physics/planck.hpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_PHYSICS_PLANCK_HPP +#define ANTKEEPER_PHYSICS_PLANCK_HPP + +#include +#include + +namespace physics { + +/// Various forms of Planck's law. +namespace planck { + +/** + * Wavelength variant of Planck's law. + * + * @param t Temperature of the blackbody, in kelvin. + * @param lambda Wavelength of light, in meters. + * @param c Speed of light in medium. + * @return Spectral radiance, in watt per steradian per square meter per meter. + */ +template +T wavelength(T t, T lambda, T c = constants::speed_of_light); + +template +T wavelength(T t, T lambda, T c) +{ + const T hc = constants::planck * c; + const T lambda2 = lambda * lambda; + + // First radiation constant (c1L) + const T c1 = T(2) * hc * c; + + // Second radiation constant (c2) + const T c2 = hc / constants::boltzmann; + + return (c1 / (lambda2 * lambda2 * lambda)) / std::expm1(c2 / (lambda * t)); +} + +} // namespace planck + +} // namespace physics + +#endif // ANTKEEPER_PHYSICS_PLANCK_HPP diff --git a/src/physics/time/constants.hpp b/src/engine/physics/time/constants.hpp similarity index 100% rename from src/physics/time/constants.hpp rename to src/engine/physics/time/constants.hpp diff --git a/src/engine/physics/time/gregorian.hpp b/src/engine/physics/time/gregorian.hpp new file mode 100644 index 0000000..b5a7f9b --- /dev/null +++ b/src/engine/physics/time/gregorian.hpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_PHYSICS_TIME_GREGORIAN_HPP +#define ANTKEEPER_PHYSICS_TIME_GREGORIAN_HPP + +#include + +namespace physics { +namespace time { + +/// Gregorian calender time. +namespace gregorian { + +/** + * Calculates the JD time from a Gregorian date and time. Valid for all dates after November 23, −4713. + * + * @param year Astronomical year numbering. 1 BC is `0`, 2 BC is `-1`. + * @param month Month number on `[1, 12]`. + * @param day Day number on `[1, 31]`. + * @param hour Hour number on `[0, 23]`. + * @param minute Minute number on `[0, 59]`. + * @param second Fractional second on `[0.0, 60.0)`. + * @param utc UTC offset. + * + * @return JD time. + * + * @see L. E. Doggett, Ch. 12, "Calendars", p. 606, in Seidelmann 1992 + */ +template +T to_jd(int year, int month, int day, int hour, int minute, T second, T utc) +{ + T jdn = static_cast((1461 * (year + 4800 + (month - 14) / 12)) / 4 + (367 * (month - 2 - 12 * ((month - 14) / 12))) / 12 - (3 * ((year + 4900 + (month - 14) / 12) / 100)) / 4 + day - 32075); + return jdn + static_cast(hour - 12) / T(24) + static_cast(minute) / T(1440) + static_cast(second) / T(86400) - utc / T(24); +} + +/** + * Calculates the UT1 time from a Gregorian date and time. Valid for all dates after November 23, −4713. + * + * @param year Astronomical year numbering. 1 BC is `0`, 2 BC is `-1`. + * @param month Month number on `[1, 12]`. + * @param day Day number on `[1, 31]`. + * @param hour Hour number on `[0, 23]`. + * @param minute Minute number on `[0, 59]`. + * @param second Fractional second on `[0.0, 60.0)`. + * @param utc UTC offset. + * + * @return UT1 time. + */ +template +T to_ut1(int year, int month, int day, int hour, int minute, T second, T utc) +{ + return physics::time::jd::to_ut1(to_jd(year, month, day, hour, minute, second, utc)); +} + +} // namespace gregorian + +} // namespace time +} // namespace physics + +#endif // ANTKEEPER_PHYSICS_TIME_GREGORIAN_HPP diff --git a/src/physics/time/jd.hpp b/src/engine/physics/time/jd.hpp similarity index 100% rename from src/physics/time/jd.hpp rename to src/engine/physics/time/jd.hpp diff --git a/src/engine/physics/time/time.hpp b/src/engine/physics/time/time.hpp new file mode 100644 index 0000000..8365042 --- /dev/null +++ b/src/engine/physics/time/time.hpp @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_PHYSICS_TIME_HPP +#define ANTKEEPER_PHYSICS_TIME_HPP + +namespace physics { + +/// Time-related functions. +namespace time {} + +} // namespace physics + +#include +#include +#include +#include +#include + +#endif // ANTKEEPER_PHYSICS_TIME_HPP diff --git a/src/engine/physics/time/ut1.hpp b/src/engine/physics/time/ut1.hpp new file mode 100644 index 0000000..a009ccc --- /dev/null +++ b/src/engine/physics/time/ut1.hpp @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_PHYSICS_TIME_UT1_HPP +#define ANTKEEPER_PHYSICS_TIME_UT1_HPP + +#include + +namespace physics { +namespace time { + +/// UT1 universal time. +namespace ut1 { + +/** + * Converts UT1 to JD. + * + * @param t UT1 time. + * @return JD time. + */ +template +T to_jd(T t) +{ + return t + T(2451545); +} + +/** + * Calculates the Earth Rotation Angle (ERA) at a given UT1 date. + * + * @param t J2000 UT1 date. + * @return ERA at the given date, in radians. + */ +template +T era(T t) +{ + return math::two_pi * (T(0.7790572732640) + T(1.00273781191135448) * t); +} + +} // namespace ut1 + +} // namespace time +} // namespace physics + +#endif // ANTKEEPER_PHYSICS_TIME_UT1_HPP diff --git a/src/engine/physics/time/utc.hpp b/src/engine/physics/time/utc.hpp new file mode 100644 index 0000000..bea62b7 --- /dev/null +++ b/src/engine/physics/time/utc.hpp @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_PHYSICS_TIME_UTC_HPP +#define ANTKEEPER_PHYSICS_TIME_UTC_HPP + +#include + +namespace physics { +namespace time { + +/// Coordinated Universal Time (UTC). +namespace utc { + +/** + * Calculates the UTC offset at a given longitude + * + * @param longitude Longitude, in radians. + * @return UTC offset. + */ +template +T offset(T longitude) +{ + return longitude / (math::two_pi / 24.0); +} + +} // namespace utc + +} // namespace time +} // namespace physics + +#endif // ANTKEEPER_PHYSICS_TIME_UTC_HPP diff --git a/src/render/anti-aliasing-method.hpp b/src/engine/render/anti-aliasing-method.hpp similarity index 100% rename from src/render/anti-aliasing-method.hpp rename to src/engine/render/anti-aliasing-method.hpp diff --git a/src/render/blend-mode.hpp b/src/engine/render/blend-mode.hpp similarity index 100% rename from src/render/blend-mode.hpp rename to src/engine/render/blend-mode.hpp diff --git a/src/engine/render/compositor.cpp b/src/engine/render/compositor.cpp new file mode 100644 index 0000000..cf99089 --- /dev/null +++ b/src/engine/render/compositor.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include + +namespace render { + +void compositor::add_pass(pass* pass) +{ + passes.push_back(pass); +} + +void compositor::remove_pass(pass* pass) +{ + passes.remove(pass); +} + +void compositor::remove_passes() +{ + passes.clear(); +} + +void compositor::composite(const render::context& ctx, render::queue& queue) const +{ + for (const pass* pass: passes) + { + if (pass->is_enabled()) + { + pass->render(ctx, queue); + } + } +} + +} // namespace render diff --git a/src/engine/render/compositor.hpp b/src/engine/render/compositor.hpp new file mode 100644 index 0000000..04e1538 --- /dev/null +++ b/src/engine/render/compositor.hpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_RENDER_COMPOSITOR_HPP +#define ANTKEEPER_RENDER_COMPOSITOR_HPP + +#include +#include +#include + +namespace render { + +class pass; + +/** + * + */ +class compositor +{ +public: + void add_pass(pass* pass); + void remove_pass(pass* pass); + void remove_passes(); + + void composite(const render::context& ctx, render::queue& queue) const; + + const std::list* get_passes() const; + +private: + std::list passes; +}; + +inline const std::list* compositor::get_passes() const +{ + return &passes; +} + +} // namespace render + +#endif // ANTKEEPER_RENDER_COMPOSITOR_HPP diff --git a/src/engine/render/context.hpp b/src/engine/render/context.hpp new file mode 100644 index 0000000..07ef15f --- /dev/null +++ b/src/engine/render/context.hpp @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_RENDER_CONTEXT_HPP +#define ANTKEEPER_RENDER_CONTEXT_HPP + +#include +#include +#include +#include +#include + +namespace scene +{ + class camera; + class collection; + class object_base; +} + +namespace render { + +/** + * Context of a renderer. + */ +struct context +{ + /// Pointer to the camera. + const scene::camera* camera; + + /// Camera transform. + math::transform camera_transform; + + /// Camera forward vector + float3 camera_forward; + + /// Camera up vector. + float3 camera_up; + + /// Camera culling volume. + const geom::bounding_volume* camera_culling_volume; + + /// Near clipping plane of the camera. + geom::plane clip_near; + + /// Camera view matrix. + float4x4 view; + + /// Camera projection matrix. + float4x4 projection; + + /// Camera view projection matrix. + float4x4 view_projection; + + /// Camera exposure normalization factor. + float exposure; + + /// Collection of scene objects being rendered. + const scene::collection* collection; + + /// Current time, in seconds. + float t; + + /// Timestep, in seconds. + float dt; + + /// Subframe interpolation factor. + float alpha; + + /// List of objects visible to the active camera. + std::list visible_objects; +}; + +} // namespace render + +#endif // ANTKEEPER_RENDER_CONTEXT_HPP diff --git a/src/render/material-flags.hpp b/src/engine/render/material-flags.hpp similarity index 100% rename from src/render/material-flags.hpp rename to src/engine/render/material-flags.hpp diff --git a/src/engine/render/material-property.cpp b/src/engine/render/material-property.cpp new file mode 100644 index 0000000..3a37f33 --- /dev/null +++ b/src/engine/render/material-property.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include + +namespace render { + +material_property_base::material_property_base(): + input(nullptr) +{} + +bool material_property_base::connect(const gl::shader_input* input) +{ + if (!input || input->get_data_type() != get_data_type()) + { + return false; + } + + this->input = input; + + return true; +} + +void material_property_base::disconnect() +{ + this->input = nullptr; +} + +} // namespace render diff --git a/src/engine/render/material-property.hpp b/src/engine/render/material-property.hpp new file mode 100644 index 0000000..92bf022 --- /dev/null +++ b/src/engine/render/material-property.hpp @@ -0,0 +1,460 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_RENDER_MATERIAL_PROPERTY_HPP +#define ANTKEEPER_RENDER_MATERIAL_PROPERTY_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace render { + +class material; + +/** + * Abstract base class for material properties. + */ +class material_property_base +{ +public: + /** + * Connects the material property to a shader input. + * + * @param input Shader input to which the material property should be connected. + * @return `true` if the property was connected to the input successfully, `false` otherwise. + */ + bool connect(const gl::shader_input* input); + + /** + * Disconnects the material property from its shader input. + */ + void disconnect(); + + /** + * Sets state 0 = state 1. + */ + virtual void update_tweens() = 0; + + /** + * Uploads the material property to its shader program. + * + * @param a Interpolation factor. Should be on `[0.0, 1.0]`. + * @return `true` if the property was uploaded successfully, `false` otherwise. + */ + virtual bool upload(double a) const = 0; + + /** + * Returns the type of data which the property contains. + */ + virtual gl::shader_variable_type get_data_type() const = 0; + + /** + * Returns `true` if the material property is connected to a shader input, `false` otherwise. + */ + bool is_connected() const; + + /** + * Creates a copy of this material property. + */ + virtual material_property_base* clone() const = 0; + +protected: + material_property_base(); + + const gl::shader_input* input; +}; + +inline bool material_property_base::is_connected() const +{ + return (input != nullptr); +} + +/** + * A property of a material which can be uploaded to a shader program via a shader input. + * + * @tparam T Property data type. + */ +template +class material_property: public material_property_base +{ +public: + typedef tween tween_type; + typedef typename tween::interpolator_type interpolator_type; + + /// Default tween interpolator function for this material property type. + static T default_interpolator(const T& x, const T& y, double a); + + /** + * Creates a material property. + * + * @param element_count Number of elements in the property array. + */ + material_property(std::size_t element_count); + + /** + * Destroys a material property. + */ + virtual ~material_property(); + + material_property(const material_property&) = delete; + material_property& operator=(const material_property&) = delete; + + /// @copydoc material_property_base::update_tweens() + virtual void update_tweens(); + + /// @copydoc material_property_base::upload() const + virtual bool upload(double a) const; + + /** + * Sets the value of this property. + * + * @param value Value to set. + */ + void set_value(const T& value); + + /** + * Sets the value of a single element in this array property. + * + * @param index Index of an array element. + * @param value Value to set. + */ + void set_value(std::size_t index, const T& value); + + /** + * Sets the values of a range of elements in this array property. + * + * @param index Index of the first array element to set. + * @param values Pointer to an array of values to set. + * @param count Number of elements to set. + */ + void set_values(std::size_t index, const T* values, std::size_t count); + + /** + * Sets the tween interpolator function. + * + * @param interpolator Tween interpolator function. + */ + void set_tween_interpolator(interpolator_type interpolator); + + /// Returns the value of the first element in this property. + const T& get_value() const; + + /** + * Returns the value of the first element in this property. + * + * @param index Index of an array element. + * @return Value of the element at the specified index. + */ + const T& get_value(std::size_t index) const; + + /// @copydoc material_property_base::get_data_type() const + virtual gl::shader_variable_type get_data_type() const; + + /// @copydoc material_property_base::clone() const + virtual material_property_base* clone() const; + +private: + std::size_t element_count; + tween* values; +}; + +template +inline T material_property::default_interpolator(const T& x, const T& y, double a) +{ + return y; +} + +template <> +inline float material_property::default_interpolator(const float& x, const float& y, double a) +{ + return math::lerp(x, y, static_cast(a)); +} + +template <> +inline float2 material_property::default_interpolator(const float2& x, const float2& y, double a) +{ + return math::lerp(x, y, static_cast(a)); +} + +template <> +inline float3 material_property::default_interpolator(const float3& x, const float3& y, double a) +{ + return math::lerp(x, y, static_cast(a)); +} + +template <> +inline float4 material_property::default_interpolator(const float4& x, const float4& y, double a) +{ + return math::lerp(x, y, static_cast(a)); +} + +template +material_property::material_property(std::size_t element_count): + element_count(element_count), + values(nullptr) +{ + values = new tween[element_count]; + set_tween_interpolator(default_interpolator); +} + +template +material_property::~material_property() +{ + delete[] values; +} + +template +void material_property::update_tweens() +{ + for (std::size_t i = 0; i < element_count; ++i) + { + values[i].update(); + } +} + +template +bool material_property::upload(double a) const +{ + if (!is_connected()) + { + return false; + } + + if (element_count > 1) + { + for (std::size_t i = 0; i < element_count; ++i) + { + if (!input->upload(i, values[i].interpolate(static_cast(a)))) + return false; + } + + return true; + } + else + { + return input->upload(values[0].interpolate(static_cast(a))); + } +} + +template +void material_property::set_value(const T& value) +{ + values[0][1] = value; +} + +template +void material_property::set_value(std::size_t index, const T& value) +{ + values[index][1] = value; +} + +template +void material_property::set_values(std::size_t index, const T* values, std::size_t count) +{ + for (std::size_t i = 0; i < count; ++i) + { + this->values[index + i][1] = values[i]; + } +} + +template +void material_property::set_tween_interpolator(interpolator_type interpolator) +{ + for (std::size_t i = 0; i < element_count; ++i) + { + this->values[i].set_interpolator(interpolator); + } +} + +template +inline const T& material_property::get_value() const +{ + return values[0][1]; +} + +template +inline const T& material_property::get_value(std::size_t index) const +{ + return values[index][1]; +} + +template <> +inline gl::shader_variable_type material_property::get_data_type() const +{ + return gl::shader_variable_type::bool1; +} + +template <> +inline gl::shader_variable_type material_property::get_data_type() const +{ + return gl::shader_variable_type::bool2; +} + +template <> +inline gl::shader_variable_type material_property::get_data_type() const +{ + return gl::shader_variable_type::bool3; +} + +template <> +inline gl::shader_variable_type material_property::get_data_type() const +{ + return gl::shader_variable_type::bool4; +} + +template <> +inline gl::shader_variable_type material_property::get_data_type() const +{ + return gl::shader_variable_type::int1; +} + +template <> +inline gl::shader_variable_type material_property::get_data_type() const +{ + return gl::shader_variable_type::int2; +} + +template <> +inline gl::shader_variable_type material_property::get_data_type() const +{ + return gl::shader_variable_type::int3; +} + +template <> +inline gl::shader_variable_type material_property::get_data_type() const +{ + return gl::shader_variable_type::int4; +} + +template <> +inline gl::shader_variable_type material_property::get_data_type() const +{ + return gl::shader_variable_type::uint1; +} + +template <> +inline gl::shader_variable_type material_property::get_data_type() const +{ + return gl::shader_variable_type::uint2; +} + +template <> +inline gl::shader_variable_type material_property::get_data_type() const +{ + return gl::shader_variable_type::uint3; +} + +template <> +inline gl::shader_variable_type material_property::get_data_type() const +{ + return gl::shader_variable_type::uint4; +} + +template <> +inline gl::shader_variable_type material_property::get_data_type() const +{ + return gl::shader_variable_type::float1; +} + +template <> +inline gl::shader_variable_type material_property::get_data_type() const +{ + return gl::shader_variable_type::float2; +} + +template <> +inline gl::shader_variable_type material_property::get_data_type() const +{ + return gl::shader_variable_type::float3; +} + +template <> +inline gl::shader_variable_type material_property::get_data_type() const +{ + return gl::shader_variable_type::float4; +} + +template <> +inline gl::shader_variable_type material_property::get_data_type() const +{ + return gl::shader_variable_type::float2x2; +} + +template <> +inline gl::shader_variable_type material_property::get_data_type() const +{ + return gl::shader_variable_type::float3x3; +} + +template <> +inline gl::shader_variable_type material_property::get_data_type() const +{ + return gl::shader_variable_type::float4x4; +} + +template <> +inline gl::shader_variable_type material_property::get_data_type() const +{ + return gl::shader_variable_type::texture_1d; +} + +template <> +inline gl::shader_variable_type material_property::get_data_type() const +{ + return gl::shader_variable_type::texture_2d; +} + +template <> +inline gl::shader_variable_type material_property::get_data_type() const +{ + return gl::shader_variable_type::texture_3d; +} + +template <> +inline gl::shader_variable_type material_property::get_data_type() const +{ + return gl::shader_variable_type::texture_cube; +} + +template +material_property_base* material_property::clone() const +{ + material_property* property = new material_property(element_count); + for (std::size_t i = 0; i < element_count; ++i) + { + property->values[i][0] = values[i][0]; + property->values[i][1] = values[i][1]; + } + property->input = input; + + return property; +} + +} // namespace render + +#endif // ANTKEEPER_RENDER_MATERIAL_PROPERTY_HPP diff --git a/src/engine/render/material.cpp b/src/engine/render/material.cpp new file mode 100644 index 0000000..d5a7579 --- /dev/null +++ b/src/engine/render/material.cpp @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include + +namespace render { + +material::material(gl::shader_program* program): + program(program), + flags(0), + blend_mode(blend_mode::opaque), + opacity_threshold(0.5f), + two_sided(false), + shadow_mode(shadow_mode::opaque) +{} + +material::material(): + material(nullptr) +{} + +material::material(const material& other) +{ + *this = other; +} + +material::~material() +{ + for (material_property_base* property: properties) + { + delete property; + } +} + +material& material::operator=(const material& other) +{ + // Remove all properties + for (material_property_base* property: properties) + { + delete property; + } + properties.clear(); + property_map.clear(); + + this->program = other.program; + this->flags = other.flags; + this->blend_mode = other.blend_mode; + this->opacity_threshold = other.opacity_threshold; + this->two_sided = other.two_sided; + this->shadow_mode = other.shadow_mode; + for (auto it = other.property_map.begin(); it != other.property_map.end(); ++it) + { + material_property_base* property = it->second->clone(); + properties.push_back(property); + property_map[it->first] = property; + } + + return *this; +} + +void material::update_tweens() +{ + for (material_property_base* property: properties) + { + property->update_tweens(); + } +} + +std::size_t material::upload(double a) const +{ + if (!program) + { + return false; + } + + std::size_t failed_upload_count = 0; + + for (material_property_base* property: properties) + { + if (!property->upload(a)) + { + ++failed_upload_count; + } + } + + return failed_upload_count; +} + +void material::set_shader_program(gl::shader_program* program) +{ + this->program = program; + reconnect_properties(); +} + +void material::set_flags(std::uint32_t flags) noexcept +{ + this->flags = flags; +} + +void material::set_blend_mode(render::blend_mode mode) noexcept +{ + blend_mode = mode; +} + +void material::set_opacity_threshold(float threshold) noexcept +{ + opacity_threshold = threshold; +} + +void material::set_two_sided(bool two_sided) noexcept +{ + this->two_sided = two_sided; +} + +void material::set_shadow_mode(render::shadow_mode mode) noexcept +{ + shadow_mode = mode; +} + +std::size_t material::reconnect_properties() +{ + std::size_t disconnected_property_count = properties.size(); + + for (auto it = property_map.begin(); it != property_map.end(); ++it) + { + material_property_base* property = it->second; + + property->disconnect(); + + if (program != nullptr) + { + if (property->connect(program->get_input(it->first))) + { + --disconnected_property_count; + } + } + } + + return disconnected_property_count; +} + +} // namespace render diff --git a/src/engine/render/material.hpp b/src/engine/render/material.hpp new file mode 100644 index 0000000..2e1122c --- /dev/null +++ b/src/engine/render/material.hpp @@ -0,0 +1,254 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_RENDER_MATERIAL_HPP +#define ANTKEEPER_RENDER_MATERIAL_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace render { + +/** + * A material is associated with exactly one shader program and contains a set of material properties which can be uploaded to that shader program via shader inputs. + */ +class material +{ +public: + /** + * Creates a material. + * + * @param program Shader program with which to associate this material. + */ + explicit material(gl::shader_program* program); + + /** + * Creates a material. + */ + material(); + + /** + * Creates a copy of another material. + * + * @param other Material to copy. + */ + material(const material& other); + + /** + * Destroys a material. + */ + ~material(); + + /** + * Makes this material a copy of aother material. + * + * @param other Material to copy. + * @return Reference to this material. + */ + material& operator=(const material& other); + + /** + * Sets state 0 = state 1 for each material property tween. + */ + void update_tweens(); + + /** + * Uploads each material property to the material's shader program. + * + * @param a Interpolation factor. Should be on `[0.0, 1.0]`. + * @return Number of material property uploads which failed. + */ + std::size_t upload(double a) const; + + /** + * Sets the material's shader program and reconnects all shader properties to their corresponding shader inputs. + * + * @param program Shader program with which to associate the material. + */ + void set_shader_program(gl::shader_program* program); + + /** + * Sets the material flags. + * + * @param flags Material flags. + */ + void set_flags(std::uint32_t flags) noexcept; + + /** + * Sets the material blend mode. + * + * @param mode Blend mode. + */ + void set_blend_mode(blend_mode mode) noexcept; + + /** + * Sets the opacity mask threshold value for masked blend mode. + * + * @param threshold Opacity mask threshold value, above which the surface is considered opaque. + * + * @see render::blend_mode::masked + */ + void set_opacity_threshold(float threshold) noexcept; + + /** + * Enables or disables back-face culling of the material surface. + * + * @param two_sided `true` to disable back-face culling, or `false` to enable it. + */ + void set_two_sided(bool two_sided) noexcept; + + /** + * Sets the material shadow mode. + * + * @param mode Shadow mode. + */ + void set_shadow_mode(shadow_mode mode) noexcept; + + /** + * Adds a material array property to the material. + * + * @param name Name of the material array property. + * @param element_count Number of elements in the array. + * @return Pointer to the added material property. + */ + template + material_property* add_property(const std::string& name, std::size_t element_count = 1); + + /** + * Returns the shader program with which this material is associated. + */ + gl::shader_program* get_shader_program() const; + + /// Returns the material flags. + std::uint32_t get_flags() const noexcept; + + /// Returns the material blend mode. + blend_mode get_blend_mode() const noexcept; + + /// Returns the opacity mask threshold value. + float get_opacity_threshold() const noexcept; + + /// Returns `true` if the material surface is two-sided, and `false` otherwise. + bool is_two_sided() const noexcept; + + /// Returns the material shadow mode. + shadow_mode get_shadow_mode() const noexcept; + + /** + * Returns the material property with the specified name, or `nullptr` if the material could not be found. + */ + material_property_base* get_property(const std::string& name) const; + + /** + * Returns a list of all material properties in the material. + */ + const std::list* get_properties() const; + +private: + /** + * Attempts to reconnect all material properties to their corresponding shader inputs. + * + * @return Number of disconnected properties. + */ + std::size_t reconnect_properties(); + + gl::shader_program* program; + std::uint32_t flags; + blend_mode blend_mode; + float opacity_threshold; + bool two_sided; + shadow_mode shadow_mode; + std::list properties; + std::unordered_map property_map; +}; + +template +material_property* material::add_property(const std::string& name, std::size_t element_count) +{ + // Allocate property + material_property* property = new material_property(element_count); + + // Add to property list and map + properties.push_back(property); + property_map[name] = property; + + // Attempt to connect property to its corresponding shader input + if (program) + { + property->connect(program->get_input(name)); + } + + return property; +} + +inline gl::shader_program* material::get_shader_program() const +{ + return program; +} + +inline std::uint32_t material::get_flags() const noexcept +{ + return flags; +} + +inline blend_mode material::get_blend_mode() const noexcept +{ + return blend_mode; +} + +inline float material::get_opacity_threshold() const noexcept +{ + return opacity_threshold; +} + +inline bool material::is_two_sided() const noexcept +{ + return two_sided; +} + +inline shadow_mode material::get_shadow_mode() const noexcept +{ + return shadow_mode; +} + +inline material_property_base* material::get_property(const std::string& name) const +{ + if (auto it = property_map.find(name); it != property_map.end()) + { + return it->second; + } + + return nullptr; +} + +inline const std::list* material::get_properties() const +{ + return &properties; +} + +} // namespace render + +#endif // ANTKEEPER_RENDER_MATERIAL_HPP diff --git a/src/engine/render/model.cpp b/src/engine/render/model.cpp new file mode 100644 index 0000000..9b3dc88 --- /dev/null +++ b/src/engine/render/model.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include + +namespace render { + +model::model(): + bounds({0, 0, 0}, {0, 0, 0}) +{} + +model::~model() +{ + for (model_group* group: groups) + { + delete group; + } +} + +model_group* model::add_group(const std::string& name) +{ + if (!name.empty()) + { + if (auto it = group_map.find(name); it != group_map.end()) + { + return it->second; + } + } + + model_group* group = new model_group(); + group->index = groups.size(); + group->name = name; + group->material = nullptr; + group->drawing_mode = gl::drawing_mode::triangles; + group->start_index = 0; + group->index_count = 0; + + groups.push_back(group); + + if (!name.empty()) + { + group_map[name] = group; + } + + return group; +} + +bool model::remove_group(const std::string& name) +{ + if (auto it = group_map.find(name); it != group_map.end()) + { + return remove_group(it->second); + } + + return false; +} + +bool model::remove_group(model_group* group) +{ + // Remove from group map + if (!group->name.empty()) + { + if (auto it = group_map.find(group->name); it != group_map.end()) + { + group_map.erase(it); + } + } + + // Adjust indices of groups after this group + for (std::size_t i = group->index + 1; i < groups.size(); ++i) + { + --groups[i]->index; + } + + // Remove from groups + groups.erase(groups.begin() + group->index); + + // Deallocate group + delete group; + + return true; +} + +const model_group* model::get_group(const std::string& name) const +{ + if (auto it = group_map.find(name); it != group_map.end()) + { + return it->second; + } + + return nullptr; +} + +model_group* model::get_group(const std::string& name) +{ + if (auto it = group_map.find(name); it != group_map.end()) + { + return it->second; + } + + return nullptr; +} + +} // namespace render diff --git a/src/engine/render/model.hpp b/src/engine/render/model.hpp new file mode 100644 index 0000000..6b871ef --- /dev/null +++ b/src/engine/render/model.hpp @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_RENDER_MODEL_HPP +#define ANTKEEPER_RENDER_MODEL_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace render { + +/** + * Part of a model which is associated with exactly one material. + */ +class model_group +{ +public: + void set_material(material* material); + void set_drawing_mode(gl::drawing_mode mode); + void set_start_index(std::size_t index); + void set_index_count(std::size_t count); + + std::size_t get_index() const; + const std::string& get_name() const; + const material* get_material() const; + material* get_material(); + gl::drawing_mode get_drawing_mode() const; + std::size_t get_start_index() const; + std::size_t get_index_count() const; + +private: + friend class model; + + std::size_t index; + std::string name; + material* material; + gl::drawing_mode drawing_mode; + std::size_t start_index; + std::size_t index_count; +}; + +inline void model_group::set_material(render::material* material) +{ + this->material = material; +} + +inline void model_group::set_drawing_mode(gl::drawing_mode mode) +{ + this->drawing_mode = mode; +} + +inline void model_group::set_start_index(std::size_t index) +{ + this->start_index = index; +} + +inline void model_group::set_index_count(std::size_t count) +{ + this->index_count = count; +} + +inline std::size_t model_group::get_index() const +{ + return index; +} + +inline const std::string& model_group::get_name() const +{ + return name; +} + +inline const material* model_group::get_material() const +{ + return material; +} + +inline material* model_group::get_material() +{ + return material; +} + +inline gl::drawing_mode model_group::get_drawing_mode() const +{ + return drawing_mode; +} + +inline std::size_t model_group::get_start_index() const +{ + return start_index; +} + +inline std::size_t model_group::get_index_count() const +{ + return index_count; +} + +/** + * + */ +class model +{ +public: + typedef geom::aabb aabb_type; + + model(); + ~model(); + + void set_bounds(const aabb_type& bounds); + + model_group* add_group(const std::string& name = std::string()); + + bool remove_group(const std::string& name); + bool remove_group(model_group* group); + + const aabb_type& get_bounds() const; + + const model_group* get_group(const std::string& name) const; + model_group* get_group(const std::string& name); + + const std::vector* get_groups() const; + + const gl::vertex_array* get_vertex_array() const; + gl::vertex_array* get_vertex_array(); + + const gl::vertex_buffer* get_vertex_buffer() const; + gl::vertex_buffer* get_vertex_buffer(); + + const skeleton& get_skeleton() const; + skeleton& get_skeleton(); + +private: + aabb_type bounds; + std::vector groups; + std::unordered_map group_map; + gl::vertex_array vao; + gl::vertex_buffer vbo; + ::skeleton skeleton; +}; + +inline void model::set_bounds(const aabb_type& bounds) +{ + this->bounds = bounds; +} + +inline const typename model::aabb_type& model::get_bounds() const +{ + return bounds; +} + +inline const std::vector* model::get_groups() const +{ + return &groups; +} + +inline const gl::vertex_array* model::get_vertex_array() const +{ + return &vao; +} + +inline gl::vertex_array* model::get_vertex_array() +{ + return &vao; +} + +inline const gl::vertex_buffer* model::get_vertex_buffer() const +{ + return &vbo; +} + +inline gl::vertex_buffer* model::get_vertex_buffer() +{ + return &vbo; +} + +inline const skeleton& model::get_skeleton() const +{ + return skeleton; +} + +inline skeleton& model::get_skeleton() +{ + return skeleton; +} + +} // namespace render + +#endif // ANTKEEPER_RENDER_MODEL_HPP diff --git a/src/engine/render/operation.hpp b/src/engine/render/operation.hpp new file mode 100644 index 0000000..56f669c --- /dev/null +++ b/src/engine/render/operation.hpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_RENDER_OPERATION_HPP +#define ANTKEEPER_RENDER_OPERATION_HPP + +#include +#include +#include +#include +#include + +namespace render { + +class material; + +/** + * Encapsulates an atomic render operation. + */ +struct operation +{ + const float4x4* skinning_palette; + std::size_t bone_count; + const material* material; + const gl::vertex_array* vertex_array; + gl::drawing_mode drawing_mode; + std::size_t start_index; + std::size_t index_count; + float4x4 transform; + float depth; + std::size_t instance_count; +}; + +} // namespace render + +#endif // ANTKEEPER_RENDER_OPERATION_HPP diff --git a/src/engine/render/pass.cpp b/src/engine/render/pass.cpp new file mode 100644 index 0000000..add2228 --- /dev/null +++ b/src/engine/render/pass.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include + +namespace render { + +pass::pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer): + rasterizer(rasterizer), + framebuffer(framebuffer), + enabled(true) +{} + +pass::~pass() +{} + +void pass::set_enabled(bool enabled) +{ + this->enabled = enabled; +} + +void pass::set_framebuffer(const gl::framebuffer* framebuffer) +{ + this->framebuffer = framebuffer; +} + +} // namespace render diff --git a/src/engine/render/pass.hpp b/src/engine/render/pass.hpp new file mode 100644 index 0000000..7b2601b --- /dev/null +++ b/src/engine/render/pass.hpp @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_RENDER_PASS_HPP +#define ANTKEEPER_RENDER_PASS_HPP + +#include +#include +#include +#include + +namespace render { + +/** + * Render pass. + */ +class pass +{ +public: + pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer); + virtual ~pass(); + + virtual void render(const render::context& ctx, render::queue& queue) const = 0; + + void set_enabled(bool enabled); + bool is_enabled() const; + + void set_framebuffer(const gl::framebuffer* framebuffer); + +protected: + gl::rasterizer* rasterizer; + const gl::framebuffer* framebuffer; + +private: + bool enabled; +}; + +inline bool pass::is_enabled() const +{ + return enabled; +} + +} // namespace render + +#endif // ANTKEEPER_RENDER_PASS_HPP + diff --git a/src/engine/render/passes/bloom-pass.cpp b/src/engine/render/passes/bloom-pass.cpp new file mode 100644 index 0000000..6fbeea9 --- /dev/null +++ b/src/engine/render/passes/bloom-pass.cpp @@ -0,0 +1,303 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace render { + +bloom_pass::bloom_pass(gl::rasterizer* rasterizer, resource_manager* resource_manager): + pass(rasterizer, nullptr), + source_texture(nullptr), + mip_chain_length(0), + filter_radius(0.005f), + corrected_filter_radius{filter_radius, filter_radius} +{ + // Load downsample shader template + downsample_shader_template = resource_manager->load("bloom-downsample.glsl"); + + // Build downsample shader program with Karis averaging + downsample_karis_shader = downsample_shader_template->build + ( + { + {"KARIS_AVERAGE", std::string()} + } + ); + downsample_karis_source_texture_input = downsample_karis_shader->get_input("source_texture"); + + // Build downsample shader program without Karis averaging + downsample_shader = downsample_shader_template->build(); + downsample_source_texture_input = downsample_shader->get_input("source_texture"); + + // Load upsample shader template + upsample_shader_template = resource_manager->load("bloom-upsample.glsl"); + + // Build upsample shader program + upsample_shader = upsample_shader_template->build(); + upsample_source_texture_input = upsample_shader->get_input("source_texture"); + upsample_filter_radius_input = upsample_shader->get_input("filter_radius"); + + const float vertex_data[] = + { + -1.0f, 1.0f, + -1.0f, -1.0f, + 1.0f, 1.0f, + 1.0f, 1.0f, + -1.0f, -1.0f, + 1.0f, -1.0f + }; + + std::size_t vertex_size = 2; + std::size_t vertex_stride = sizeof(float) * vertex_size; + std::size_t vertex_count = 6; + + quad_vbo = new gl::vertex_buffer(sizeof(float) * vertex_size * vertex_count, vertex_data); + quad_vao = new gl::vertex_array(); + + // Define position vertex attribute + gl::vertex_attribute position_attribute; + position_attribute.buffer = quad_vbo; + position_attribute.offset = 0; + position_attribute.stride = vertex_stride; + position_attribute.type = gl::vertex_attribute_type::float_32; + position_attribute.components = 2; + + // Bind vertex attributes to VAO + quad_vao->bind(render::vertex_attribute::position, position_attribute); +} + +bloom_pass::~bloom_pass() +{ + delete quad_vao; + delete quad_vbo; + + set_mip_chain_length(0); + + delete downsample_karis_shader; + delete downsample_shader; + delete upsample_shader; + + /// @TODO + //resource_manager->unload("bloom-downsample.glsl"); + //resource_manager->unload("bloom-upsample.glsl"); +} + +void bloom_pass::render(const render::context& ctx, render::queue& queue) const +{ + if (!source_texture || !mip_chain_length) + return; + + // Disable depth testing + glDisable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); + + // Enable back-face culling + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + + // Disable blending + glDisable(GL_BLEND); + + // Downsample first mip with Karis average + { + rasterizer->use_program(*downsample_karis_shader); + downsample_karis_source_texture_input->upload(source_texture); + + rasterizer->use_framebuffer(*framebuffers[0]); + rasterizer->set_viewport(0, 0, textures[0]->get_width(), textures[0]->get_height()); + + rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6); + } + + // Downsample remaining mips + rasterizer->use_program(*downsample_shader); + for (int i = 1; i < static_cast(mip_chain_length); ++i) + { + rasterizer->use_framebuffer(*framebuffers[i]); + rasterizer->set_viewport(0, 0, textures[i]->get_width(), textures[i]->get_height()); + + // Use previous downsample texture as downsample source + downsample_source_texture_input->upload(textures[i - 1]); + + rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6); + } + + // Enable additive blending + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE); + glBlendEquation(GL_FUNC_ADD); + + // Upsample + rasterizer->use_program(*upsample_shader); + upsample_filter_radius_input->upload(corrected_filter_radius); + for (int i = static_cast(mip_chain_length) - 1; i > 0; --i) + { + const int j = i - 1; + rasterizer->use_framebuffer(*framebuffers[j]); + rasterizer->set_viewport(0, 0, textures[j]->get_width(), textures[j]->get_height()); + + upsample_source_texture_input->upload(textures[i]); + + rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6); + } +} + +void bloom_pass::resize() +{ + unsigned int source_width = 1; + unsigned int source_height = 1; + if (source_texture) + { + // Get source texture dimensions + source_width = source_texture->get_width(); + source_height = source_texture->get_height(); + + // Correct filter radius according to source texture aspect ratio + const float aspect_ratio = static_cast(source_height) / static_cast(source_width); + corrected_filter_radius = {filter_radius * aspect_ratio, filter_radius}; + } + + // Resize mip chain + for (unsigned int i = 0; i < mip_chain_length; ++i) + { + // Calculate mip dimensions + unsigned int mip_width = std::max(1, source_width >> (i + 1)); + unsigned int mip_height = std::max(1, source_height >> (i + 1)); + + // Resize mip texture + textures[i]->resize(mip_width, mip_height, nullptr); + + // Resize mip framebuffer + framebuffers[i]->resize({(int)mip_width, (int)mip_height}); + } +} + +void bloom_pass::set_source_texture(const gl::texture_2d* texture) +{ + if (texture != source_texture) + { + if (texture) + { + if (source_texture) + { + if (texture->get_width() != source_texture->get_width() || texture->get_height() != source_texture->get_height()) + { + source_texture = texture; + resize(); + } + else + { + source_texture = texture; + } + } + else + { + source_texture = texture; + resize(); + } + } + else + { + source_texture = texture; + } + } +} + +void bloom_pass::set_mip_chain_length(unsigned int length) +{ + unsigned int source_width = 1; + unsigned int source_height = 1; + if (source_texture) + { + // Get source texture dimensions + source_width = source_texture->get_width(); + source_height = source_texture->get_height(); + } + + if (length > mip_chain_length) + { + // Generate additional framebuffers + for (unsigned int i = mip_chain_length; i < length; ++i) + { + // Calculate mip resolution + unsigned int mip_width = std::max(1, source_width >> (i + 1)); + unsigned int mip_height = std::max(1, source_height >> (i + 1)); + + // Generate mip texture + gl::texture_2d* texture = new gl::texture_2d(mip_width, mip_height, gl::pixel_type::float_16, gl::pixel_format::rgb); + texture->set_wrapping(gl::texture_wrapping::extend, gl::texture_wrapping::extend); + texture->set_filters(gl::texture_min_filter::linear, gl::texture_mag_filter::linear); + texture->set_max_anisotropy(0.0f); + textures.push_back(texture); + + // Generate mip framebuffer + gl::framebuffer* framebuffer = new gl::framebuffer(mip_width, mip_height); + framebuffer->attach(gl::framebuffer_attachment_type::color, texture); + framebuffers.push_back(framebuffer); + } + } + else if (length < mip_chain_length) + { + // Free excess framebuffers + while (framebuffers.size() > length) + { + delete framebuffers.back(); + framebuffers.pop_back(); + + delete textures.back(); + textures.pop_back(); + } + } + + // Update mip chain length + mip_chain_length = length; +} + +void bloom_pass::set_filter_radius(float radius) +{ + filter_radius = radius; + + // Get aspect ratio of source texture + float aspect_ratio = 1.0f; + if (source_texture) + { + aspect_ratio = static_cast(source_texture->get_height()) / static_cast(source_texture->get_width()); + } + + // Correct filter radius according to source texture aspect ratio + corrected_filter_radius = {filter_radius * aspect_ratio, filter_radius}; +} + +} // namespace render diff --git a/src/engine/render/passes/bloom-pass.hpp b/src/engine/render/passes/bloom-pass.hpp new file mode 100644 index 0000000..44fd932 --- /dev/null +++ b/src/engine/render/passes/bloom-pass.hpp @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_RENDER_BLOOM_PASS_HPP +#define ANTKEEPER_RENDER_BLOOM_PASS_HPP + +#include +#include +#include +#include +#include +#include +#include + +class resource_manager; + +namespace render { + +/** + * Physically-based bloom render pass. + * + * @see Jimenez, J. (2014). Next generation post processing in call of duty advanced warfare. SIGGRAPH Advances in Real-Time Rendering. + * @see https://learnopengl.com/Guest-Articles/2022/Phys.-Based-Bloom + */ +class bloom_pass: public pass +{ +public: + /** + * Constructs a bloom pass. + * + * @param rasterizer Rasterizer. + * @param resource_manager Resource manager. + */ + bloom_pass(gl::rasterizer* rasterizer, resource_manager* resource_manager); + + /** + * Destructs a bloom pass. + */ + virtual ~bloom_pass(); + + /** + * Renders a bloom texture. + * + * @param ctx Render context. + * @param queue Render queue. + */ + virtual void render(const render::context& ctx, render::queue& queue) const final; + + /** + * Resizes the mip chain resolution according to the resolution of the source texture. + */ + void resize(); + + /** + * Sets the bloom source texture. + * + * @param texture Bloom source texture. + */ + void set_source_texture(const gl::texture_2d* texture); + + /** + * Sets the mip chain length. A length of `1` indicates a single stage bloom. + * + * @param length Mip chain length. + */ + void set_mip_chain_length(unsigned int length); + + /** + * Sets the upsample filter radius. + * + * @param radius Upsample filter radius, in texture coordinates. + */ + void set_filter_radius(float radius); + + /** + * Returns the texture containing the bloom result. + */ + const gl::texture_2d* get_bloom_texture() const; + +private: + const gl::texture_2d* source_texture; + + shader_template* downsample_shader_template; + shader_template* upsample_shader_template; + + gl::shader_program* downsample_karis_shader; + const gl::shader_input* downsample_karis_source_texture_input; + + gl::shader_program* downsample_shader; + const gl::shader_input* downsample_source_texture_input; + + gl::shader_program* upsample_shader; + const gl::shader_input* upsample_source_texture_input; + const gl::shader_input* upsample_filter_radius_input; + + gl::vertex_buffer* quad_vbo; + gl::vertex_array* quad_vao; + + unsigned int mip_chain_length; + std::vector framebuffers; + std::vector textures; + float filter_radius; + float2 corrected_filter_radius; +}; + +inline const gl::texture_2d* bloom_pass::get_bloom_texture() const +{ + return textures.empty() ? nullptr : textures.front(); +} + +} // namespace render + +#endif // ANTKEEPER_RENDER_BLOOM_PASS_HPP diff --git a/src/engine/render/passes/clear-pass.cpp b/src/engine/render/passes/clear-pass.cpp new file mode 100644 index 0000000..86f4814 --- /dev/null +++ b/src/engine/render/passes/clear-pass.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include +#include + +namespace render { + +clear_pass::clear_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer): + pass(rasterizer, framebuffer), + clear_color_buffer(false), + clear_depth_buffer(false), + clear_stencil_buffer(false), + clear_color({0.0f, 0.0f, 0.0f, 0.0f}), + clear_depth(1.0f), + clear_stencil(0) +{} + +clear_pass::~clear_pass() +{} + +void clear_pass::render(const render::context& ctx, render::queue& queue) const +{ + if (clear_color_buffer) + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + if (clear_depth_buffer) + glDepthMask(GL_TRUE); + if (clear_stencil_buffer) + glStencilMask(0xFF); + + rasterizer->use_framebuffer(*framebuffer); + + auto viewport = framebuffer->get_dimensions(); + rasterizer->set_viewport(0, 0, std::get<0>(viewport), std::get<1>(viewport)); + + rasterizer->set_clear_color(clear_color[0], clear_color[1], clear_color[2], clear_color[3]); + rasterizer->set_clear_depth(clear_depth); + rasterizer->set_clear_stencil(clear_stencil); + rasterizer->clear_framebuffer(clear_color_buffer, clear_depth_buffer, clear_stencil_buffer); +} + +void clear_pass::set_cleared_buffers(bool color, bool depth, bool stencil) +{ + clear_color_buffer = color; + clear_depth_buffer = depth; + clear_stencil_buffer = stencil; +} + +void clear_pass::set_clear_color(const float4& color) +{ + clear_color = color; +} + +void clear_pass::set_clear_depth(float depth) +{ + clear_depth = depth; +} + +void clear_pass::set_clear_stencil(int stencil) +{ + clear_stencil = stencil; +} + +} // namespace render diff --git a/src/engine/render/passes/clear-pass.hpp b/src/engine/render/passes/clear-pass.hpp new file mode 100644 index 0000000..568a5b7 --- /dev/null +++ b/src/engine/render/passes/clear-pass.hpp @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_RENDER_CLEAR_PASS_HPP +#define ANTKEEPER_RENDER_CLEAR_PASS_HPP + +#include +#include + +namespace render { + +/** + * Clears the color, depth, or stencil buffer of a render target. + */ +class clear_pass: public pass +{ +public: + clear_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer); + virtual ~clear_pass(); + virtual void render(const render::context& ctx, render::queue& queue) const final; + + /** + * Sets the buffers to be cleared. + * + * @param color Clear the color buffer. + * @param depth Clear the depth buffer. + * @param stencil Clear the stencil buffer. + */ + void set_cleared_buffers(bool color, bool depth, bool stencil); + + /** + * Sets color buffer clear color. + * + * @param color Clear color. + */ + void set_clear_color(const float4& color); + + /** + * Sets the depth buffer clear value. + * + * @param depth Clear value. + */ + void set_clear_depth(float depth); + + /** + * Sets the stencil buffer clear value. + * + * @param stencil Clear value. + */ + void set_clear_stencil(int stencil); + +private: + bool clear_color_buffer; + bool clear_depth_buffer; + bool clear_stencil_buffer; + float4 clear_color; + float clear_depth; + int clear_stencil; +}; + +} // namespace render + +#endif // ANTKEEPER_RENDER_CLEAR_PASS_HPP + diff --git a/src/engine/render/passes/final-pass.cpp b/src/engine/render/passes/final-pass.cpp new file mode 100644 index 0000000..e901b78 --- /dev/null +++ b/src/engine/render/passes/final-pass.cpp @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace render { + +final_pass::final_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager): + pass(rasterizer, framebuffer), + color_texture(nullptr), + bloom_texture(nullptr), + bloom_weight(0.04f), + blue_noise_texture(nullptr), + blue_noise_scale(1.0f) +{ + // Load shader template + shader_template = resource_manager->load("final.glsl"); + + // Build shader program + shader_program = shader_template->build(); + color_texture_input = shader_program->get_input("color_texture"); + bloom_texture_input = shader_program->get_input("bloom_texture"); + bloom_weight_input = shader_program->get_input("bloom_weight"); + blue_noise_texture_input = shader_program->get_input("blue_noise_texture"); + blue_noise_scale_input = shader_program->get_input("blue_noise_scale"); + resolution_input = shader_program->get_input("resolution"); + time_input = shader_program->get_input("time"); + + const float vertex_data[] = + { + -1.0f, 1.0f, + -1.0f, -1.0f, + 1.0f, 1.0f, + 1.0f, 1.0f, + -1.0f, -1.0f, + 1.0f, -1.0f + }; + + std::size_t vertex_size = 2; + std::size_t vertex_stride = sizeof(float) * vertex_size; + std::size_t vertex_count = 6; + + quad_vbo = new gl::vertex_buffer(sizeof(float) * vertex_size * vertex_count, vertex_data); + quad_vao = new gl::vertex_array(); + + // Define position vertex attribute + gl::vertex_attribute position_attribute; + position_attribute.buffer = quad_vbo; + position_attribute.offset = 0; + position_attribute.stride = vertex_stride; + position_attribute.type = gl::vertex_attribute_type::float_32; + position_attribute.components = 2; + + // Bind vertex attributes to VAO + quad_vao->bind(render::vertex_attribute::position, position_attribute); +} + +final_pass::~final_pass() +{ + delete quad_vao; + delete quad_vbo; + + delete shader_program; + + /// @TODO + // resource_manager->unload("final.glsl"); +} + +void final_pass::render(const render::context& ctx, render::queue& queue) const +{ + rasterizer->use_framebuffer(*framebuffer); + + glDisable(GL_BLEND); + glDisable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + + auto viewport = framebuffer->get_dimensions(); + rasterizer->set_viewport(0, 0, std::get<0>(viewport), std::get<1>(viewport)); + + float2 resolution = {std::get<0>(viewport), std::get<1>(viewport)}; + + // Change shader program + rasterizer->use_program(*shader_program); + + // Upload shader parameters + color_texture_input->upload(color_texture); + if (bloom_texture && bloom_texture_input) + bloom_texture_input->upload(bloom_texture); + if (bloom_weight_input) + bloom_weight_input->upload(bloom_weight); + if (blue_noise_texture && blue_noise_texture_input) + blue_noise_texture_input->upload(blue_noise_texture); + if (blue_noise_scale_input) + blue_noise_scale_input->upload(blue_noise_scale); + if (resolution_input) + resolution_input->upload(resolution); + if (time_input) + time_input->upload(ctx.t); + + // Draw quad + rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6); +} + +void final_pass::set_color_texture(const gl::texture_2d* texture) +{ + this->color_texture = texture; +} + +void final_pass::set_bloom_texture(const gl::texture_2d* texture) noexcept +{ + this->bloom_texture = texture; +} + +void final_pass::set_bloom_weight(float weight) noexcept +{ + this->bloom_weight = weight; +} + +void final_pass::set_blue_noise_texture(const gl::texture_2d* texture) +{ + this->blue_noise_texture = texture; + blue_noise_scale = 1.0f / static_cast(texture->get_dimensions()[0]); +} + +} // namespace render diff --git a/src/engine/render/passes/final-pass.hpp b/src/engine/render/passes/final-pass.hpp new file mode 100644 index 0000000..40e2913 --- /dev/null +++ b/src/engine/render/passes/final-pass.hpp @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_RENDER_FINAL_PASS_HPP +#define ANTKEEPER_RENDER_FINAL_PASS_HPP + +#include +#include +#include +#include +#include +#include +#include + +class resource_manager; + +namespace render { + +/** + * + */ +class final_pass: public pass +{ +public: + final_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager); + virtual ~final_pass(); + virtual void render(const render::context& ctx, render::queue& queue) const final; + + void set_color_texture(const gl::texture_2d* texture); + void set_bloom_texture(const gl::texture_2d* texture) noexcept; + void set_bloom_weight(float weight) noexcept; + void set_blue_noise_texture(const gl::texture_2d* texture); + +private: + render::shader_template* shader_template; + + gl::shader_program* shader_program; + const gl::shader_input* color_texture_input; + const gl::shader_input* bloom_texture_input; + const gl::shader_input* bloom_weight_input; + const gl::shader_input* blue_noise_texture_input; + const gl::shader_input* blue_noise_scale_input; + const gl::shader_input* resolution_input; + const gl::shader_input* time_input; + gl::vertex_buffer* quad_vbo; + gl::vertex_array* quad_vao; + + const gl::texture_2d* color_texture; + const gl::texture_2d* bloom_texture; + float bloom_weight; + const gl::texture_2d* blue_noise_texture; + float blue_noise_scale; +}; + +} // namespace render + +#endif // ANTKEEPER_RENDER_FINAL_PASS_HPP + diff --git a/src/engine/render/passes/fxaa-pass.cpp b/src/engine/render/passes/fxaa-pass.cpp new file mode 100644 index 0000000..45a267e --- /dev/null +++ b/src/engine/render/passes/fxaa-pass.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace render { + +fxaa_pass::fxaa_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager): + pass(rasterizer, framebuffer), + source_texture(nullptr) +{ + // Load FXAA shader template + shader_template = resource_manager->load("fxaa.glsl"); + + // Build FXAA shader program + shader = shader_template->build(); + source_texture_input = shader->get_input("source_texture"); + texel_size_input = shader->get_input("texel_size"); + + const float vertex_data[] = + { + -1.0f, 1.0f, + -1.0f, -1.0f, + 1.0f, 1.0f, + 1.0f, 1.0f, + -1.0f, -1.0f, + 1.0f, -1.0f + }; + + std::size_t vertex_size = 2; + std::size_t vertex_stride = sizeof(float) * vertex_size; + std::size_t vertex_count = 6; + + quad_vbo = new gl::vertex_buffer(sizeof(float) * vertex_size * vertex_count, vertex_data); + quad_vao = new gl::vertex_array(); + + // Define position vertex attribute + gl::vertex_attribute position_attribute; + position_attribute.buffer = quad_vbo; + position_attribute.offset = 0; + position_attribute.stride = vertex_stride; + position_attribute.type = gl::vertex_attribute_type::float_32; + position_attribute.components = 2; + + // Bind vertex attributes to VAO + quad_vao->bind(render::vertex_attribute::position, position_attribute); +} + +fxaa_pass::~fxaa_pass() +{ + delete quad_vao; + delete quad_vbo; + + delete shader; + + /// @TODO + // resource_manager->unload("fxaa.glsl"); +} + +void fxaa_pass::render(const render::context& ctx, render::queue& queue) const +{ + if (!source_texture) + return; + + // Set rasterizer state + glDisable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + glDisable(GL_BLEND); + + // Render FXAA + rasterizer->use_framebuffer(*framebuffer); + rasterizer->set_viewport(0, 0, framebuffer->get_dimensions()[0], framebuffer->get_dimensions()[1]); + rasterizer->use_program(*shader); + source_texture_input->upload(source_texture); + + if (texel_size_input) + { + const float2 texel_size = 1.0f / float2{static_cast(source_texture->get_width()), static_cast(source_texture->get_height())}; + texel_size_input->upload(texel_size); + } + + rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6); +} + +void fxaa_pass::set_source_texture(const gl::texture_2d* texture) +{ + source_texture = texture; +} + +} // namespace render diff --git a/src/engine/render/passes/fxaa-pass.hpp b/src/engine/render/passes/fxaa-pass.hpp new file mode 100644 index 0000000..dd09471 --- /dev/null +++ b/src/engine/render/passes/fxaa-pass.hpp @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_RENDER_FXAA_PASS_HPP +#define ANTKEEPER_RENDER_FXAA_PASS_HPP + +#include +#include +#include +#include +#include +#include +#include + +class resource_manager; + +namespace render { + +/** + * FXAA render pass. + * + * @see Lottes, T. (2009). Fxaa. White paper, Nvidia, Febuary, 2. + */ +class fxaa_pass: public pass +{ +public: + /** + * Constructs an FXAA pass. + * + * @param rasterizer Rasterizer. + * @param framebuffer Target framebuffer. + * @param resource_manager Resource manager. + */ + fxaa_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager); + + /** + * Destructs an FXAA pass. + */ + virtual ~fxaa_pass(); + + /** + * Renders FXAA. + * + * @param ctx Render context. + * @param queue Render queue. + */ + virtual void render(const render::context& ctx, render::queue& queue) const final; + + /** + * Sets the FXAA source texture. + * + * @param texture FXAA source texture. + */ + void set_source_texture(const gl::texture_2d* texture); + +private: + const gl::texture_2d* source_texture; + + render::shader_template* shader_template; + gl::shader_program* shader; + const gl::shader_input* source_texture_input; + const gl::shader_input* texel_size_input; + + gl::vertex_buffer* quad_vbo; + gl::vertex_array* quad_vao; +}; + +} // namespace render + +#endif // ANTKEEPER_RENDER_FXAA_PASS_HPP diff --git a/src/engine/render/passes/ground-pass.cpp b/src/engine/render/passes/ground-pass.cpp new file mode 100644 index 0000000..a4d8a9f --- /dev/null +++ b/src/engine/render/passes/ground-pass.cpp @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace render { + +ground_pass::ground_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager): + pass(rasterizer, framebuffer), + ground_model(nullptr), + ground_model_vao(nullptr), + ground_material(nullptr), + shader_program(nullptr) +{} + +ground_pass::~ground_pass() +{} + +void ground_pass::render(const render::context& ctx, render::queue& queue) const +{ + if (!ground_model) + return; + + rasterizer->use_framebuffer(*framebuffer); + + glDisable(GL_BLEND); + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_ALWAYS); + glDepthMask(GL_TRUE); + glDepthRange(-1.0f, 1.0f); + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + glDisable(GL_STENCIL_TEST); + glStencilMask(0); + + auto viewport = framebuffer->get_dimensions(); + rasterizer->set_viewport(0, 0, std::get<0>(viewport), std::get<1>(viewport)); + + float2 resolution = {static_cast(std::get<0>(viewport)), static_cast(std::get<1>(viewport))}; + + const scene::camera& camera = *ctx.camera; + float clip_near = camera.get_clip_near_tween().interpolate(ctx.alpha); + float clip_far = camera.get_clip_far_tween().interpolate(ctx.alpha); + float3 model_scale = float3{1.0f, 1.0f, 1.0f} * (clip_near + clip_far) * 0.5f; + float4x4 model = math::scale(math::matrix4::identity(), model_scale); + float4x4 view = float4x4(float3x3(ctx.view)); + float4x4 model_view = view * model; + const float4x4& projection = ctx.projection; + const float4x4& view_projection = ctx.view_projection; + float4x4 model_view_projection = projection * model_view; + + + + float3 ambient_light_color = {0.0f, 0.0f, 0.0f}; + float3 directional_light_color = {0.0f, 0.0f, 0.0f}; + float3 directional_light_direction = {0.0f, 0.0f, 0.0f}; + + // Collect lights + const std::list* lights = ctx.collection->get_objects(scene::light::object_type_id); + for (const scene::object_base* object: *lights) + { + // Skip inactive lights + if (!object->is_active()) + continue; + + const scene::light* light = static_cast(object); + switch (light->get_light_type()) + { + // Add ambient light + case scene::light_type::ambient: + { + // Pre-expose light + ambient_light_color = light->get_scaled_color_tween().interpolate(ctx.alpha) * ctx.exposure; + break; + } + + // Add directional light + case scene::light_type::directional: + { + const scene::directional_light* directional_light = static_cast(light); + + // Pre-expose light + float3 light_color = light->get_scaled_color_tween().interpolate(ctx.alpha) * ctx.exposure; + if (light_color.x() < directional_light_color.x()) + break; + + directional_light_color = light_color; + + directional_light_direction = static_cast(light)->get_direction_tween().interpolate(ctx.alpha); + break; + } + + default: + break; + } + } + + // Draw ground + rasterizer->use_program(*shader_program); + + if (model_view_projection_input) + model_view_projection_input->upload(model_view_projection); + if (view_projection_input) + view_projection_input->upload(view_projection); + if (camera_position_input) + camera_position_input->upload(ctx.camera_transform.translation); + if (directional_light_colors_input) + directional_light_colors_input->upload(0, &directional_light_color, 1); + if (directional_light_directions_input) + directional_light_directions_input->upload(0, &directional_light_direction, 1); + if (ambient_light_colors_input) + ambient_light_colors_input->upload(0, &ambient_light_color, 1); + + ground_material->upload(ctx.alpha); + + + + rasterizer->draw_arrays(*ground_model_vao, ground_model_drawing_mode, ground_model_start_index, ground_model_index_count); +} + +void ground_pass::set_ground_model(const model* model) +{ + ground_model = model; + + if (ground_model) + { + ground_model_vao = model->get_vertex_array(); + + const std::vector& groups = *model->get_groups(); + for (model_group* group: groups) + { + ground_material = group->get_material(); + ground_model_drawing_mode = group->get_drawing_mode(); + ground_model_start_index = group->get_start_index(); + ground_model_index_count = group->get_index_count(); + } + + if (ground_material) + { + shader_program = ground_material->get_shader_program(); + + if (shader_program) + { + model_view_projection_input = shader_program->get_input("model_view_projection"); + view_projection_input = shader_program->get_input("view_projection"); + camera_position_input = shader_program->get_input("camera.position"); + directional_light_colors_input = shader_program->get_input("directional_light_colors"); + directional_light_directions_input = shader_program->get_input("directional_light_directions"); + ambient_light_colors_input = shader_program->get_input("ambient_light_colors"); + } + } + } + else + { + ground_model_vao = nullptr; + } +} + +} // namespace render diff --git a/src/engine/render/passes/ground-pass.hpp b/src/engine/render/passes/ground-pass.hpp new file mode 100644 index 0000000..1dfec08 --- /dev/null +++ b/src/engine/render/passes/ground-pass.hpp @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_RENDER_GROUND_PASS_HPP +#define ANTKEEPER_RENDER_GROUND_PASS_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +class resource_manager; + +namespace render { + +class material; +class model; + +/** + * + */ +class ground_pass: public pass +{ +public: + ground_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager); + virtual ~ground_pass(); + virtual void render(const render::context& ctx, render::queue& queue) const final; + + void set_ground_model(const model* model); + +private: + gl::shader_program* shader_program; + const gl::shader_input* model_view_projection_input; + const gl::shader_input* view_projection_input; + const gl::shader_input* camera_position_input; + const gl::shader_input* directional_light_colors_input; + const gl::shader_input* directional_light_directions_input; + const gl::shader_input* ambient_light_colors_input; + + const model* ground_model; + const material* ground_material; + const gl::vertex_array* ground_model_vao; + gl::drawing_mode ground_model_drawing_mode; + std::size_t ground_model_start_index; + std::size_t ground_model_index_count; +}; + +} // namespace render + +#endif // ANTKEEPER_RENDER_GROUND_PASS_HPP diff --git a/src/engine/render/passes/material-pass.cpp b/src/engine/render/passes/material-pass.cpp new file mode 100644 index 0000000..c39c7c6 --- /dev/null +++ b/src/engine/render/passes/material-pass.cpp @@ -0,0 +1,732 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace render { + +static bool operation_compare(const render::operation& a, const render::operation& b); + +material_pass::material_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager): + pass(rasterizer, framebuffer), + fallback_material(nullptr), + mouse_position({0.0f, 0.0f}) +{ + max_ambient_light_count = MATERIAL_PASS_MAX_AMBIENT_LIGHT_COUNT; + max_point_light_count = MATERIAL_PASS_MAX_POINT_LIGHT_COUNT; + max_directional_light_count = MATERIAL_PASS_MAX_DIRECTIONAL_LIGHT_COUNT; + max_spot_light_count = MATERIAL_PASS_MAX_SPOTLIGHT_COUNT; + + ambient_light_colors = new float3[max_ambient_light_count]; + point_light_colors = new float3[max_point_light_count]; + point_light_positions = new float3[max_point_light_count]; + point_light_attenuations = new float3[max_point_light_count]; + directional_light_colors = new float3[max_directional_light_count]; + directional_light_directions = new float3[max_directional_light_count]; + directional_light_textures = new const gl::texture_2d*[max_directional_light_count]; + directional_light_texture_matrices = new float4x4[max_directional_light_count]; + directional_light_texture_opacities = new float[max_directional_light_count]; + + spot_light_colors = new float3[max_spot_light_count]; + spot_light_positions = new float3[max_spot_light_count]; + spot_light_directions = new float3[max_spot_light_count]; + spot_light_attenuations = new float3[max_spot_light_count]; + spot_light_cutoffs = new float2[max_spot_light_count]; +} + +material_pass::~material_pass() +{ + delete[] ambient_light_colors; + delete[] point_light_colors; + delete[] point_light_positions; + delete[] point_light_attenuations; + delete[] directional_light_colors; + delete[] directional_light_directions; + delete[] directional_light_textures; + delete[] directional_light_texture_matrices; + delete[] directional_light_texture_opacities; + delete[] spot_light_colors; + delete[] spot_light_positions; + delete[] spot_light_directions; + delete[] spot_light_attenuations; + delete[] spot_light_cutoffs; +} + +void material_pass::render(const render::context& ctx, render::queue& queue) const +{ + rasterizer->use_framebuffer(*framebuffer); + + glDisable(GL_BLEND); + glEnable(GL_DEPTH_TEST); + glDepthMask(GL_TRUE); + glDepthFunc(GL_GREATER); + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + glDisable(GL_STENCIL_TEST); + glStencilMask(0x00); + + // For half-z buffer + glDepthRange(-1.0f, 1.0f); + + auto viewport = framebuffer->get_dimensions(); + rasterizer->set_viewport(0, 0, std::get<0>(viewport), std::get<1>(viewport)); + + float2 resolution = {static_cast(std::get<0>(viewport)), static_cast(std::get<1>(viewport))}; + + const float3& camera_position = ctx.camera_transform.translation; + const float4x4& view = ctx.view; + const float4x4& projection = ctx.projection; + const float4x4& view_projection = ctx.view_projection; + float4x4 model_view_projection; + float4x4 model; + float4x4 model_view; + float3x3 normal_model; + float3x3 normal_model_view; + float2 clip_depth; + clip_depth[0] = ctx.camera->get_clip_near_tween().interpolate(ctx.alpha); + clip_depth[1] = ctx.camera->get_clip_far_tween().interpolate(ctx.alpha); + float log_depth_coef = 2.0f / std::log2(clip_depth[1] + 1.0f); + + int active_material_flags = 0; + const gl::shader_program* active_shader_program = nullptr; + const render::material* active_material = nullptr; + const parameter_set* parameters = nullptr; + blend_mode active_blend_mode = blend_mode::opaque; + bool active_two_sided = false; + + // Reset light counts + ambient_light_count = 0; + point_light_count = 0; + directional_light_count = 0; + spot_light_count = 0; + const gl::texture_2d* shadow_map_texture = nullptr; + unsigned int shadow_cascade_count = 0; + const float* shadow_splits_directional = nullptr; + const float4x4* shadow_matrices_directional = nullptr; + float shadow_bias_directional = 0.0f; + + // Collect lights + const std::list* lights = ctx.collection->get_objects(scene::light::object_type_id); + for (const scene::object_base* object: *lights) + { + // Skip inactive lights + if (!object->is_active()) + continue; + + const scene::light* light = static_cast(object); + switch (light->get_light_type()) + { + // Add ambient light + case scene::light_type::ambient: + { + if (ambient_light_count < max_ambient_light_count) + { + // Pre-expose light + ambient_light_colors[ambient_light_count] = light->get_scaled_color_tween().interpolate(ctx.alpha) * ctx.exposure; + ++ambient_light_count; + } + break; + } + + // Add point light + case scene::light_type::point: + { + if (point_light_count < max_point_light_count) + { + // Pre-expose light + point_light_colors[point_light_count] = light->get_scaled_color_tween().interpolate(ctx.alpha) * ctx.exposure; + + float3 position = light->get_transform_tween().interpolate(ctx.alpha).translation; + point_light_positions[point_light_count] = position; + + point_light_attenuations[point_light_count] = static_cast(light)->get_attenuation_tween().interpolate(ctx.alpha); + ++point_light_count; + } + break; + } + + // Add directional light + case scene::light_type::directional: + { + if (directional_light_count < max_directional_light_count) + { + const scene::directional_light* directional_light = static_cast(light); + + // Pre-expose light + directional_light_colors[directional_light_count] = light->get_scaled_color_tween().interpolate(ctx.alpha) * ctx.exposure; + + float3 direction = static_cast(light)->get_direction_tween().interpolate(ctx.alpha); + directional_light_directions[directional_light_count] = direction; + + if (directional_light->is_shadow_caster()) + { + if (directional_light->get_shadow_framebuffer()) + shadow_map_texture = directional_light->get_shadow_framebuffer()->get_depth_attachment(); + shadow_bias_directional = directional_light->get_shadow_bias(); + shadow_cascade_count = directional_light->get_shadow_cascade_count(); + shadow_splits_directional = directional_light->get_shadow_cascade_distances(); + shadow_matrices_directional = directional_light->get_shadow_cascade_matrices(); + } + + if (directional_light->get_light_texture()) + { + directional_light_textures[directional_light_count] = directional_light->get_light_texture(); + directional_light_texture_opacities[directional_light_count] = directional_light->get_light_texture_opacity_tween().interpolate(ctx.alpha); + + math::transform light_transform = light->get_transform_tween().interpolate(ctx.alpha); + float3 forward = light_transform.rotation * config::global_forward; + float3 up = light_transform.rotation * config::global_up; + float4x4 light_view = math::look_at(light_transform.translation, light_transform.translation + forward, up); + + float2 scale = directional_light->get_light_texture_scale_tween().interpolate(ctx.alpha); + float4x4 light_projection = math::ortho(-scale.x(), scale.x(), -scale.y(), scale.y(), -1.0f, 1.0f); + + directional_light_texture_matrices[directional_light_count] = light_projection * light_view; + } + else + { + directional_light_textures[directional_light_count] = nullptr; + directional_light_texture_opacities[directional_light_count] = 0.0f; + } + + ++directional_light_count; + } + break; + } + + // Add spot_light + case scene::light_type::spot: + { + if (spot_light_count < max_spot_light_count) + { + const scene::spot_light* spot_light = static_cast(light); + + // Pre-expose light + spot_light_colors[spot_light_count] = light->get_scaled_color_tween().interpolate(ctx.alpha) * ctx.exposure; + + float3 position = light->get_transform_tween().interpolate(ctx.alpha).translation; + spot_light_positions[spot_light_count] = position; + + float3 direction = spot_light->get_direction_tween().interpolate(ctx.alpha); + spot_light_directions[spot_light_count] = direction; + + spot_light_attenuations[spot_light_count] = spot_light->get_attenuation_tween().interpolate(ctx.alpha); + spot_light_cutoffs[spot_light_count] = spot_light->get_cosine_cutoff_tween().interpolate(ctx.alpha); + + ++spot_light_count; + } + break; + } + + default: + break; + } + } + + // Sort render queue + queue.sort(operation_compare); + + for (const render::operation& operation: queue) + { + // Get operation material + const render::material* material = operation.material; + if (!material) + { + if (fallback_material) + { + // No material specified, use fallback material + material = fallback_material; + } + else + { + // No material specified and no fallback material, skip operation + continue; + } + } + + // Switch materials if necessary + if (active_material != material) + { + active_material = material; + + // Set blend mode + const blend_mode material_blend_mode = active_material->get_blend_mode(); + if (material_blend_mode != active_blend_mode) + { + if (material_blend_mode == blend_mode::translucent) + { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + else if (active_blend_mode == blend_mode::translucent && (material_blend_mode == blend_mode::opaque || material_blend_mode == blend_mode::masked)) + { + glDisable(GL_BLEND); + } + + active_blend_mode = material_blend_mode; + } + + // Set back-face culling mode + const bool material_two_sided = active_material->is_two_sided(); + if (material_two_sided != active_two_sided) + { + if (material_two_sided) + { + glDisable(GL_CULL_FACE); + } + else + { + glEnable(GL_CULL_FACE); + } + + active_two_sided = material_two_sided; + } + + // Change rasterizer state according to material flags + std::uint32_t material_flags = active_material->get_flags(); + if (active_material_flags != material_flags) + { + if ((material_flags & MATERIAL_FLAG_X_RAY) != (active_material_flags & MATERIAL_FLAG_X_RAY)) + { + if (material_flags & MATERIAL_FLAG_X_RAY) + { + glDisable(GL_DEPTH_TEST); + + } + else + { + glEnable(GL_DEPTH_TEST); + } + } + + if ((material_flags & MATERIAL_FLAG_DECAL_SURFACE) != (active_material_flags & MATERIAL_FLAG_DECAL_SURFACE)) + { + if (material_flags & MATERIAL_FLAG_DECAL_SURFACE) + { + glEnable(GL_STENCIL_TEST); + glStencilFunc(GL_ALWAYS, 1, ~0); + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + glStencilMask(~0); + } + else + { + glDisable(GL_STENCIL_TEST); + glStencilMask(0); + } + } + + if ((material_flags & MATERIAL_FLAG_DECAL) != (active_material_flags & MATERIAL_FLAG_DECAL)) + { + if (material_flags & MATERIAL_FLAG_DECAL) + { + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_GEQUAL); + glDepthMask(GL_FALSE); + + glEnable(GL_STENCIL_TEST); + glStencilFunc(GL_EQUAL, 1, ~0); + //glStencilOp(GL_KEEP, GL_KEEP, GL_ZERO); + //glStencilMask(~0); + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + glStencilMask(0); + } + else + { + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_GREATER); + glDepthMask(GL_TRUE); + glDisable(GL_STENCIL_TEST); + glStencilMask(0); + } + } + + /* + if ((material_flags & MATERIAL_FLAG_OUTLINE) != (active_material_flags & MATERIAL_FLAG_OUTLINE)) + { + if (material_flags & MATERIAL_FLAG_OUTLINE) + { + glEnable(GL_STENCIL_TEST); + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + glStencilFunc(GL_ALWAYS, 2, 0xFF); + glStencilMask(0xFF); + } + else + { + glDisable(GL_STENCIL_TEST); + glStencilMask(0x00); + } + } + */ + + active_material_flags = material_flags; + } + + // Switch shaders if necessary + const gl::shader_program* shader_program = active_material->get_shader_program(); + if (active_shader_program != shader_program) + { + active_shader_program = shader_program; + if (!active_shader_program) + { + continue; + } + + // Change shader program + rasterizer->use_program(*active_shader_program); + + // Get set of known shader input parameters + if (auto it = parameter_sets.find(active_shader_program); it != parameter_sets.end()) + { + parameters = it->second; + } + else + { + parameters = load_parameter_set(active_shader_program); + } + + // Upload context-dependent shader parameters + if (parameters->time) + parameters->time->upload(ctx.t); + if (parameters->mouse) + parameters->mouse->upload(mouse_position); + if (parameters->resolution) + parameters->resolution->upload(resolution); + if (parameters->camera_position) + parameters->camera_position->upload(camera_position); + if (parameters->camera_exposure) + parameters->camera_exposure->upload(ctx.exposure); + if (parameters->view) + parameters->view->upload(view); + if (parameters->view_projection) + parameters->view_projection->upload(view_projection); + if (parameters->ambient_light_count) + parameters->ambient_light_count->upload(ambient_light_count); + if (parameters->ambient_light_colors) + parameters->ambient_light_colors->upload(0, ambient_light_colors, ambient_light_count); + if (parameters->point_light_count) + parameters->point_light_count->upload(point_light_count); + if (parameters->point_light_colors) + parameters->point_light_colors->upload(0, point_light_colors, point_light_count); + if (parameters->point_light_positions) + parameters->point_light_positions->upload(0, point_light_positions, point_light_count); + if (parameters->point_light_attenuations) + parameters->point_light_attenuations->upload(0, point_light_attenuations, point_light_count); + if (parameters->directional_light_count) + parameters->directional_light_count->upload(directional_light_count); + if (parameters->directional_light_colors) + parameters->directional_light_colors->upload(0, directional_light_colors, directional_light_count); + if (parameters->directional_light_directions) + parameters->directional_light_directions->upload(0, directional_light_directions, directional_light_count); + + if (parameters->directional_light_textures) + parameters->directional_light_textures->upload(0, directional_light_textures, directional_light_count); + if (parameters->directional_light_texture_matrices) + parameters->directional_light_texture_matrices->upload(0, directional_light_texture_matrices, directional_light_count); + if (parameters->directional_light_texture_opacities) + parameters->directional_light_texture_opacities->upload(0, directional_light_texture_opacities, directional_light_count); + + if (parameters->shadow_map_directional && shadow_map_texture) + parameters->shadow_map_directional->upload(shadow_map_texture); + if (parameters->shadow_bias_directional) + parameters->shadow_bias_directional->upload(shadow_bias_directional); + if (parameters->shadow_matrices_directional) + parameters->shadow_matrices_directional->upload(0, shadow_matrices_directional, shadow_cascade_count); + if (parameters->shadow_splits_directional) + parameters->shadow_splits_directional->upload(0, shadow_splits_directional, shadow_cascade_count); + + if (parameters->spot_light_count) + parameters->spot_light_count->upload(spot_light_count); + if (parameters->spot_light_colors) + parameters->spot_light_colors->upload(0, spot_light_colors, spot_light_count); + if (parameters->spot_light_positions) + parameters->spot_light_positions->upload(0, spot_light_positions, spot_light_count); + if (parameters->spot_light_directions) + parameters->spot_light_directions->upload(0, spot_light_directions, spot_light_count); + if (parameters->spot_light_attenuations) + parameters->spot_light_attenuations->upload(0, spot_light_attenuations, spot_light_count); + if (parameters->spot_light_cutoffs) + parameters->spot_light_cutoffs->upload(0, spot_light_cutoffs, spot_light_count); + } + + // Upload material properties to shader + active_material->upload(ctx.alpha); + } + + // Calculate operation-dependent parameters + model = operation.transform; + model_view_projection = view_projection * model; + model_view = view * model; + normal_model = math::transpose(math::inverse(math::matrix(model))); + normal_model_view = math::transpose(math::inverse(math::matrix(model_view))); + + // Skinning palette + if (operation.bone_count && parameters->skinning_palette) + { + parameters->skinning_palette->upload(0, operation.skinning_palette, operation.bone_count); + } + + // Upload operation-dependent parameters + if (parameters->model) + parameters->model->upload(model); + if (parameters->model_view) + parameters->model_view->upload(model_view); + if (parameters->model_view_projection) + parameters->model_view_projection->upload(model_view_projection); + if (parameters->normal_model) + parameters->normal_model->upload(normal_model); + if (parameters->normal_model_view) + parameters->normal_model_view->upload(normal_model_view); + if (parameters->clip_depth) + parameters->clip_depth->upload(clip_depth); + if (parameters->log_depth_coef) + parameters->log_depth_coef->upload(log_depth_coef); + + // Draw geometry + if (operation.instance_count) + rasterizer->draw_arrays_instanced(*operation.vertex_array, operation.drawing_mode, operation.start_index, operation.index_count, operation.instance_count); + else + rasterizer->draw_arrays(*operation.vertex_array, operation.drawing_mode, operation.start_index, operation.index_count); + } +} + +void material_pass::set_fallback_material(const material* fallback) +{ + this->fallback_material = fallback; +} + +const material_pass::parameter_set* material_pass::load_parameter_set(const gl::shader_program* program) const +{ + // Allocate a new parameter set + parameter_set* parameters = new parameter_set(); + + // Connect inputs + parameters->time = program->get_input("time"); + parameters->mouse = program->get_input("mouse"); + parameters->resolution = program->get_input("resolution"); + parameters->camera_position = program->get_input("camera.position"); + parameters->camera_exposure = program->get_input("camera.exposure"); + parameters->model = program->get_input("model"); + parameters->view = program->get_input("view"); + parameters->projection = program->get_input("projection"); + parameters->model_view = program->get_input("model_view"); + parameters->view_projection = program->get_input("view_projection"); + parameters->model_view_projection = program->get_input("model_view_projection"); + parameters->normal_model = program->get_input("normal_model"); + parameters->normal_model_view = program->get_input("normal_model_view"); + parameters->clip_depth = program->get_input("clip_depth"); + parameters->log_depth_coef = program->get_input("log_depth_coef"); + parameters->ambient_light_count = program->get_input("ambient_light_count"); + parameters->ambient_light_colors = program->get_input("ambient_light_colors"); + parameters->point_light_count = program->get_input("point_light_count"); + parameters->point_light_colors = program->get_input("point_light_colors"); + parameters->point_light_positions = program->get_input("point_light_positions"); + parameters->point_light_attenuations = program->get_input("point_light_attenuations"); + parameters->directional_light_count = program->get_input("directional_light_count"); + parameters->directional_light_colors = program->get_input("directional_light_colors"); + parameters->directional_light_directions = program->get_input("directional_light_directions"); + parameters->directional_light_textures = program->get_input("directional_light_textures"); + parameters->directional_light_texture_matrices = program->get_input("directional_light_texture_matrices"); + parameters->directional_light_texture_opacities = program->get_input("directional_light_texture_opacities"); + parameters->spot_light_count = program->get_input("spot_light_count"); + parameters->spot_light_colors = program->get_input("spot_light_colors"); + parameters->spot_light_positions = program->get_input("spot_light_positions"); + parameters->spot_light_directions = program->get_input("spot_light_directions"); + parameters->spot_light_attenuations = program->get_input("spot_light_attenuations"); + parameters->spot_light_cutoffs = program->get_input("spot_light_cutoffs"); + parameters->shadow_map_directional = program->get_input("shadow_map_directional"); + parameters->shadow_bias_directional = program->get_input("shadow_bias_directional"); + parameters->shadow_splits_directional = program->get_input("shadow_splits_directional"); + parameters->shadow_matrices_directional = program->get_input("shadow_matrices_directional"); + parameters->skinning_palette = program->get_input("skinning_palette"); + + // Add parameter set to map of parameter sets + parameter_sets[program] = parameters; + + return parameters; +} + +bool operation_compare(const render::operation& a, const render::operation& b) +{ + if (!a.material) + return false; + else if (!b.material) + return true; + + bool xray_a = a.material->get_flags() & MATERIAL_FLAG_X_RAY; + bool xray_b = b.material->get_flags() & MATERIAL_FLAG_X_RAY; + + const bool two_sided_a = (a.material) ? a.material->is_two_sided() : false; + const bool two_sided_b = (b.material) ? b.material->is_two_sided() : false; + + if (xray_a) + { + if (xray_b) + { + // A and B are both xray, render back to front + return (a.depth > b.depth); + } + else + { + // A is xray, B is not. Render B first + return false; + } + } + else + { + if (xray_b) + { + // A is opaque, B is xray. Render A first + return true; + } + else + { + // Determine transparency + bool transparent_a = a.material->get_blend_mode() == blend_mode::translucent; + bool transparent_b = b.material->get_blend_mode() == blend_mode::translucent; + + if (transparent_a) + { + if (transparent_b) + { + // Determine decal status + bool decal_a = a.material->get_flags() & MATERIAL_FLAG_DECAL; + bool decal_b = b.material->get_flags() & MATERIAL_FLAG_DECAL; + + if (decal_a) + { + if (decal_b) + { + // A and B are both transparent decals, render back to front + return (a.depth > b.depth); + } + else + { + // A is a transparent decal, B is transparent but not a decal, render A first + return true; + } + } + else + { + if (decal_b) + { + // A is transparent but not a decal, B is a transparent decal, render B first + return false; + } + else + { + // A and B are both transparent, but not decals, render back to front + return (a.depth < b.depth); + } + } + } + else + { + // A is transparent, B is opaque. Render B first + return false; + } + } + else + { + if (transparent_b) + { + // A is opaque, B is transparent. Render A first + return true; + } + else + { + // A and B are both opaque + if (a.material->get_shader_program() == b.material->get_shader_program()) + { + // A and B have the same shader + if (a.vertex_array == b.vertex_array) + { + // A and B have the same VAO, render front to back + return (a.depth < b.depth); + } + else + { + // A and B have different VAOs, sort by two-sided + if (two_sided_a) + { + if (two_sided_b) + { + // A and B are both two-sided, sort by VAO + return (a.vertex_array < b.vertex_array); + } + else + { + // A is two-sided, B is one-sided. Render B first + return false; + } + } + else + { + if (two_sided_b) + { + // A is one-sided, B is two-sided. Render A first + return true; + } + else + { + // A and B are both one-sided, sort by VAO + return (a.vertex_array < b.vertex_array); + } + } + } + } + else + { + // A and B are both opaque and have different shaders, sort by shader + return (a.material->get_shader_program() < b.material->get_shader_program()); + } + } + } + } + } +} + +} // namespace render diff --git a/src/engine/render/passes/material-pass.hpp b/src/engine/render/passes/material-pass.hpp new file mode 100644 index 0000000..1e3936e --- /dev/null +++ b/src/engine/render/passes/material-pass.hpp @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_RENDER_MATERIAL_PASS_HPP +#define ANTKEEPER_RENDER_MATERIAL_PASS_HPP + +#include +#include +#include +#include +#include +#include +#include + +class resource_manager; + +namespace render { + +/** + * Renders scene objects using their material-specified shaders and properties. + */ +class material_pass: public pass +{ +public: + material_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager); + virtual ~material_pass(); + virtual void render(const render::context& ctx, render::queue& queue) const final; + + /// Sets the material to be used when a render operation is missing a material. If no fallback material is specified, render operations without materials will not be processed. + void set_fallback_material(const material* fallback); + +private: + /** + * Sets of known shader input parameters. Each time a new shader is encountered, a parameter set will be created and its inputs connected to the shader program. A null input indiciates that the shader doesn't have that parameter. + */ + struct parameter_set + { + const gl::shader_input* time; + const gl::shader_input* mouse; + const gl::shader_input* resolution; + const gl::shader_input* camera_position; + const gl::shader_input* camera_exposure; + const gl::shader_input* model; + const gl::shader_input* view; + const gl::shader_input* projection; + const gl::shader_input* model_view; + const gl::shader_input* view_projection; + const gl::shader_input* model_view_projection; + const gl::shader_input* normal_model; + const gl::shader_input* normal_model_view; + const gl::shader_input* clip_depth; + const gl::shader_input* log_depth_coef; + + const gl::shader_input* ambient_light_count; + const gl::shader_input* ambient_light_colors; + const gl::shader_input* point_light_count; + const gl::shader_input* point_light_colors; + const gl::shader_input* point_light_positions; + const gl::shader_input* point_light_attenuations; + const gl::shader_input* directional_light_count; + const gl::shader_input* directional_light_colors; + const gl::shader_input* directional_light_directions; + const gl::shader_input* directional_light_textures; + const gl::shader_input* directional_light_texture_matrices; + const gl::shader_input* directional_light_texture_opacities; + const gl::shader_input* spot_light_count; + const gl::shader_input* spot_light_colors; + const gl::shader_input* spot_light_positions; + const gl::shader_input* spot_light_directions; + const gl::shader_input* spot_light_attenuations; + const gl::shader_input* spot_light_cutoffs; + + const gl::shader_input* shadow_map_directional; + const gl::shader_input* shadow_bias_directional; + const gl::shader_input* shadow_splits_directional; + const gl::shader_input* shadow_matrices_directional; + + const gl::shader_input* skinning_palette; + }; + + const parameter_set* load_parameter_set(const gl::shader_program* program) const; + + mutable std::unordered_map parameter_sets; + const material* fallback_material; + float2 mouse_position; + + int max_ambient_light_count; + int max_point_light_count; + int max_directional_light_count; + int max_spot_light_count; + int max_bone_count; + + mutable int ambient_light_count; + mutable int point_light_count; + mutable int directional_light_count; + mutable int spot_light_count; + + float3* ambient_light_colors; + float3* point_light_colors; + float3* point_light_positions; + float3* point_light_attenuations; + float3* directional_light_colors; + float3* directional_light_directions; + const gl::texture_2d** directional_light_textures; + float4x4* directional_light_texture_matrices; + float* directional_light_texture_opacities; + float3* spot_light_colors; + float3* spot_light_positions; + float3* spot_light_directions; + float3* spot_light_attenuations; + float2* spot_light_cutoffs; +}; + +} // namespace render + +#endif // ANTKEEPER_RENDER_MATERIAL_PASS_HPP diff --git a/src/engine/render/passes/outline-pass.cpp b/src/engine/render/passes/outline-pass.cpp new file mode 100644 index 0000000..18967b9 --- /dev/null +++ b/src/engine/render/passes/outline-pass.cpp @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace render { + +outline_pass::outline_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager): + pass(rasterizer, framebuffer), + fill_shader(nullptr), + stroke_shader(nullptr) +{ + // Load fill shader + fill_shader = resource_manager->load("outline-fill-unskinned.glsl"); + fill_model_view_projection_input = fill_shader->get_input("model_view_projection"); + + // Load stroke shader + stroke_shader = resource_manager->load("outline-stroke-unskinned.glsl"); + stroke_model_view_projection_input = stroke_shader->get_input("model_view_projection"); + stroke_width_input = stroke_shader->get_input("width"); + stroke_color_input = stroke_shader->get_input("color"); +} + +outline_pass::~outline_pass() +{} + +void outline_pass::render(const render::context& ctx, render::queue& queue) const +{ + rasterizer->use_framebuffer(*framebuffer); + + // Determine viewport based on framebuffer resolution + auto viewport = framebuffer->get_dimensions(); + rasterizer->set_viewport(0, 0, std::get<0>(viewport), std::get<1>(viewport)); + + // Get camera matrices + float4x4 view = ctx.camera->get_view_tween().interpolate(ctx.alpha); + float4x4 view_projection = ctx.camera->get_view_projection_tween().interpolate(ctx.alpha); + + float4x4 model_view_projection; + + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + glDisable(GL_DEPTH_TEST); + glEnable(GL_STENCIL_TEST); + + // Render fill + { + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + glStencilFunc(GL_ALWAYS, 2, 0xFF); + glStencilMask(0xFF); + glDisable(GL_BLEND); + + // Setup fill shader + rasterizer->use_program(*fill_shader); + + // Render fills + for (const render::operation& operation: queue) + { + const render::material* material = operation.material; + if (!material || !(material->get_flags() & MATERIAL_FLAG_OUTLINE)) + continue; + + model_view_projection = view_projection * operation.transform; + fill_model_view_projection_input->upload(model_view_projection); + + rasterizer->draw_arrays(*operation.vertex_array, operation.drawing_mode, operation.start_index, operation.index_count); + } + } + + // Render stroke + { + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + + if (outline_color[3] < 1.0f) + { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + else + { + glDisable(GL_BLEND); + } + + glStencilFunc(GL_NOTEQUAL, 2, 0xFF); + glStencilMask(0x00); + + // Setup stroke shader + rasterizer->use_program(*stroke_shader); + stroke_width_input->upload(outline_width); + stroke_color_input->upload(outline_color); + + // Render strokes + for (const render::operation& operation: queue) + { + const render::material* material = operation.material; + if (!material || !(material->get_flags() & MATERIAL_FLAG_OUTLINE)) + continue; + + model_view_projection = view_projection * operation.transform; + stroke_model_view_projection_input->upload(model_view_projection); + + rasterizer->draw_arrays(*operation.vertex_array, operation.drawing_mode, operation.start_index, operation.index_count); + } + } + + glDisable(GL_STENCIL_TEST); +} + +void outline_pass::set_outline_width(float width) +{ + outline_width = width; +} + +void outline_pass::set_outline_color(const float4& color) +{ + outline_color = color; +} + +} // namespace render diff --git a/src/engine/render/passes/outline-pass.hpp b/src/engine/render/passes/outline-pass.hpp new file mode 100644 index 0000000..b7393b5 --- /dev/null +++ b/src/engine/render/passes/outline-pass.hpp @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_RENDER_OUTLINE_PASS_HPP +#define ANTKEEPER_RENDER_OUTLINE_PASS_HPP + +#include +#include +#include +#include + +class resource_manager; + +namespace render { + +/** + * + */ +class outline_pass: public pass +{ +public: + outline_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager); + virtual ~outline_pass(); + virtual void render(const render::context& ctx, render::queue& queue) const final; + + void set_outline_width(float width); + void set_outline_color(const float4& color); + +private: + gl::shader_program* fill_shader; + const gl::shader_input* fill_model_view_projection_input; + + gl::shader_program* stroke_shader; + const gl::shader_input* stroke_model_view_projection_input; + const gl::shader_input* stroke_width_input; + const gl::shader_input* stroke_color_input; + + float outline_width; + float4 outline_color; +}; + +} // namespace render + +#endif // ANTKEEPER_RENDER_OUTLINE_PASS_HPP diff --git a/src/engine/render/passes/resample-pass.cpp b/src/engine/render/passes/resample-pass.cpp new file mode 100644 index 0000000..9746ef4 --- /dev/null +++ b/src/engine/render/passes/resample-pass.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace render { + +resample_pass::resample_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager): + pass(rasterizer, framebuffer), + source_texture(nullptr) +{ + // Load resample shader template + shader_template = resource_manager->load("resample.glsl"); + + // Build resample shader program + shader = shader_template->build(); + source_texture_input = shader->get_input("source_texture"); + + const float vertex_data[] = + { + -1.0f, 1.0f, + -1.0f, -1.0f, + 1.0f, 1.0f, + 1.0f, 1.0f, + -1.0f, -1.0f, + 1.0f, -1.0f + }; + + std::size_t vertex_size = 2; + std::size_t vertex_stride = sizeof(float) * vertex_size; + std::size_t vertex_count = 6; + + quad_vbo = new gl::vertex_buffer(sizeof(float) * vertex_size * vertex_count, vertex_data); + quad_vao = new gl::vertex_array(); + + // Define position vertex attribute + gl::vertex_attribute position_attribute; + position_attribute.buffer = quad_vbo; + position_attribute.offset = 0; + position_attribute.stride = vertex_stride; + position_attribute.type = gl::vertex_attribute_type::float_32; + position_attribute.components = 2; + + // Bind vertex attributes to VAO + quad_vao->bind(render::vertex_attribute::position, position_attribute); +} + +resample_pass::~resample_pass() +{ + delete quad_vao; + delete quad_vbo; + + delete shader; + + /// @TODO + // resource_manager->unload("resample.glsl"); +} + +void resample_pass::render(const render::context& ctx, render::queue& queue) const +{ + if (!source_texture) + return; + + // Set rasterizer state + glDisable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + glDisable(GL_BLEND); + + // Render FXAA + rasterizer->use_framebuffer(*framebuffer); + rasterizer->set_viewport(0, 0, framebuffer->get_dimensions()[0], framebuffer->get_dimensions()[1]); + rasterizer->use_program(*shader); + source_texture_input->upload(source_texture); + rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6); +} + +void resample_pass::set_source_texture(const gl::texture_2d* texture) +{ + source_texture = texture; +} + +} // namespace render diff --git a/src/engine/render/passes/resample-pass.hpp b/src/engine/render/passes/resample-pass.hpp new file mode 100644 index 0000000..b816df6 --- /dev/null +++ b/src/engine/render/passes/resample-pass.hpp @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_RENDER_RESAMPLE_PASS_HPP +#define ANTKEEPER_RENDER_RESAMPLE_PASS_HPP + +#include +#include +#include +#include +#include +#include +#include + +class resource_manager; + +namespace render { + +/** + * Resamples a texture. + */ +class resample_pass: public pass +{ +public: + /** + * Constructs a resample pass. + * + * @param rasterizer Rasterizer. + * @param framebuffer Target framebuffer. + * @param resource_manager Resource manager. + */ + resample_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager); + + /** + * Destructs a resample pass. + */ + virtual ~resample_pass(); + + /** + * Resamples a texture. + * + * @param ctx Render context. + * @param queue Render queue. + */ + virtual void render(const render::context& ctx, render::queue& queue) const final; + + /** + * Sets the resample source texture. + * + * @param texture Texture to resample. + */ + void set_source_texture(const gl::texture_2d* texture); + +private: + const gl::texture_2d* source_texture; + + render::shader_template* shader_template; + gl::shader_program* shader; + const gl::shader_input* source_texture_input; + + gl::vertex_buffer* quad_vbo; + gl::vertex_array* quad_vao; +}; + +} // namespace render + +#endif // ANTKEEPER_RENDER_RESAMPLE_PASS_HPP diff --git a/src/engine/render/passes/shadow-map-pass.cpp b/src/engine/render/passes/shadow-map-pass.cpp new file mode 100644 index 0000000..021108e --- /dev/null +++ b/src/engine/render/passes/shadow-map-pass.cpp @@ -0,0 +1,368 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace render { + +static bool operation_compare(const render::operation& a, const render::operation& b); + +shadow_map_pass::shadow_map_pass(gl::rasterizer* rasterizer, resource_manager* resource_manager): + pass(rasterizer, nullptr) +{ + // Load skinned shader program + unskinned_shader_program = resource_manager->load("depth-unskinned.glsl"); + unskinned_model_view_projection_input = unskinned_shader_program->get_input("model_view_projection"); + + // Load unskinned shader program + skinned_shader_program = resource_manager->load("depth-skinned.glsl"); + skinned_model_view_projection_input = skinned_shader_program->get_input("model_view_projection"); + + // Calculate bias-tile matrices + float4x4 bias_matrix = math::translate(math::matrix4::identity(), float3{0.5f, 0.5f, 0.5f}) * math::scale(math::matrix4::identity(), float3{0.5f, 0.5f, 0.5f}); + float4x4 tile_scale = math::scale(math::matrix4::identity(), float3{0.5f, 0.5f, 1.0f}); + for (int i = 0; i < 4; ++i) + { + float x = static_cast(i % 2) * 0.5f; + float y = static_cast(i / 2) * 0.5f; + float4x4 tile_matrix = math::translate(math::matrix4::identity(), float3{x, y, 0.0f}) * tile_scale; + bias_tile_matrices[i] = tile_matrix * bias_matrix; + } +} + +shadow_map_pass::~shadow_map_pass() +{} + +void shadow_map_pass::render(const render::context& ctx, render::queue& queue) const +{ + // Collect lights + const std::list* lights = ctx.collection->get_objects(scene::light::object_type_id); + for (const scene::object_base* object: *lights) + { + // Ignore inactive lights + if (!object->is_active()) + continue; + + // Ignore non-directional lights + const scene::light* light = static_cast(object); + if (light->get_light_type() != scene::light_type::directional) + continue; + + // Ignore non-shadow casters + const scene::directional_light* directional_light = static_cast(light); + if (!directional_light->is_shadow_caster()) + continue; + + // Ignore improperly-configured lights + if (!directional_light->get_shadow_cascade_count() || !directional_light->get_shadow_framebuffer()) + continue; + + // Render cascaded shadow maps for light + render_csm(*directional_light, ctx, queue); + } +} + +void shadow_map_pass::render_csm(const scene::directional_light& light, const render::context& ctx, render::queue& queue) const +{ + rasterizer->use_framebuffer(*light.get_shadow_framebuffer()); + + // Disable blending + glDisable(GL_BLEND); + + // Enable depth testing + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LESS); + glDepthMask(GL_TRUE); + + // Enable back-face culling + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + bool two_sided = false; + + // For half-z buffer + glDepthRange(-1.0f, 1.0f); + + // Get camera + const scene::camera& camera = *ctx.camera; + + // Get tweened camera parameters + const float camera_fov = camera.get_fov_tween().interpolate(ctx.alpha); + const float camera_aspect_ratio = camera.get_aspect_ratio_tween().interpolate(ctx.alpha); + const float camera_clip_near = camera.get_clip_near_tween().interpolate(ctx.alpha); + const float camera_clip_far = camera.get_clip_far_tween().interpolate(ctx.alpha); + + // Calculate distance to shadow cascade depth clipping planes + const float shadow_clip_far = math::lerp(camera_clip_near, camera_clip_far, light.get_shadow_cascade_coverage()); + + const unsigned int cascade_count = light.get_shadow_cascade_count(); + float* cascade_distances = light.get_shadow_cascade_distances(); + float4x4* cascade_matrices = light.get_shadow_cascade_matrices(); + + // Calculate cascade far clipping plane distances + cascade_distances[cascade_count - 1] = shadow_clip_far; + for (unsigned int i = 0; i < cascade_count - 1; ++i) + { + const float weight = static_cast(i + 1) / static_cast(cascade_count); + + // Calculate linear and logarithmic distribution distances + const float linear_distance = math::lerp(camera_clip_near, shadow_clip_far, weight); + const float log_distance = math::log_lerp(camera_clip_near, shadow_clip_far, weight); + + // Interpolate between linear and logarithmic distribution distances + cascade_distances[i] = math::lerp(linear_distance, log_distance, light.get_shadow_cascade_distribution()); + } + + // Calculate viewports for each shadow map + const int shadow_map_resolution = static_cast(light.get_shadow_framebuffer()->get_depth_attachment()->get_width()); + const int cascade_resolution = shadow_map_resolution / 2; + int4 shadow_map_viewports[4]; + for (int i = 0; i < 4; ++i) + { + int x = i % 2; + int y = i / 2; + + int4& viewport = shadow_map_viewports[i]; + viewport[0] = x * cascade_resolution; + viewport[1] = y * cascade_resolution; + viewport[2] = cascade_resolution; + viewport[3] = cascade_resolution; + } + + // Calculate a view-projection matrix from the directional light's transform + math::transform light_transform = light.get_transform_tween().interpolate(ctx.alpha); + float3 forward = light_transform.rotation * config::global_forward; + float3 up = light_transform.rotation * config::global_up; + float4x4 light_view = math::look_at(light_transform.translation, light_transform.translation + forward, up); + float4x4 light_projection = math::ortho(-1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f); + float4x4 light_view_projection = light_projection * light_view; + + float4x4 cropped_view_projection; + float4x4 model_view_projection; + + // Sort render queue + queue.sort(operation_compare); + + gl::shader_program* active_shader_program = nullptr; + + for (unsigned int i = 0; i < cascade_count; ++i) + { + // Set viewport for this shadow map + const int4& viewport = shadow_map_viewports[i]; + rasterizer->set_viewport(viewport[0], viewport[1], viewport[2], viewport[3]); + + // Calculate projection matrix for view camera subfrustum + const float subfrustum_near = (i) ? cascade_distances[i - 1] : camera_clip_near; + const float subfrustum_far = cascade_distances[i]; + float4x4 subfrustum_projection = math::perspective_half_z(camera_fov, camera_aspect_ratio, subfrustum_near, subfrustum_far); + + // Calculate view camera subfrustum + geom::view_frustum subfrustum(subfrustum_projection * ctx.view); + + // Create AABB containing the view camera subfrustum corners + const std::array& subfrustum_corners = subfrustum.get_corners(); + geom::aabb subfrustum_aabb = {subfrustum_corners[0], subfrustum_corners[0]}; + for (int j = 1; j < 8; ++j) + { + subfrustum_aabb.min_point = math::min(subfrustum_aabb.min_point, subfrustum_corners[j]); + subfrustum_aabb.max_point = math::max(subfrustum_aabb.max_point, subfrustum_corners[j]); + } + + // Transform subfrustum AABB into the light clip-space + geom::aabb cropping_bounds = geom::aabb::transform(subfrustum_aabb, light_view_projection); + + // Quantize clip-space coordinates + const float texel_scale_x = (cropping_bounds.max_point.x() - cropping_bounds.min_point.x()) / static_cast(cascade_resolution); + const float texel_scale_y = (cropping_bounds.max_point.y() - cropping_bounds.min_point.y()) / static_cast(cascade_resolution); + cropping_bounds.min_point.x() = std::floor(cropping_bounds.min_point.x() / texel_scale_x) * texel_scale_x; + cropping_bounds.max_point.x() = std::floor(cropping_bounds.max_point.x() / texel_scale_x) * texel_scale_x; + cropping_bounds.min_point.y() = std::floor(cropping_bounds.min_point.y() / texel_scale_y) * texel_scale_y; + cropping_bounds.max_point.y() = std::floor(cropping_bounds.max_point.y() / texel_scale_y) * texel_scale_y; + + // Recalculate light projection matrix with quantized coordinates + light_projection = math::ortho_half_z + ( + cropping_bounds.min_point.x(), cropping_bounds.max_point.x(), + cropping_bounds.min_point.y(), cropping_bounds.max_point.y(), + cropping_bounds.min_point.z(), cropping_bounds.max_point.z() + ); + + // Calculate cropped view projection matrix + cropped_view_projection = light_projection * light_view; + + // Calculate world-space to cascade texture-space transformation matrix + cascade_matrices[i] = bias_tile_matrices[i] * cropped_view_projection; + + for (const render::operation& operation: queue) + { + const render::material* material = operation.material; + if (material) + { + // Skip materials which don't cast shadows + if (material->get_shadow_mode() == shadow_mode::none) + continue; + + if (material->is_two_sided() != two_sided) + { + if (material->is_two_sided()) + { + glDisable(GL_CULL_FACE); + } + else + { + glEnable(GL_CULL_FACE); + } + + two_sided = material->is_two_sided(); + } + } + + // Switch shader programs if necessary + gl::shader_program* shader_program = (operation.bone_count) ? skinned_shader_program : unskinned_shader_program; + if (active_shader_program != shader_program) + { + active_shader_program = shader_program; + rasterizer->use_program(*active_shader_program); + } + + // Calculate model-view-projection matrix + model_view_projection = cropped_view_projection * operation.transform; + + // Upload operation-dependent parameters to shader program + if (active_shader_program == unskinned_shader_program) + { + unskinned_model_view_projection_input->upload(model_view_projection); + } + else if (active_shader_program == skinned_shader_program) + { + skinned_model_view_projection_input->upload(model_view_projection); + } + + // Draw geometry + rasterizer->draw_arrays(*operation.vertex_array, operation.drawing_mode, operation.start_index, operation.index_count); + } + } +} + +bool operation_compare(const render::operation& a, const render::operation& b) +{ + const bool skinned_a = (a.bone_count); + const bool skinned_b = (b.bone_count); + const bool two_sided_a = (a.material) ? a.material->is_two_sided() : false; + const bool two_sided_b = (b.material) ? b.material->is_two_sided() : false; + + if (skinned_a) + { + if (skinned_b) + { + // A and B are both skinned, sort by two-sided + if (two_sided_a) + { + if (two_sided_b) + { + // A and B are both two-sided, sort by VAO + return (a.vertex_array < b.vertex_array); + } + else + { + // A is two-sided, B is one-sided. Render B first + return false; + } + } + else + { + if (two_sided_b) + { + // A is one-sided, B is two-sided. Render A first + return true; + } + else + { + // A and B are both one-sided, sort by VAO + return (a.vertex_array < b.vertex_array); + } + } + } + else + { + // A is skinned, B is unskinned. Render B first + return false; + } + } + else + { + if (skinned_b) + { + // A is unskinned, B is skinned. Render A first + return true; + } + else + { + // A and B are both unskinned, sort by two-sided + if (two_sided_a) + { + if (two_sided_b) + { + // A and B are both two-sided, sort by VAO + return (a.vertex_array < b.vertex_array); + } + else + { + // A is two-sided, B is one-sided. Render B first + return false; + } + } + else + { + if (two_sided_b) + { + // A is one-sided, B is two-sided. Render A first + return true; + } + else + { + // A and B are both one-sided, sort by VAO + return (a.vertex_array < b.vertex_array); + } + } + } + } +} + +} // namespace render diff --git a/src/engine/render/passes/shadow-map-pass.hpp b/src/engine/render/passes/shadow-map-pass.hpp new file mode 100644 index 0000000..8f979cc --- /dev/null +++ b/src/engine/render/passes/shadow-map-pass.hpp @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_RENDER_SHADOW_MAP_PASS_HPP +#define ANTKEEPER_RENDER_SHADOW_MAP_PASS_HPP + +#include +#include +#include +#include +#include + +class resource_manager; + +namespace render { + +/** + * Renders shadow maps. + */ +class shadow_map_pass: public pass +{ +public: + /** + * Constructs a shadow map pass. + * + * @param rasterizer Rasterizer. + * @param framebuffer Shadow map framebuffer. + * @param resource_manage Resource manager. + */ + shadow_map_pass(gl::rasterizer* rasterizer, resource_manager* resource_manager); + + /** + * Destructs a shadow map pass. + */ + virtual ~shadow_map_pass(); + + /** + * Renders shadow maps for a single camera. + * + * @param ctx Render context. + * @param queue Render queue. + */ + virtual void render(const render::context& ctx, render::queue& queue) const final; + +private: + /** + * Renders cascaded shadow maps for a single directional light. + * + * @param light Shadow-casting directional light. + * @param ctx Render context. + * @param queue Render queue. + */ + void render_csm(const scene::directional_light& light, const render::context& ctx, render::queue& queue) const; + + gl::shader_program* unskinned_shader_program; + const gl::shader_input* unskinned_model_view_projection_input; + + gl::shader_program* skinned_shader_program; + const gl::shader_input* skinned_model_view_projection_input; + + float4x4 bias_tile_matrices[4]; +}; + +} // namespace render + +#endif // ANTKEEPER_RENDER_SHADOW_MAP_PASS_HPP diff --git a/src/engine/render/passes/sky-pass.cpp b/src/engine/render/passes/sky-pass.cpp new file mode 100644 index 0000000..18f3756 --- /dev/null +++ b/src/engine/render/passes/sky-pass.cpp @@ -0,0 +1,665 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace render { + +sky_pass::sky_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager): + pass(rasterizer, framebuffer), + mouse_position({0.0f, 0.0f}), + sky_model(nullptr), + sky_material(nullptr), + sky_model_vao(nullptr), + sky_shader_program(nullptr), + moon_model(nullptr), + moon_model_vao(nullptr), + moon_material(nullptr), + moon_shader_program(nullptr), + stars_model(nullptr), + stars_model_vao(nullptr), + star_material(nullptr), + star_shader_program(nullptr), + observer_position_tween({0, 0, 0}, math::lerp), + sun_position_tween(float3{1.0f, 0.0f, 0.0f}, math::lerp), + sun_luminance_tween(float3{0.0f, 0.0f, 0.0f}, math::lerp), + sun_illuminance_tween(float3{0.0f, 0.0f, 0.0f}, math::lerp), + icrf_to_eus_translation({0, 0, 0}, math::lerp), + icrf_to_eus_rotation(math::quaternion::identity(), math::nlerp), + moon_position_tween(float3{0, 0, 0}, math::lerp), + moon_rotation_tween(math::quaternion::identity(), math::nlerp), + moon_angular_radius_tween(0.0f, math::lerp), + moon_sunlight_direction_tween(float3{0, 0, 0}, math::lerp), + moon_sunlight_illuminance_tween(float3{0, 0, 0}, math::lerp), + moon_planetlight_direction_tween(float3{0, 0, 0}, math::lerp), + moon_planetlight_illuminance_tween(float3{0, 0, 0}, math::lerp), + moon_illuminance_tween(float3{0.0f, 0.0f, 0.0f}, math::lerp), + render_transmittance_lut(false), + magnification(1.0f) +{ + // Build quad VBO and VAO + const float quad_vertex_data[] = + { + -1.0f, 1.0f, 0.0f, + -1.0f, -1.0f, 0.0f, + 1.0f, 1.0f, 0.0f, + 1.0f, 1.0f, 0.0f, + -1.0f, -1.0f, 0.0f, + 1.0f, -1.0f, 0.0f + }; + std::size_t quad_vertex_size = 3; + std::size_t quad_vertex_stride = sizeof(float) * quad_vertex_size; + std::size_t quad_vertex_count = 6; + quad_vbo = new gl::vertex_buffer(sizeof(float) * quad_vertex_size * quad_vertex_count, quad_vertex_data); + quad_vao = new gl::vertex_array(); + gl::vertex_attribute quad_position_attribute; + quad_position_attribute.buffer = quad_vbo; + quad_position_attribute.offset = 0; + quad_position_attribute.stride = quad_vertex_stride; + quad_position_attribute.type = gl::vertex_attribute_type::float_32; + quad_position_attribute.components = 3; + quad_vao->bind(render::vertex_attribute::position, quad_position_attribute); + + // Create transmittance LUT texture and framebuffer (32F color, no depth) + transmittance_lut_texture = new gl::texture_2d(256, 64, gl::pixel_type::float_32, gl::pixel_format::rgb); + transmittance_lut_texture->set_wrapping(gl::texture_wrapping::extend, gl::texture_wrapping::extend); + transmittance_lut_texture->set_filters(gl::texture_min_filter::linear, gl::texture_mag_filter::linear); + transmittance_lut_texture->set_max_anisotropy(0.0f); + transmittance_lut_framebuffer = new gl::framebuffer({transmittance_lut_texture->get_width(), transmittance_lut_texture->get_height()}); + transmittance_lut_framebuffer->attach(gl::framebuffer_attachment_type::color, transmittance_lut_texture); + transmittance_lut_resolution = {static_cast(transmittance_lut_texture->get_width()), static_cast(transmittance_lut_texture->get_height())}; + + // Load transmittance LUT shader template + transmittance_shader_template = resource_manager->load("transmittance-lut.glsl"); + + // Build transmittance LUT shader program + transmittance_shader_program = transmittance_shader_template->build(); + transmittance_atmosphere_radii_input = transmittance_shader_program->get_input("atmosphere_radii"); + transmittance_rayleigh_parameters_input = transmittance_shader_program->get_input("rayleigh_parameters"); + transmittance_mie_parameters_input = transmittance_shader_program->get_input("mie_parameters"); + transmittance_ozone_distribution_input = transmittance_shader_program->get_input("ozone_distribution"); + transmittance_ozone_absorption_input = transmittance_shader_program->get_input("ozone_absorption"); + transmittance_resolution_input = transmittance_shader_program->get_input("resolution"); + + // Create sky LUT texture and framebuffer (32F color, no depth) + int sky_lut_width = 200; + int sky_lut_height = 100; + sky_lut_resolution = {static_cast(sky_lut_width), static_cast(sky_lut_height)}; + sky_lut_texture = new gl::texture_2d(sky_lut_width, sky_lut_height, gl::pixel_type::float_32, gl::pixel_format::rgb); + sky_lut_texture->set_wrapping(gl::texture_wrapping::extend, gl::texture_wrapping::extend); + sky_lut_texture->set_filters(gl::texture_min_filter::linear, gl::texture_mag_filter::linear); + sky_lut_texture->set_max_anisotropy(0.0f); + sky_lut_framebuffer = new gl::framebuffer(sky_lut_width, sky_lut_height); + sky_lut_framebuffer->attach(gl::framebuffer_attachment_type::color, sky_lut_texture); + + // Load sky LUT shader template + sky_lut_shader_template = resource_manager->load("sky-illuminance-lut.glsl"); + + // Build sky LUT shader program + sky_lut_shader_program = sky_lut_shader_template->build(); + sky_lut_light_direction_input = sky_lut_shader_program->get_input("light_direction"); + sky_lut_light_illuminance_input = sky_lut_shader_program->get_input("light_illuminance"); + sky_lut_atmosphere_radii_input = sky_lut_shader_program->get_input("atmosphere_radii"); + sky_lut_observer_position_input = sky_lut_shader_program->get_input("observer_position"); + sky_lut_rayleigh_parameters_input = sky_lut_shader_program->get_input("rayleigh_parameters"); + sky_lut_mie_parameters_input = sky_lut_shader_program->get_input("mie_parameters"); + sky_lut_ozone_distribution_input = sky_lut_shader_program->get_input("ozone_distribution"); + sky_lut_ozone_absorption_input = sky_lut_shader_program->get_input("ozone_absorption"); + sky_lut_airglow_illuminance_input = sky_lut_shader_program->get_input("airglow_illuminance"); + sky_lut_resolution_input = sky_lut_shader_program->get_input("resolution"); + sky_lut_transmittance_lut_input = sky_lut_shader_program->get_input("transmittance_lut"); + sky_lut_transmittance_lut_resolution_input = sky_lut_shader_program->get_input("transmittance_lut_resolution"); +} + +sky_pass::~sky_pass() +{ + delete sky_lut_framebuffer; + delete sky_lut_texture; + delete transmittance_lut_framebuffer; + delete transmittance_lut_texture; + delete quad_vao; + delete quad_vbo; + + delete transmittance_shader_program; + delete sky_lut_shader_program; + + /// @TODO + // resource_maanger->unload("transmittance-lut.glsl"); + // resource_maanger->unload("sky-illuminance-lut.glsl"); +} + +void sky_pass::render(const render::context& ctx, render::queue& queue) const +{ + glDisable(GL_BLEND); + glDisable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + + // Render transmittance LUT if transmittance parameters have been altered. + if (render_transmittance_lut) + { + // Render transmittance LUT + rasterizer->set_viewport(0, 0, transmittance_lut_texture->get_width(), transmittance_lut_texture->get_height()); + rasterizer->use_framebuffer(*transmittance_lut_framebuffer); + rasterizer->use_program(*transmittance_shader_program); + transmittance_atmosphere_radii_input->upload(atmosphere_radii); + transmittance_rayleigh_parameters_input->upload(rayleigh_parameters); + transmittance_mie_parameters_input->upload(mie_parameters); + transmittance_ozone_distribution_input->upload(ozone_distribution); + transmittance_ozone_absorption_input->upload(ozone_absorption); + if (transmittance_resolution_input) + transmittance_resolution_input->upload(transmittance_lut_resolution); + rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6); + + // Don't render transmittance LUT next frame unless parameters have changed. + render_transmittance_lut = false; + } + + // Construct matrices + const scene::camera& camera = *ctx.camera; + float clip_near = camera.get_clip_near_tween().interpolate(ctx.alpha); + float clip_far = camera.get_clip_far_tween().interpolate(ctx.alpha); + float3 model_scale = float3{1.0f, 1.0f, 1.0f} * (clip_near + clip_far) * 0.5f; + float4x4 model = math::scale(math::matrix4::identity(), model_scale); + float4x4 view = float4x4(float3x3(ctx.view)); + float4x4 model_view = view * model; + const float4x4& projection = ctx.projection; + float4x4 view_projection = projection * view; + float4x4 model_view_projection = projection * model_view; + + // Interpolate observer position + float3 observer_position = observer_position_tween.interpolate(ctx.alpha); + + // Construct tweened ICRF to EUS transformation + math::transformation::se3 icrf_to_eus = + { + icrf_to_eus_translation.interpolate(ctx.alpha), + icrf_to_eus_rotation.interpolate(ctx.alpha) + }; + + // Get EUS direction to sun + float3 sun_position = sun_position_tween.interpolate(ctx.alpha); + float3 sun_direction = math::normalize(sun_position); + + // Interpolate and expose sun luminance and illuminance + float3 sun_illuminance = sun_illuminance_tween.interpolate(ctx.alpha) * ctx.exposure; + float3 sun_luminance = sun_luminance_tween.interpolate(ctx.alpha) * ctx.exposure; + + float3 moon_position = moon_position_tween.interpolate(ctx.alpha); + float3 moon_direction = math::normalize(moon_position); + float3 moon_illuminance = moon_illuminance_tween.interpolate(ctx.alpha) * ctx.exposure; + float moon_angular_radius = moon_angular_radius_tween.interpolate(ctx.alpha) * magnification; + + float sun_y = color::aces::ap1.luminance(sun_transmitted_illuminance); + float moon_y = color::aces::ap1.luminance(moon_transmitted_illuminance); + float3 dominant_light_direction = (sun_y > moon_y) ? sun_direction : moon_direction; + float3 dominant_light_illuminance = (sun_y > moon_y) ? sun_illuminance : moon_illuminance; + + if (moon_y > sun_y) + sun_luminance *= 0.0f; + + // Render sky illuminance LUT + auto sky_lut_viewport = sky_lut_framebuffer->get_dimensions(); + rasterizer->set_viewport(0, 0, std::get<0>(sky_lut_viewport), std::get<1>(sky_lut_viewport)); + rasterizer->use_framebuffer(*sky_lut_framebuffer); + rasterizer->use_program(*sky_lut_shader_program); + sky_lut_light_direction_input->upload(dominant_light_direction); + sky_lut_light_illuminance_input->upload(dominant_light_illuminance); + sky_lut_atmosphere_radii_input->upload(atmosphere_radii); + sky_lut_observer_position_input->upload(observer_position); + sky_lut_rayleigh_parameters_input->upload(rayleigh_parameters); + sky_lut_mie_parameters_input->upload(mie_parameters); + sky_lut_ozone_distribution_input->upload(ozone_distribution); + sky_lut_ozone_absorption_input->upload(ozone_absorption); + sky_lut_airglow_illuminance_input->upload(airglow_illuminance * ctx.exposure); + if (sky_lut_resolution_input) + sky_lut_resolution_input->upload(sky_lut_resolution); + sky_lut_transmittance_lut_input->upload(transmittance_lut_texture); + if (sky_lut_transmittance_lut_resolution_input) + sky_lut_transmittance_lut_resolution_input->upload(transmittance_lut_resolution); + rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6); + + + + rasterizer->use_framebuffer(*framebuffer); + auto viewport = framebuffer->get_dimensions(); + rasterizer->set_viewport(0, 0, std::get<0>(viewport), std::get<1>(viewport)); + float2 resolution = {static_cast(std::get<0>(viewport)), static_cast(std::get<1>(viewport))}; + + // Draw atmosphere + if (sky_model) + { + rasterizer->use_program(*sky_shader_program); + + // Upload shader parameters + if (model_view_projection_input) + model_view_projection_input->upload(model_view_projection); + if (mouse_input) + mouse_input->upload(mouse_position); + if (resolution_input) + resolution_input->upload(resolution); + if (light_direction_input) + light_direction_input->upload(dominant_light_direction); + if (sun_luminance_input) + sun_luminance_input->upload(sun_luminance); + if (sun_angular_radius_input) + sun_angular_radius_input->upload(sun_angular_radius * magnification); + if (atmosphere_radii_input) + atmosphere_radii_input->upload(atmosphere_radii); + if (observer_position_input) + observer_position_input->upload(observer_position); + if (sky_illuminance_lut_input) + sky_illuminance_lut_input->upload(sky_lut_texture); + if (sky_illuminance_lut_resolution_input) + sky_illuminance_lut_resolution_input->upload(sky_lut_resolution); + + sky_material->upload(ctx.alpha); + + rasterizer->draw_arrays(*sky_model_vao, sky_model_drawing_mode, sky_model_start_index, sky_model_index_count); + } + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + //glBlendFunc(GL_ONE, GL_ONE); + + // Draw stars + if (stars_model) + { + float star_distance = (clip_near + clip_far) * 0.5f; + + model = float4x4(float3x3(icrf_to_eus.r)); + model = math::scale(model, {star_distance, star_distance, star_distance}); + + model_view = view * model; + + rasterizer->use_program(*star_shader_program); + if (star_model_view_input) + star_model_view_input->upload(model_view); + if (star_projection_input) + star_projection_input->upload(projection); + if (star_distance_input) + star_distance_input->upload(star_distance); + if (star_exposure_input) + star_exposure_input->upload(ctx.exposure); + + star_material->upload(ctx.alpha); + + rasterizer->draw_arrays(*stars_model_vao, stars_model_drawing_mode, stars_model_start_index, stars_model_index_count); + } + + // Draw moon model + //if (moon_position.y() >= -moon_angular_radius) + { + float moon_distance = (clip_near + clip_far) * 0.5f; + float moon_radius = moon_angular_radius * moon_distance; + + math::transform moon_transform; + moon_transform.translation = math::normalize(moon_position) * moon_distance; + moon_transform.rotation = moon_rotation_tween.interpolate(ctx.alpha); + moon_transform.scale = {moon_radius, moon_radius, moon_radius}; + + model = math::matrix_cast(moon_transform); + float3x3 normal_model = math::transpose(math::inverse(float3x3(model))); + + rasterizer->use_program(*moon_shader_program); + if (moon_model_input) + moon_model_input->upload(model); + if (moon_view_projection_input) + moon_view_projection_input->upload(view_projection); + if (moon_normal_model_input) + moon_normal_model_input->upload(normal_model); + if (moon_camera_position_input) + moon_camera_position_input->upload(ctx.camera_transform.translation); + if (moon_sunlight_direction_input) + moon_sunlight_direction_input->upload(math::normalize(moon_sunlight_direction_tween.interpolate(ctx.alpha))); + if (moon_sunlight_illuminance_input) + moon_sunlight_illuminance_input->upload(moon_sunlight_illuminance_tween.interpolate(ctx.alpha) * ctx.exposure); + if (moon_planetlight_direction_input) + moon_planetlight_direction_input->upload(math::normalize(moon_planetlight_direction_tween.interpolate(ctx.alpha))); + if (moon_planetlight_illuminance_input) + moon_planetlight_illuminance_input->upload(moon_planetlight_illuminance_tween.interpolate(ctx.alpha) * ctx.exposure); + + moon_material->upload(ctx.alpha); + rasterizer->draw_arrays(*moon_model_vao, moon_model_drawing_mode, moon_model_start_index, moon_model_index_count); + } +} + +void sky_pass::set_sky_model(const model* model) +{ + sky_model = model; + + if (sky_model) + { + sky_model_vao = model->get_vertex_array(); + + const std::vector& groups = *model->get_groups(); + for (model_group* group: groups) + { + sky_material = group->get_material(); + sky_model_drawing_mode = group->get_drawing_mode(); + sky_model_start_index = group->get_start_index(); + sky_model_index_count = group->get_index_count(); + } + + if (sky_material) + { + sky_shader_program = sky_material->get_shader_program(); + + if (sky_shader_program) + { + model_view_projection_input = sky_shader_program->get_input("model_view_projection"); + mouse_input = sky_shader_program->get_input("mouse"); + resolution_input = sky_shader_program->get_input("resolution"); + light_direction_input = sky_shader_program->get_input("light_direction"); + sun_luminance_input = sky_shader_program->get_input("sun_luminance"); + sun_angular_radius_input = sky_shader_program->get_input("sun_angular_radius"); + atmosphere_radii_input = sky_shader_program->get_input("atmosphere_radii"); + observer_position_input = sky_shader_program->get_input("observer_position"); + sky_illuminance_lut_input = sky_shader_program->get_input("sky_illuminance_lut"); + sky_illuminance_lut_resolution_input = sky_shader_program->get_input("sky_illuminance_lut_resolution"); + } + } + } + else + { + sky_model_vao = nullptr; + } +} + +void sky_pass::set_moon_model(const model* model) +{ + moon_model = model; + + if (moon_model) + { + moon_model_vao = model->get_vertex_array(); + + const std::vector& groups = *model->get_groups(); + for (model_group* group: groups) + { + moon_material = group->get_material(); + moon_model_drawing_mode = group->get_drawing_mode(); + moon_model_start_index = group->get_start_index(); + moon_model_index_count = group->get_index_count(); + } + + if (moon_material) + { + moon_shader_program = moon_material->get_shader_program(); + + if (moon_shader_program) + { + moon_model_input = moon_shader_program->get_input("model"); + moon_view_projection_input = moon_shader_program->get_input("view_projection"); + moon_normal_model_input = moon_shader_program->get_input("normal_model"); + moon_camera_position_input = moon_shader_program->get_input("camera_position"); + moon_sunlight_direction_input = moon_shader_program->get_input("sunlight_direction"); + moon_sunlight_illuminance_input = moon_shader_program->get_input("sunlight_illuminance"); + moon_planetlight_direction_input = moon_shader_program->get_input("planetlight_direction"); + moon_planetlight_illuminance_input = moon_shader_program->get_input("planetlight_illuminance"); + } + } + } + else + { + moon_model = nullptr; + } +} + +void sky_pass::set_stars_model(const model* model) +{ + stars_model = model; + + if (stars_model) + { + stars_model_vao = model->get_vertex_array(); + + const std::vector& groups = *model->get_groups(); + for (model_group* group: groups) + { + star_material = group->get_material(); + stars_model_drawing_mode = group->get_drawing_mode(); + stars_model_start_index = group->get_start_index(); + stars_model_index_count = group->get_index_count(); + } + + if (star_material) + { + star_shader_program = star_material->get_shader_program(); + + if (star_shader_program) + { + star_model_view_input = star_shader_program->get_input("model_view"); + star_projection_input = star_shader_program->get_input("projection"); + star_distance_input = star_shader_program->get_input("star_distance"); + star_exposure_input = star_shader_program->get_input("camera.exposure"); + } + } + } + else + { + stars_model = nullptr; + } +} + +void sky_pass::update_tweens() +{ + observer_position_tween.update(); + sun_position_tween.update(); + sun_luminance_tween.update(); + sun_illuminance_tween.update(); + icrf_to_eus_translation.update(); + icrf_to_eus_rotation.update(); + + moon_position_tween.update(); + moon_rotation_tween.update(); + moon_angular_radius_tween.update(); + moon_sunlight_direction_tween.update(); + moon_sunlight_illuminance_tween.update(); + moon_planetlight_direction_tween.update(); + moon_planetlight_illuminance_tween.update(); + moon_illuminance_tween.update(); +} + +void sky_pass::set_magnification(float magnification) +{ + this->magnification = magnification; +} + +void sky_pass::set_icrf_to_eus(const math::transformation::se3& transformation) +{ + icrf_to_eus_translation[1] = transformation.t; + icrf_to_eus_rotation[1] = transformation.r; +} + +void sky_pass::set_sun_position(const float3& position) +{ + sun_position_tween[1] = position; +} + +void sky_pass::set_sun_illuminance(const float3& illuminance, const float3& transmitted_illuminance) +{ + sun_illuminance_tween[1] = illuminance; + sun_transmitted_illuminance = transmitted_illuminance; +} + +void sky_pass::set_sun_luminance(const float3& luminance) +{ + sun_luminance_tween[1] = luminance; +} + +void sky_pass::set_sun_angular_radius(float radius) +{ + sun_angular_radius = radius; +} + +void sky_pass::set_planet_radius(float radius) +{ + atmosphere_radii.x() = radius; + atmosphere_radii.y() = atmosphere_radii.x() + atmosphere_upper_limit; + atmosphere_radii.z() = atmosphere_radii.y() * atmosphere_radii.y(); + observer_position_tween[1] = {0.0f, atmosphere_radii.x() + observer_elevation, 0.0f}; + + // Trigger transmittance LUT render + render_transmittance_lut = true; +} + +void sky_pass::set_atmosphere_upper_limit(float limit) +{ + atmosphere_upper_limit = limit; + atmosphere_radii.y() = atmosphere_radii.x() + atmosphere_upper_limit; + atmosphere_radii.z() = atmosphere_radii.y() * atmosphere_radii.y(); + + // Trigger transmittance LUT render + render_transmittance_lut = true; +} + +void sky_pass::set_observer_elevation(float elevation) +{ + observer_elevation = elevation; + observer_position_tween[1] = {0.0f, atmosphere_radii.x() + observer_elevation, 0.0f}; +} + +void sky_pass::set_rayleigh_parameters(float scale_height, const float3& scattering) +{ + rayleigh_parameters = + { + -1.0f / scale_height, + scattering.x(), + scattering.y(), + scattering.z() + }; + + // Trigger transmittance LUT render + render_transmittance_lut = true; +} + +void sky_pass::set_mie_parameters(float scale_height, float scattering, float extinction, float anisotropy) +{ + mie_parameters = + { + -1.0f / scale_height, + scattering, + extinction, + anisotropy + }; + + // Trigger transmittance LUT render + render_transmittance_lut = true; +} + +void sky_pass::set_ozone_parameters(float lower_limit, float upper_limit, float mode, const float3& absorption) +{ + ozone_distribution = + { + 1.0f / (lower_limit - mode), + 1.0f / (upper_limit - mode), + mode + }; + ozone_absorption = absorption; + + // Trigger transmittance LUT render + render_transmittance_lut = true; +} + +void sky_pass::set_airglow_illuminance(const float3& illuminance) +{ + airglow_illuminance = illuminance; +} + +void sky_pass::set_moon_position(const float3& position) +{ + moon_position_tween[1] = position; +} + +void sky_pass::set_moon_rotation(const math::quaternion& rotation) +{ + moon_rotation_tween[1] = rotation; +} + +void sky_pass::set_moon_angular_radius(float angular_radius) +{ + moon_angular_radius_tween[1] = angular_radius; +} + +void sky_pass::set_moon_sunlight_direction(const float3& direction) +{ + moon_sunlight_direction_tween[1] = direction; +} + +void sky_pass::set_moon_sunlight_illuminance(const float3& illuminance) +{ + moon_sunlight_illuminance_tween[1] = illuminance; +} + +void sky_pass::set_moon_planetlight_direction(const float3& direction) +{ + moon_planetlight_direction_tween[1] = direction; +} + +void sky_pass::set_moon_planetlight_illuminance(const float3& illuminance) +{ + moon_planetlight_illuminance_tween[1] = illuminance; +} + +void sky_pass::set_moon_illuminance(const float3& illuminance, const float3& transmitted_illuminance) +{ + moon_illuminance_tween[1] = illuminance; + moon_transmitted_illuminance = transmitted_illuminance; +} + +void sky_pass::set_transmittance_lut_resolution(std::uint16_t width, std::uint16_t height) +{ + transmittance_lut_texture->resize(width, height, nullptr); + transmittance_lut_framebuffer->resize({transmittance_lut_texture->get_width(), transmittance_lut_texture->get_height()}); + transmittance_lut_resolution = {static_cast(width), static_cast(height)}; + + // Trigger transmittance LUT render + render_transmittance_lut = true; +} + +} // namespace render diff --git a/src/engine/render/passes/sky-pass.hpp b/src/engine/render/passes/sky-pass.hpp new file mode 100644 index 0000000..417a450 --- /dev/null +++ b/src/engine/render/passes/sky-pass.hpp @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_RENDER_SKY_PASS_HPP +#define ANTKEEPER_RENDER_SKY_PASS_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class resource_manager; + +namespace render { + +class material; +class model; + +/** + * + */ +class sky_pass: public pass +{ +public: + sky_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager); + virtual ~sky_pass(); + virtual void render(const render::context& ctx, render::queue& queue) const final; + + void update_tweens(); + + void set_magnification(float scale); + + void set_sky_model(const model* model); + void set_moon_model(const model* model); + void set_stars_model(const model* model); + + void set_icrf_to_eus(const math::transformation::se3& transformation); + + void set_sun_position(const float3& position); + void set_sun_luminance(const float3& luminance); + void set_sun_illuminance(const float3& illuminance, const float3& transmitted_illuminance); + void set_sun_angular_radius(float radius); + void set_planet_radius(float radius); + void set_atmosphere_upper_limit(float limit); + void set_observer_elevation(float elevation); + void set_rayleigh_parameters(float scale_height, const float3& scattering); + void set_mie_parameters(float scale_height, float scattering, float extinction, float anisotropy); + void set_ozone_parameters(float lower_limit, float upper_limit, float mode, const float3& absorption); + void set_airglow_illuminance(const float3& illuminance); + + void set_moon_position(const float3& position); + void set_moon_rotation(const math::quaternion& rotation); + void set_moon_angular_radius(float angular_radius); + void set_moon_sunlight_direction(const float3& direction); + void set_moon_sunlight_illuminance(const float3& illuminance); + void set_moon_planetlight_direction(const float3& direction); + void set_moon_planetlight_illuminance(const float3& illuminance); + void set_moon_illuminance(const float3& illuminance, const float3& transmitted_illuminance); + + /** + * Sets the resolution of transmittance LUT. + * + * @param width Transmittance LUT width, in pixels. + * @param height Transmittance LUT height, in pixels. + */ + void set_transmittance_lut_resolution(std::uint16_t width, std::uint16_t height); + +private: + gl::vertex_buffer* quad_vbo; + gl::vertex_array* quad_vao; + + gl::texture_2d* transmittance_lut_texture; + gl::framebuffer* transmittance_lut_framebuffer; + float2 transmittance_lut_resolution; + render::shader_template* transmittance_shader_template; + gl::shader_program* transmittance_shader_program; + const gl::shader_input* transmittance_atmosphere_radii_input; + const gl::shader_input* transmittance_rayleigh_parameters_input; + const gl::shader_input* transmittance_mie_parameters_input; + const gl::shader_input* transmittance_ozone_distribution_input; + const gl::shader_input* transmittance_ozone_absorption_input; + const gl::shader_input* transmittance_resolution_input; + mutable bool render_transmittance_lut; + + gl::texture_2d* sky_lut_texture; + gl::framebuffer* sky_lut_framebuffer; + render::shader_template* sky_lut_shader_template; + gl::shader_program* sky_lut_shader_program; + float2 sky_lut_resolution; + const gl::shader_input* sky_lut_light_direction_input; + const gl::shader_input* sky_lut_light_illuminance_input; + const gl::shader_input* sky_lut_atmosphere_radii_input; + const gl::shader_input* sky_lut_observer_position_input; + const gl::shader_input* sky_lut_rayleigh_parameters_input; + const gl::shader_input* sky_lut_mie_parameters_input; + const gl::shader_input* sky_lut_ozone_distribution_input; + const gl::shader_input* sky_lut_ozone_absorption_input; + const gl::shader_input* sky_lut_airglow_illuminance_input; + const gl::shader_input* sky_lut_resolution_input; + const gl::shader_input* sky_lut_transmittance_lut_input; + const gl::shader_input* sky_lut_transmittance_lut_resolution_input; + + gl::shader_program* sky_shader_program; + const gl::shader_input* model_view_projection_input; + const gl::shader_input* mouse_input; + const gl::shader_input* resolution_input; + const gl::shader_input* light_direction_input; + const gl::shader_input* sun_luminance_input; + const gl::shader_input* sun_angular_radius_input; + const gl::shader_input* atmosphere_radii_input; + const gl::shader_input* observer_position_input; + const gl::shader_input* sky_illuminance_lut_input; + const gl::shader_input* sky_illuminance_lut_resolution_input; + + gl::shader_program* moon_shader_program; + const gl::shader_input* moon_model_input; + const gl::shader_input* moon_view_projection_input; + const gl::shader_input* moon_normal_model_input; + const gl::shader_input* moon_camera_position_input; + const gl::shader_input* moon_sunlight_direction_input; + const gl::shader_input* moon_sunlight_illuminance_input; + const gl::shader_input* moon_planetlight_direction_input; + const gl::shader_input* moon_planetlight_illuminance_input; + + const model* sky_model; + const material* sky_material; + const gl::vertex_array* sky_model_vao; + gl::drawing_mode sky_model_drawing_mode; + std::size_t sky_model_start_index; + std::size_t sky_model_index_count; + + const model* moon_model; + const material* moon_material; + const gl::vertex_array* moon_model_vao; + gl::drawing_mode moon_model_drawing_mode; + std::size_t moon_model_start_index; + std::size_t moon_model_index_count; + + const model* stars_model; + const material* star_material; + const gl::vertex_array* stars_model_vao; + gl::drawing_mode stars_model_drawing_mode; + std::size_t stars_model_start_index; + std::size_t stars_model_index_count; + gl::shader_program* star_shader_program; + const gl::shader_input* star_model_view_input; + const gl::shader_input* star_projection_input; + const gl::shader_input* star_exposure_input; + const gl::shader_input* star_distance_input; + + float2 mouse_position; + + tween sun_position_tween; + tween sun_luminance_tween; + tween sun_illuminance_tween; + float3 sun_transmitted_illuminance; + tween icrf_to_eus_translation; + tween> icrf_to_eus_rotation; + + tween moon_position_tween; + tween> moon_rotation_tween; + tween moon_angular_radius_tween; + tween moon_sunlight_direction_tween; + tween moon_sunlight_illuminance_tween; + tween moon_planetlight_direction_tween; + tween moon_planetlight_illuminance_tween; + tween moon_illuminance_tween; + float3 moon_transmitted_illuminance; + + float sun_angular_radius; + float atmosphere_upper_limit; + float3 atmosphere_radii; + float observer_elevation; + tween observer_position_tween; + float4 rayleigh_parameters; + float4 mie_parameters; + float3 ozone_distribution; + float3 ozone_absorption; + float3 airglow_illuminance; + + float magnification; +}; + +} // namespace render + +#endif // ANTKEEPER_RENDER_SKY_PASS_HPP diff --git a/src/engine/render/passes/ui-pass.cpp b/src/engine/render/passes/ui-pass.cpp new file mode 100644 index 0000000..973ed5e --- /dev/null +++ b/src/engine/render/passes/ui-pass.cpp @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace render { + +ui_pass::ui_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager): + pass(rasterizer, framebuffer) +{} + +ui_pass::~ui_pass() +{} + +void ui_pass::render(const render::context& ctx, render::queue& queue) const +{ + glEnable(GL_BLEND); + glDisable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); + glCullFace(GL_BACK); + + auto viewport = framebuffer->get_dimensions(); + rasterizer->set_viewport(0, 0, std::get<0>(viewport), std::get<1>(viewport)); + + float4x4 view = ctx.camera->get_view_tween().interpolate(ctx.alpha); + float4x4 projection = ctx.camera->get_projection_tween().interpolate(ctx.alpha); + float4x4 view_projection = projection * view; + float4x4 model_view_projection; + + // Collect billboards + std::list billboards = *ctx.collection->get_objects(scene::billboard::object_type_id); + + // Sort billboards + + // Rebuild vertex buffer +} + +const ui_pass::parameter_set* ui_pass::load_parameter_set(const gl::shader_program* program) const +{ + // Allocate a new parameter set + parameter_set* parameters = new parameter_set(); + + // Connect inputs + parameters->time = program->get_input("time"); + parameters->model_view_projection = program->get_input("model_view_projection"); + + // Add parameter set to map of parameter sets + parameter_sets[program] = parameters; + + return parameters; +} + +} // namespace render diff --git a/src/engine/render/passes/ui-pass.hpp b/src/engine/render/passes/ui-pass.hpp new file mode 100644 index 0000000..fde9829 --- /dev/null +++ b/src/engine/render/passes/ui-pass.hpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_RENDER_UI_PASS_HPP +#define ANTKEEPER_RENDER_UI_PASS_HPP + +#include +#include +#include +#include +#include +#include + +class resource_manager; + +namespace render { + +/** + * + */ +class ui_pass: public pass +{ +public: + ui_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager); + virtual ~ui_pass(); + virtual void render(const render::context& ctx, render::queue& queue) const final; + +private: + /** + * Sets of known shader input parameters. Each time a new shader is encountered, a parameter set will be created and its inputs connected to the shader program. A null input indiciates that the shader doesn't have that parameter. + */ + struct parameter_set + { + const gl::shader_input* time; + const gl::shader_input* model_view_projection; + }; + + const parameter_set* load_parameter_set(const gl::shader_program* program) const; + + mutable std::unordered_map parameter_sets; +}; + +} // namespace render + +#endif // ANTKEEPER_RENDER_UI_PASS_HPP diff --git a/src/engine/render/queue.hpp b/src/engine/render/queue.hpp new file mode 100644 index 0000000..30f0105 --- /dev/null +++ b/src/engine/render/queue.hpp @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_RENDER_QUEUE_HPP +#define ANTKEEPER_RENDER_QUEUE_HPP + +#include +#include + +namespace render { + +/// Queue of render operations +typedef std::list queue; + +} // namespace render + +#endif // ANTKEEPER_RENDER_QUEUE_HPP diff --git a/src/render/render.hpp b/src/engine/render/render.hpp similarity index 100% rename from src/render/render.hpp rename to src/engine/render/render.hpp diff --git a/src/engine/render/renderer.cpp b/src/engine/render/renderer.cpp new file mode 100644 index 0000000..d99f520 --- /dev/null +++ b/src/engine/render/renderer.cpp @@ -0,0 +1,247 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace render { + +renderer::renderer() +{ + // Setup billboard render operation + billboard_op.bone_count = 0; + billboard_op.skinning_palette = nullptr; + billboard_op.drawing_mode = gl::drawing_mode::triangles; + billboard_op.vertex_array = nullptr; + billboard_op.start_index = 0; + billboard_op.index_count = 6; + billboard_op.instance_count = 0; + + // Allocate skinning palette + skinning_palette = new float4x4[MATERIAL_PASS_MAX_BONE_COUNT]; + + // Construct culling stage + culling_stage = new render::culling_stage(); +} + +renderer::~renderer() +{ + delete[] skinning_palette; + delete culling_stage; +} + +void renderer::render(float t, float dt, float alpha, const scene::collection& collection) const +{ + // Get list of all objects in the collection + const std::list* objects = collection.get_objects(); + + // Build list of cameras to be sorted + const std::list* cameras = collection.get_objects(scene::camera::object_type_id); + std::list sorted_cameras; + for (scene::object_base* object: *cameras) + { + sorted_cameras.push_back(static_cast(object)); + } + + // Sort cameras according to their respective compositing indices + sorted_cameras.sort + ( + [](const scene::camera* a, const scene::camera* b) -> bool + { + return a->get_composite_index() < b->get_composite_index(); + } + ); + + // Init render context + render::context ctx; + ctx.collection = &collection; + ctx.t = t; + ctx.dt = dt; + ctx.alpha = alpha; + + // Process cameras in order + for (const scene::camera* camera: sorted_cameras) + { + // Skip inactive cameras + if (!camera->is_active()) + { + continue; + } + + // Skip cameras with no compositors + const compositor* compositor = camera->get_compositor(); + if (!compositor) + { + continue; + } + + // Update render context with camera parameters + ctx.camera = camera; + ctx.camera_transform = camera->get_transform_tween().interpolate(alpha); + ctx.camera_forward = ctx.camera_transform.rotation * config::global_forward; + ctx.camera_up = ctx.camera_transform.rotation * config::global_up; + ctx.clip_near = camera->get_view_frustum().get_near(); ///< @TODO: tween this + ctx.view = camera->get_view_tween().interpolate(alpha); + ctx.projection = camera->get_projection_tween().interpolate(alpha); + ctx.view_projection = ctx.projection * ctx.view; + ctx.exposure = std::exp2(-camera->get_exposure_tween().interpolate(alpha)); + + // Execute culling stage + culling_stage->execute(ctx); + + // Create render queue + render::queue queue; + + // Queue render operations for each visible scene object + for (const scene::object_base* object: ctx.visible_objects) + { + // Process object + process_object(ctx, queue, object); + } + + // Pass render context to the camera's compositor + compositor->composite(ctx, queue); + } +} + +void renderer::set_billboard_vao(gl::vertex_array* vao) +{ + billboard_op.vertex_array = vao; +} + +void renderer::process_object(const render::context& ctx, render::queue& queue, const scene::object_base* object) const +{ + std::size_t type = object->get_object_type_id(); + + if (type == scene::model_instance::object_type_id) + process_model_instance(ctx, queue, static_cast(object)); + else if (type == scene::billboard::object_type_id) + process_billboard(ctx, queue, static_cast(object)); + else if (type == scene::lod_group::object_type_id) + process_lod_group(ctx, queue, static_cast(object)); + else if (type == scene::text::object_type_id) + process_text(ctx, queue, static_cast(object)); +} + +void renderer::process_model_instance(const render::context& ctx, render::queue& queue, const scene::model_instance* model_instance) const +{ + const model* model = model_instance->get_model(); + if (!model) + return; + + const std::vector* instance_materials = model_instance->get_materials(); + const std::vector* groups = model->get_groups(); + + render::operation operation; + operation.transform = math::matrix_cast(model_instance->get_transform_tween().interpolate(ctx.alpha)); + operation.depth = ctx.clip_near.signed_distance(float3(operation.transform[3])); + operation.vertex_array = model->get_vertex_array(); + operation.instance_count = model_instance->get_instance_count(); + + // Skinning parameters + operation.bone_count = model_instance->get_pose().size(); + if (operation.bone_count) + { + operation.skinning_palette = skinning_palette; + ::matrix_palette(model->get_skeleton().inverse_bind_pose, model_instance->get_pose(), skinning_palette); + } + else + { + operation.skinning_palette = nullptr; + } + + for (model_group* group: *groups) + { + // Determine operation material + operation.material = group->get_material(); + if ((*instance_materials)[group->get_index()]) + { + // Override model group material with the instance's material + operation.material = (*instance_materials)[group->get_index()]; + } + + operation.drawing_mode = group->get_drawing_mode(); + operation.start_index = group->get_start_index(); + operation.index_count = group->get_index_count(); + + queue.push_back(operation); + } +} + +void renderer::process_billboard(const render::context& ctx, render::queue& queue, const scene::billboard* billboard) const +{ + math::transform billboard_transform = billboard->get_transform_tween().interpolate(ctx.alpha); + billboard_op.material = billboard->get_material(); + billboard_op.depth = ctx.clip_near.signed_distance(float3(billboard_transform.translation)); + + // Align billboard + if (billboard->get_billboard_type() == scene::billboard_type::spherical) + { + billboard_transform.rotation = math::normalize(math::look_rotation(ctx.camera_forward, ctx.camera_up) * billboard_transform.rotation); + } + else if (billboard->get_billboard_type() == scene::billboard_type::cylindrical) + { + const float3& alignment_axis = billboard->get_alignment_axis(); + float3 look = math::normalize(geom::project_on_plane(billboard_transform.translation - ctx.camera_transform.translation, {0.0f, 0.0f, 0.0f}, alignment_axis)); + float3 right = math::normalize(math::cross(alignment_axis, look)); + look = math::cross(right, alignment_axis); + float3 up = math::cross(look, right); + billboard_transform.rotation = math::normalize(math::look_rotation(look, up) * billboard_transform.rotation); + } + + billboard_op.transform = math::matrix_cast(billboard_transform); + + queue.push_back(billboard_op); +} + +void renderer::process_lod_group(const render::context& ctx, render::queue& queue, const scene::lod_group* lod_group) const +{ + // Select level of detail + std::size_t level = lod_group->select_lod(*ctx.camera); + + // Process all objects in the group with the selected level of detail + const std::list& objects = lod_group->get_objects(level); + for (const scene::object_base* object: objects) + { + process_object(ctx, queue, object); + } +} + +void renderer::process_text(const render::context& ctx, render::queue& queue, const scene::text* text) const +{ + text->render(ctx, queue); +} + +} // namespace render diff --git a/src/engine/render/renderer.hpp b/src/engine/render/renderer.hpp new file mode 100644 index 0000000..eb6e6a1 --- /dev/null +++ b/src/engine/render/renderer.hpp @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_RENDER_RENDERER_HPP +#define ANTKEEPER_RENDER_RENDERER_HPP + +#include +#include +#include +#include +#include + +namespace scene +{ + class collection; + class object_base; + class model_instance; + class billboard; + class lod_group; + class text; +} + +namespace render { + +/** + * + */ +class renderer +{ +public: + renderer(); + ~renderer(); + + /** + * Renders a collection of scene objects. + * + * @param t Current time, in seconds. + * @param dt Timestep, in seconds. + * @param alpha Subframe interpolation factor. + * @param collection Collection of scene objects to render. + */ + void render(float t, float dt, float alpha, const scene::collection& collection) const; + + /** + * Sets the VAO to be used when generating render operations for billboards. + */ + void set_billboard_vao(gl::vertex_array* vao); + +private: + void process_object(const render::context& ctx, render::queue& queue, const scene::object_base* object) const; + void process_model_instance(const render::context& ctx, render::queue& queue, const scene::model_instance* model_instance) const; + void process_billboard(const render::context& ctx, render::queue& queue, const scene::billboard* billboard) const; + void process_lod_group(const render::context& ctx, render::queue& queue, const scene::lod_group* lod_group) const; + void process_text(const render::context& ctx, render::queue& queue, const scene::text* text) const; + + mutable render::operation billboard_op; + float4x4* skinning_palette; + + render::culling_stage* culling_stage; +}; + +} // namespace render + +#endif // ANTKEEPER_RENDER_RENDERER_HPP diff --git a/src/engine/render/shader-template.cpp b/src/engine/render/shader-template.cpp new file mode 100644 index 0000000..1dde337 --- /dev/null +++ b/src/engine/render/shader-template.cpp @@ -0,0 +1,226 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include + +namespace render { + +shader_template::shader_template(const std::string& source_code) +{ + source(source_code); +} + +shader_template::shader_template(): + hash(std::hash{}(std::string())) +{} + +void shader_template::source(const std::string& source) +{ + // Reset template + template_source.clear(); + vertex_directives.clear(); + fragment_directives.clear(); + geometry_directives.clear(); + define_directives.clear(); + + // Iterate through source line-by-line + std::istringstream source_stream(source); + std::string line; + while (std::getline(source_stream, line)) + { + std::string token; + std::istringstream line_stream(line); + + // Detect `#pragma` directives + if (line_stream >> token && token == "#pragma") + { + if (line_stream >> token) + { + // Map line numbers of supported directives + if (token == "define") + { + if (line_stream >> token) + define_directives.insert({token, template_source.size()}); + } + else if (token == "vertex") + vertex_directives.insert(template_source.size()); + else if (token == "fragment") + fragment_directives.insert(template_source.size()); + else if (token == "geometry") + geometry_directives.insert(template_source.size()); + } + } + + // Append line to template source + template_source.push_back(line); + } + + // Calculate hash of source + hash = std::hash{}(source); +} + +std::string shader_template::configure(gl::shader_stage stage, const dictionary_type& definitions) const +{ + replace_stage_directives(stage); + replace_define_directives(definitions); + + // Join vector of source lines into single string + std::ostringstream stream; + std::copy(template_source.begin(), template_source.end(), std::ostream_iterator(stream, "\n")); + return stream.str(); +} + +gl::shader_object* shader_template::compile(gl::shader_stage stage, const dictionary_type& definitions) const +{ + // Generate shader object source + std::string object_source = configure(stage, definitions); + + // Create new shader object + gl::shader_object* object = new gl::shader_object(stage); + + // Set shader object source + object->source(object_source); + + // Compile shader object + object->compile(); + + return object; +} + +gl::shader_program* shader_template::build(const dictionary_type& definitions) const +{ + gl::shader_object* vertex_object = nullptr; + gl::shader_object* fragment_object = nullptr; + gl::shader_object* geometry_object = nullptr; + + // Create shader program + gl::shader_program* program = new gl::shader_program(); + + if (has_vertex_directive()) + { + // Compile vertex shader object and attach to shader program + vertex_object = compile(gl::shader_stage::vertex, definitions); + program->attach(vertex_object); + } + + if (has_fragment_directive()) + { + // Compile fragment shader object and attach to shader program + fragment_object = compile(gl::shader_stage::fragment, definitions); + program->attach(fragment_object); + } + + if (has_geometry_directive()) + { + // Compile fragment shader object and attach to shader program + geometry_object = compile(gl::shader_stage::geometry, definitions); + program->attach(geometry_object); + } + + // Link attached shader objects into shader program + program->link(); + + if (vertex_object) + { + // Detach and delete vertex shader object + program->detach(vertex_object); + delete vertex_object; + } + + if (fragment_object) + { + // Detach and delete fragment shader object + program->detach(fragment_object); + delete fragment_object; + } + + if (geometry_object) + { + // Detach and delete geometry shader object + program->detach(geometry_object); + delete geometry_object; + } + + return program; +} + +void shader_template::replace_stage_directives(gl::shader_stage stage) const +{ + // Determine stage directives according to the shader stage being generated + const std::string vertex_directive = (stage == gl::shader_stage::vertex) ? "#define __VERTEX__" : "/* #undef __VERTEX__ */"; + const std::string fragment_directive = (stage == gl::shader_stage::fragment) ? "#define __FRAGMENT__" : "/* #undef __FRAGMENT__ */"; + const std::string geometry_directive = (stage == gl::shader_stage::geometry) ? "#define __GEOMETRY__" : "/* #undef __GEOMETRY__ */"; + + // Handle `#pragma ` directives + for (std::size_t i: vertex_directives) + template_source[i] = vertex_directive; + for (std::size_t i: fragment_directives) + template_source[i] = fragment_directive; + for (std::size_t i: geometry_directives) + template_source[i] = geometry_directive; +} + +void shader_template::replace_define_directives(const dictionary_type& definitions) const +{ + // For each `#pragma define ` directive + for (const auto& define_directive: define_directives) + { + // Get a reference to the directive line + std::string& line = template_source[define_directive.second]; + + // Check if the corresponding definition was given by the configuration + auto definitions_it = definitions.find(define_directive.first); + if (definitions_it != definitions.end()) + { + // Definition found, replace `#pragma define ` with `#define ` or `#define ` + line = "#define " + define_directive.first; + if (!definitions_it->second.empty()) + line += " " + definitions_it->second; + } + else + { + // Definition not found, replace `#pragma define ` with the comment `/* #undef */`. + line = "/* #undef " + define_directive.first + " */"; + } + } +} + +bool shader_template::has_vertex_directive() const +{ + return !vertex_directives.empty(); +} + +bool shader_template::has_fragment_directive() const +{ + return !fragment_directives.empty(); +} + +bool shader_template::has_geometry_directive() const +{ + return !geometry_directives.empty(); +} + +bool shader_template::has_define_directive(const std::string& key) const +{ + return (define_directives.find(key) != define_directives.end()); +} + +} // namespace render diff --git a/src/engine/render/shader-template.hpp b/src/engine/render/shader-template.hpp new file mode 100644 index 0000000..55daeaf --- /dev/null +++ b/src/engine/render/shader-template.hpp @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_RENDER_SHADER_TEMPLATE_HPP +#define ANTKEEPER_RENDER_SHADER_TEMPLATE_HPP + +#include +#include +#include +#include +#include +#include + +namespace render { + +/** + * Template used to for generating one or more shader variants from a single source. + * + * Shader templates support the following preprocessor directives: + * + * * `#pragma vertex`: Replaced with `#define __VERTEX__` when generating vertex shader objects. + * * `#pragma fragment`: Replaced with `#define __FRAGMENT__` when generating fragment shader objects. + * * `#pragma geometry`: Replaced with `#define __GEOMETRY__` when generating geometry shader objects. + * * `#pragma define `: Will be replaced with `#define ` if its definition is passed to the shader template. + * + * @see gl::shader_stage + * @see gl::shader_object + * @see gl::shader_program + */ +class shader_template +{ +public: + /// Container of definitions used to generate `#pragma define ` directives. + typedef std::unordered_map dictionary_type; + + /** + * Constructs a shader template and sets its source code. + * + * @param source_code String containing the shader template source code. + * + * @see shader_template::source(const std::string&) + */ + shader_template(const std::string& source_code); + + /** + * Constructs an empty shader template. + */ + shader_template(); + + /** + * Replaces the source code of the shader template. + * + * @param source_code String containing shader template source code. + */ + void source(const std::string& source_code); + + /** + * Configures shader object source code given a shader stage and template dictionary. + * + * @param stage Shader stage of the shader object to generate. Instances of `#pragma ` in the template source will be replaced with `#define ____`. + * @param definitions Container of definitions used to replace `#pragma define ` directives. + * @return Configured shader object source code. + */ + std::string configure(gl::shader_stage stage, const dictionary_type& definitions = {}) const; + + /** + * Configures and compiles a shader object. + * + * @param stage Shader stage of the shader object to generate. Instances of `#pragma ` in the template source will be replaced with `#define ____`. + * @param definitions Container of definitions used to replace `#pragma define ` directives. + * @return Compiled shader object. + * + * @exception std::runtime_error Any exceptions thrown by gl::shader_object. + */ + gl::shader_object* compile(gl::shader_stage stage, const dictionary_type& definitions = {}) const; + + /** + * Configures and compiles shader objects, then links them into a shader program. Shader object stages are determined according to the presence of `#pragma ` directives. + * + * @param definitions Container of definitions used to replace `#pragma define ` directives. + * @return Linked shader program. + * + * @exception std::runtime_error Any exceptions thrown by gl::shader_object or gl::shader_program. + * + * @see has_vertex_directive() const + * @see has_fragment_directive() const + * @see has_geometry_directive() const + */ + gl::shader_program* build(const dictionary_type& definitions = {}) const; + + /// Returns `true` if the template source contains one or more `#pragma vertex` directive. + bool has_vertex_directive() const; + + /// Returns `true` if the template source contains one or more `#pragma fragment` directive. + bool has_fragment_directive() const; + + /// Returns `true` if the template source contains one or more `#pragma geometry` directive. + bool has_geometry_directive() const; + + /** + * Returns `true` if the template source contains one or more instance of `#pragma define `. + * + * @param key Definition key. + */ + bool has_define_directive(const std::string& key) const; + + /// Returns a hash of the template source. + std::size_t get_hash() const; + +private: + void replace_stage_directives(gl::shader_stage stage) const; + void replace_define_directives(const dictionary_type& definitions) const; + + mutable std::vector template_source; + std::unordered_set vertex_directives; + std::unordered_set fragment_directives; + std::unordered_set geometry_directives; + std::multimap define_directives; + std::size_t hash; +}; + +inline std::size_t shader_template::get_hash() const +{ + return hash; +} + +} // namespace render + +#endif // ANTKEEPER_RENDER_SHADER_TEMPLATE_HPP diff --git a/src/render/shadow-mode.hpp b/src/engine/render/shadow-mode.hpp similarity index 100% rename from src/render/shadow-mode.hpp rename to src/engine/render/shadow-mode.hpp diff --git a/src/engine/render/stage.cpp b/src/engine/render/stage.cpp new file mode 100644 index 0000000..cf6d1e8 --- /dev/null +++ b/src/engine/render/stage.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include + +namespace render { + +stage::stage(): + priority(0) +{} + +} // namespace render diff --git a/src/engine/render/stage.hpp b/src/engine/render/stage.hpp new file mode 100644 index 0000000..0515390 --- /dev/null +++ b/src/engine/render/stage.hpp @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_RENDER_STAGE_HPP +#define ANTKEEPER_RENDER_STAGE_HPP + +#include + +namespace render { + +/** + * Abstract base class for a single stage in a render pipeline. + */ +class stage +{ +public: + /** + * Constructs a render stage and sets it priority. + * + * @param priority Stage execution order priority. + */ + stage(int priority); + + /// Constructs a render stage. + stage(); + + /// Destructs a render stage. + virtual ~stage() = default; + + /** + * Executes the render stage. + * + * @param ctx Render context. + */ + virtual void execute(render::context& ctx) const = 0; + + /** + * Sets the priority of the stage's execution order in the render pipeline. + * + * @param priority Stage execution order priority. Stages with lower priorities are executed first. + */ + void set_priority(int priority); + + /// Returns the priority of the stage's execution order in the render pipeline. + int get_priority() const noexcept; + +private: + int priority; +}; + +inline int stage::get_priority() const noexcept +{ + return priority; +} + +} // namespace render + +#endif // ANTKEEPER_RENDER_STAGE_HPP diff --git a/src/engine/render/stage/culling-stage.cpp b/src/engine/render/stage/culling-stage.cpp new file mode 100644 index 0000000..0d06213 --- /dev/null +++ b/src/engine/render/stage/culling-stage.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +namespace render { + +void culling_stage::execute(render::context& ctx) const +{ + // Get list of all objects in the collection + const std::list& objects = *(ctx.collection->get_objects()); + + // Get camera culling volume + ctx.camera_culling_volume = ctx.camera->get_culling_mask(); + if (!ctx.camera_culling_volume) + ctx.camera_culling_volume = &ctx.camera->get_world_bounds(); + + // Clear set of visible objects + ctx.visible_objects.clear(); + + // Construct mutex to guard set of visible objects + std::mutex mutex; + + // For each object in the scene collection + std::for_each + ( + std::execution::par, + std::begin(objects), + std::end(objects), + [&](scene::object_base* object) + { + // Ignore inactive objects and cameras + if (!object->is_active() || object->get_object_type_id() == scene::camera::object_type_id) + return; + + // Cull object if it doesn't share any common layers with the camera + //if (!(object->get_layer_mask() & camera_layer_mask)) + // return; + + // Get object culling volume + const geom::bounding_volume* object_culling_volume = object->get_culling_mask(); + if (!object_culling_volume) + object_culling_volume = &object->get_world_bounds(); + + // Cull object if it's outside of the camera culling volume + if (!ctx.camera_culling_volume->intersects(*object_culling_volume)) + return; + + // Insert object into set of visible objects + std::lock_guard guard(mutex); + ctx.visible_objects.push_back(object); + } + ); +} + +} // namespace render diff --git a/src/engine/render/stage/culling-stage.hpp b/src/engine/render/stage/culling-stage.hpp new file mode 100644 index 0000000..68e5a2f --- /dev/null +++ b/src/engine/render/stage/culling-stage.hpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_RENDER_CULLING_STAGE_HPP +#define ANTKEEPER_RENDER_CULLING_STAGE_HPP + +#include + +namespace render { + +/** + * Builds a set of scene objects visible to the current camera and stores it in the render context. + */ +class culling_stage: public stage +{ +public: + /// Constructs a culling stage. + culling_stage() = default; + + /// Destructs a culling stage. + virtual ~culling_stage() = default; + + /// @copydoc render::stage::execute(render::context&) + virtual void execute(render::context& ctx) const final; +}; + +} // namespace render + +#endif // ANTKEEPER_RENDER_CULLING_STAGE_HPP diff --git a/src/render/vertex-attribute.hpp b/src/engine/render/vertex-attribute.hpp similarity index 100% rename from src/render/vertex-attribute.hpp rename to src/engine/render/vertex-attribute.hpp diff --git a/src/engine/resources/deserialize-context.cpp b/src/engine/resources/deserialize-context.cpp new file mode 100644 index 0000000..fcf2bca --- /dev/null +++ b/src/engine/resources/deserialize-context.cpp @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include + +deserialize_context::deserialize_context(void* handle): + handle(handle), + m_eof(false), + m_error(false) +{} + +std::size_t deserialize_context::read8(std::byte* data, std::size_t count) +{ + const PHYSFS_sint64 status = PHYSFS_readBytes(reinterpret_cast(handle), data, count); + + if (status < 0) + { + m_error = true; + throw deserialize_error(PHYSFS_getLastError()); + //return 0; + } + + if (status != count) + { + m_eof = true; + m_error = true; + throw deserialize_error(PHYSFS_getLastError()); + //return static_cast(count); + } + + return count; +} + +std::size_t deserialize_context::read16_le(std::byte* data, std::size_t count) +{ + PHYSFS_File* file = reinterpret_cast(handle); + PHYSFS_uint16* data16 = reinterpret_cast(data); + + for (std::size_t i = 0; i < count; ++i) + { + if (!PHYSFS_readULE16(file, data16)) + { + m_eof = (PHYSFS_eof(file) != 0); + m_error = true; + throw deserialize_error(PHYSFS_getLastError()); + //return i; + } + + ++data16; + } + + return count; +} + +std::size_t deserialize_context::read16_be(std::byte* data, std::size_t count) +{ + PHYSFS_File* file = reinterpret_cast(handle); + PHYSFS_uint16* data16 = reinterpret_cast(data); + + for (std::size_t i = 0; i < count; ++i) + { + if (!PHYSFS_readUBE16(file, data16)) + { + m_eof = (PHYSFS_eof(file) != 0); + m_error = true; + throw deserialize_error(PHYSFS_getLastError()); + //return i; + } + + ++data16; + } + + return count; +} + +std::size_t deserialize_context::read32_le(std::byte* data, std::size_t count) +{ + PHYSFS_File* file = reinterpret_cast(handle); + PHYSFS_uint32* data32 = reinterpret_cast(data); + + for (std::size_t i = 0; i < count; ++i) + { + if (!PHYSFS_readULE32(file, data32)) + { + m_eof = (PHYSFS_eof(file) != 0); + m_error = true; + throw deserialize_error(PHYSFS_getLastError()); + //return i; + } + + ++data32; + } + + return count; +} + +std::size_t deserialize_context::read32_be(std::byte* data, std::size_t count) +{ + PHYSFS_File* file = reinterpret_cast(handle); + PHYSFS_uint32* data32 = reinterpret_cast(data); + + for (std::size_t i = 0; i < count; ++i) + { + if (!PHYSFS_readUBE32(file, data32)) + { + m_eof = (PHYSFS_eof(file) != 0); + m_error = true; + throw deserialize_error(PHYSFS_getLastError()); + //return i; + } + + ++data32; + } + + return count; +} + +std::size_t deserialize_context::read64_le(std::byte* data, std::size_t count) +{ + PHYSFS_File* file = reinterpret_cast(handle); + PHYSFS_uint64* data64 = reinterpret_cast(data); + + for (std::size_t i = 0; i < count; ++i) + { + if (!PHYSFS_readULE64(file, data64)) + { + m_eof = (PHYSFS_eof(file) != 0); + m_error = true; + throw deserialize_error(PHYSFS_getLastError()); + //return i; + } + + ++data64; + } + + return count; +} + +std::size_t deserialize_context::read64_be(std::byte* data, std::size_t count) +{ + PHYSFS_File* file = reinterpret_cast(handle); + PHYSFS_uint64* data64 = reinterpret_cast(data); + + for (std::size_t i = 0; i < count; ++i) + { + if (!PHYSFS_readUBE64(file, data64)) + { + m_eof = (PHYSFS_eof(file) != 0); + m_error = true; + throw deserialize_error(PHYSFS_getLastError()); + //return i; + } + + ++data64; + } + + return count; +} diff --git a/src/resources/deserialize-context.hpp b/src/engine/resources/deserialize-context.hpp similarity index 100% rename from src/resources/deserialize-context.hpp rename to src/engine/resources/deserialize-context.hpp diff --git a/src/resources/deserialize-error.hpp b/src/engine/resources/deserialize-error.hpp similarity index 100% rename from src/resources/deserialize-error.hpp rename to src/engine/resources/deserialize-error.hpp diff --git a/src/engine/resources/deserializer.cpp b/src/engine/resources/deserializer.cpp new file mode 100644 index 0000000..ff458d2 --- /dev/null +++ b/src/engine/resources/deserializer.cpp @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include + +template <> +void deserializer::deserialize(bool& value, deserialize_context& ctx) +{ + std::uint8_t temp; + ctx.read8(reinterpret_cast(&temp), 1); + value = (temp) ? true : false; +}; + +template <> +void deserializer::deserialize(std::uint8_t& value, deserialize_context& ctx) +{ + ctx.read8(reinterpret_cast(&value), 1); +}; + +template <> +void deserializer::deserialize(std::uint16_t& value, deserialize_context& ctx) +{ + ctx.read16(reinterpret_cast(&value), 1); +}; + +template <> +void deserializer::deserialize(std::uint32_t& value, deserialize_context& ctx) +{ + ctx.read32(reinterpret_cast(&value), 1); +}; + +template <> +void deserializer::deserialize(std::uint64_t& value, deserialize_context& ctx) +{ + ctx.read64(reinterpret_cast(&value), 1); +}; + +template <> +void deserializer::deserialize(std::int8_t& value, deserialize_context& ctx) +{ + ctx.read8(reinterpret_cast(&value), 1); +}; + +template <> +void deserializer::deserialize(std::int16_t& value, deserialize_context& ctx) +{ + ctx.read16(reinterpret_cast(&value), 1); +}; + +template <> +void deserializer::deserialize(std::int32_t& value, deserialize_context& ctx) +{ + ctx.read32(reinterpret_cast(&value), 1); +}; + +template <> +void deserializer::deserialize(std::int64_t& value, deserialize_context& ctx) +{ + ctx.read64(reinterpret_cast(&value), 1); +}; + +template <> +void deserializer::deserialize(float& value, deserialize_context& ctx) +{ + ctx.read32(reinterpret_cast(&value), 1); +}; + +template <> +void deserializer::deserialize(double& value, deserialize_context& ctx) +{ + ctx.read64(reinterpret_cast(&value), 1); +}; + +template <> +void deserializer::deserialize(std::string& value, deserialize_context& ctx) +{ + std::uint64_t length = 0; + ctx.read64(reinterpret_cast(&length), 1); + value.resize(static_cast(length)); + ctx.read8(reinterpret_cast(value.data()), static_cast(length)); +}; + +template <> +void deserializer::deserialize(std::u8string& value, deserialize_context& ctx) +{ + std::uint64_t length = 0; + ctx.read64(reinterpret_cast(&length), 1); + value.resize(static_cast(length)); + ctx.read8(reinterpret_cast(value.data()), static_cast(length)); +}; + +template <> +void deserializer::deserialize(std::u16string& value, deserialize_context& ctx) +{ + std::uint64_t length = 0; + ctx.read64(reinterpret_cast(&length), 1); + value.resize(static_cast(length)); + ctx.read16(reinterpret_cast(value.data()), static_cast(length)); +}; + +template <> +void deserializer::deserialize(std::u32string& value, deserialize_context& ctx) +{ + std::uint64_t length = 0; + ctx.read64(reinterpret_cast(&length), 1); + value.resize(static_cast(length)); + ctx.read32(reinterpret_cast(value.data()), static_cast(length)); +}; diff --git a/src/engine/resources/deserializer.hpp b/src/engine/resources/deserializer.hpp new file mode 100644 index 0000000..d95db65 --- /dev/null +++ b/src/engine/resources/deserializer.hpp @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_RESOURCES_DESERIALIZER_HPP +#define ANTKEEPER_RESOURCES_DESERIALIZER_HPP + +#include + +/** + * Specializations of deserializer define the deserialization process for a given type. + * + * @tparam T Deserializable type. + */ +template +struct deserializer +{ + /** + * Deserializes a value. + * + * @param value Value to deserialize. + * @param ctx Deserialize context. + */ + void deserialize(T& value, deserialize_context& ctx); +}; + +#endif // ANTKEEPER_RESOURCES_DESERIALIZER_HPP diff --git a/src/engine/resources/dict-loader.cpp b/src/engine/resources/dict-loader.cpp new file mode 100644 index 0000000..2585ee0 --- /dev/null +++ b/src/engine/resources/dict-loader.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +template <> +dict* resource_loader>::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +{ + dict* dict = new ::dict(); + + deserialize_context ctx(file); + deserializer<::dict>().deserialize(*dict, ctx); + + return dict; +} + +template <> +void resource_loader>::save(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path, const dict* dict) +{ + serialize_context ctx(file); + serializer<::dict>().serialize(*dict, ctx); +} diff --git a/src/engine/resources/ephemeris-loader.cpp b/src/engine/resources/ephemeris-loader.cpp new file mode 100644 index 0000000..23c0530 --- /dev/null +++ b/src/engine/resources/ephemeris-loader.cpp @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include +#include +#include + +/// Offset to time data in the JPL DE header, in bytes. +static constexpr std::size_t jpl_de_offset_time = 0xA5C; + +/// Offset to the first coefficient table in the JPL DE header, in bytes. +static constexpr std::size_t jpl_de_offset_table1 = 0xA88; + +/// Offset to the DE version number in the JPL DE header, in bytes. +static constexpr std::size_t jpl_de_offset_denum = 0xB18; + +/// Offset to the second coefficient table in the JPL DE header, in bytes. +static constexpr std::size_t jpl_de_offset_table2 = 0xB1C; + +/// Offset to the third coefficient table in the JPL DE header, in bytes, if the constant limit has not been exceeded. +static constexpr std::size_t jpl_de_offset_table3 = 0xB28; + +/// Mask to detect bytes in the most significant word of the JPL DE version number. +static constexpr std::int32_t jpl_de_denum_endian_mask = 0xFFFF0000; + +/// Number of items in the first coefficient table. +static constexpr std::size_t jpl_de_table1_count = 12; + +/// Number of items in the second coefficient table. +static constexpr std::size_t jpl_de_table2_count = 1; + +/// Number of items in the third coefficient table. +static constexpr std::size_t jpl_de_table3_count = 2; + +/// Maximum number of items in a JPL DE file. +static constexpr std::size_t jpl_de_max_item_count = jpl_de_table1_count + jpl_de_table2_count + jpl_de_table3_count; + +/// Maximum number of constants in the first set of constant names. +static constexpr std::size_t jpl_de_constant_limit = 400; + +/// Length of a constant name, in bytes. +static constexpr std::size_t jpl_de_constant_length = 6; + +/// Enumerated IDs of the JPL DE items. +enum +{ + /// Mercury + jpl_de_id_mercury, + + /// Venus + jpl_de_id_venus, + + /// Earth-Moon barycenter + jpl_de_id_embary, + + /// Mars + jpl_de_id_mars, + + /// Jupiter + jpl_de_id_jupiter, + + /// Saturn + jpl_de_id_saturn, + + /// Uranus + jpl_de_id_uranus, + + /// Neptune + jpl_de_id_neptune, + + /// Pluto + jpl_de_id_pluto, + + /// Moon + jpl_de_id_moon, + + /// Sun + jpl_de_id_sun, + + /// Earth nutation + jpl_de_id_earth_nutation, + + /// Lunar mantle libration + jpl_de_id_luma_libration, + + /// Lunar mantle angular velocity + jpl_de_id_luma_angular_velocity, + + /// TT-TDB + jpl_de_id_tt_tdb +}; + +/// Number of components for each JPL DE item. +static constexpr std::size_t jpl_de_component_count[jpl_de_max_item_count] = +{ + 3, // Mercury: x,y,z (km) + 3, // Venus: x,y,z (km) + 3, // Earth-Moon barycenter: x,y,z (km) + 3, // Mars: x,y,z (km) + 3, // Jupiter: x,y,z (km) + 3, // Saturn: x,y,z (km) + 3, // Uranus: x,y,z (km) + 3, // Neptune: x,y,z (km) + 3, // Pluto: x,y,z (km) + 3, // Moon: x,y,z (km) + 3, // Sun: x,y,z (km) + 2, // Earth nutation: d_psi,d_epsilon (radians) + 3, // Lunar mantle libration: phi,theta,ps (radians) + 3, // Lunar mantle angular velocity: omega_x,omega_y,omega_z (radians/day) + 1 // TT-TDB: t (seconds) +}; + +/// Reads and swaps the byte order of 32-bit numbers. +static PHYSFS_sint64 read_swap32(PHYSFS_File* handle, void* buffer, PHYSFS_uint64 len) +{ + PHYSFS_sint64 status = PHYSFS_readBytes(handle, buffer, len); + for (std::uint32_t* ptr32 = (uint32_t*)buffer; len >= 8; len -= 8, ++ptr32) + *ptr32 = bit::swap32(*ptr32); + return status; +} + +/// Reads and swaps the byte order of 64-bit numbers. +static PHYSFS_sint64 read_swap64(PHYSFS_File* handle, void* buffer, PHYSFS_uint64 len) +{ + PHYSFS_sint64 status = PHYSFS_readBytes(handle, buffer, len); + for (std::uint64_t* ptr64 = (uint64_t*)buffer; len >= 8; len -= 8, ++ptr64) + *ptr64 = bit::swap64(*ptr64); + return status; +} + +template <> +physics::orbit::ephemeris* resource_loader>::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +{ + // Init file reading function pointers + PHYSFS_sint64 (*read32)(PHYSFS_File*, void*, PHYSFS_uint64) = &PHYSFS_readBytes; + PHYSFS_sint64 (*read64)(PHYSFS_File*, void*, PHYSFS_uint64) = &PHYSFS_readBytes; + + // Read DE version number + std::int32_t denum; + PHYSFS_seek(file, jpl_de_offset_denum); + PHYSFS_readBytes(file, &denum, sizeof(std::int32_t)); + + // If file endianness does not match host + if (denum & jpl_de_denum_endian_mask) + { + // Use endian-swapping read functions + read32 = &read_swap32; + read64 = &read_swap64; + } + + // Read ephemeris time + double ephemeris_time[3]; + PHYSFS_seek(file, jpl_de_offset_time); + read64(file, ephemeris_time, sizeof(double) * 3); + + // Make time relative to J2000 epoch + const double epoch = 2451545.0; + ephemeris_time[0] -= epoch; + ephemeris_time[1] -= epoch; + + // Read number of constants + std::int32_t constant_count; + read32(file, &constant_count, sizeof(std::int32_t)); + + // Read first coefficient table + std::int32_t coeff_table[jpl_de_max_item_count][3]; + PHYSFS_seek(file, jpl_de_offset_table1); + read32(file, coeff_table, sizeof(std::int32_t) * jpl_de_table1_count * 3); + + // Read second coefficient table + PHYSFS_seek(file, jpl_de_offset_table2); + read32(file, &coeff_table[jpl_de_table1_count][0], sizeof(std::int32_t) * jpl_de_table2_count * 3); + + // Seek past extra constant names + if (constant_count > jpl_de_constant_limit) + PHYSFS_seek(file, jpl_de_offset_table3 + (constant_count - jpl_de_constant_limit) * jpl_de_constant_length); + + // Read third coefficient table + read32(file, &coeff_table[jpl_de_table1_count + jpl_de_table2_count][0], sizeof(std::int32_t) * jpl_de_table3_count * 3); + + // Calculate number of coefficients per record + std::int32_t record_coeff_count = 0; + for (int i = 0; i < jpl_de_max_item_count; ++i) + { + std::int32_t coeff_count = coeff_table[i][0] + coeff_table[i][1] * coeff_table[i][2] * jpl_de_component_count[i] - 1; + record_coeff_count = std::max(record_coeff_count, coeff_count); + } + + // Calculate record size and record count + std::size_t record_size = record_coeff_count * sizeof(double); + std::size_t record_count = static_cast((ephemeris_time[1] - ephemeris_time[0]) / ephemeris_time[2]); + + // Calculate coefficient strides + std::size_t strides[11]; + for (int i = 0; i < 11; ++i) + strides[i] = coeff_table[i][2] * coeff_table[i][1] * 3; + + // Allocate and resize ephemeris to accommodate items 0-10 + physics::orbit::ephemeris* ephemeris = new physics::orbit::ephemeris(); + ephemeris->resize(11); + + // Init trajectories + for (int i = 0; i < 11; ++i) + { + auto& trajectory = (*ephemeris)[i]; + trajectory.t0 = ephemeris_time[0]; + trajectory.t1 = ephemeris_time[1]; + trajectory.dt = ephemeris_time[2] / static_cast(coeff_table[i][2]); + trajectory.n = coeff_table[i][1]; + trajectory.a.resize(record_count * strides[i]); + } + + // Read coefficients + for (std::size_t i = 0; i < record_count; ++i) + { + // Seek to coefficient record + PHYSFS_seek(file, (i + 2) * record_size + 2 * sizeof(double)); + + for (int j = 0; j < 11; ++j) + { + read64(file, &(*ephemeris)[j].a[i * strides[j]], sizeof(double) * strides[j]); + } + } + + return ephemeris; +} diff --git a/src/engine/resources/file-buffer-loader.cpp b/src/engine/resources/file-buffer-loader.cpp new file mode 100644 index 0000000..4fa2dba --- /dev/null +++ b/src/engine/resources/file-buffer-loader.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include + +template <> +file_buffer* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +{ + file_buffer* buffer = new file_buffer(); + + // Read file into buffer + std::size_t size = static_cast(PHYSFS_fileLength(file)); + buffer->resize(size); + PHYSFS_readBytes(file, buffer->data(), size); + + return buffer; +} diff --git a/src/resources/file-buffer.hpp b/src/engine/resources/file-buffer.hpp similarity index 100% rename from src/resources/file-buffer.hpp rename to src/engine/resources/file-buffer.hpp diff --git a/src/engine/resources/image-loader.cpp b/src/engine/resources/image-loader.cpp new file mode 100644 index 0000000..22442f7 --- /dev/null +++ b/src/engine/resources/image-loader.cpp @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +template <> +image* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +{ + unsigned char* buffer; + int size; + ::image* image = nullptr; + + // Read input stream into buffer + size = static_cast(PHYSFS_fileLength(file)); + buffer = new unsigned char[size]; + PHYSFS_readBytes(file, buffer, size); + + // Select loader according to file extension + if (path.extension() == ".exr") + { + // Load OpenEXR with TinyEXR + int status = TINYEXR_SUCCESS; + const char* error = nullptr; + + // Read EXR version + EXRVersion exr_version; + status = ParseEXRVersionFromMemory(&exr_version, buffer, size); + if (status != TINYEXR_SUCCESS) + { + delete[] buffer; + throw std::runtime_error("TinyEXR parse version error (" + std::to_string(status) + "): invalid EXR file"); + } + + // Check if image is multipart + if (exr_version.multipart) + { + throw std::runtime_error("OpenEXR multipart images not supported"); + } + + // Read EXR header + EXRHeader exr_header; + InitEXRHeader(&exr_header); + status = ParseEXRHeaderFromMemory(&exr_header, &exr_version, buffer, size, &error); + if (status != TINYEXR_SUCCESS) + { + std::string error_string(error); + FreeEXRErrorMessage(error); + delete[] buffer; + throw std::runtime_error("TinyEXR parse header error (" + std::to_string(status) + "): " + error_string); + } + + // Check if image is tiled + if (exr_header.tiled) + { + FreeEXRHeader(&exr_header); + delete[] buffer; + throw std::runtime_error("OpenEXR tiled images not supported"); + } + + // Read half channels as float + for (int i = 0; i < exr_header.num_channels; ++i) + { + if (exr_header.pixel_types[i] == TINYEXR_PIXELTYPE_HALF) + { + exr_header.requested_pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT; + } + } + + // Read EXR data + EXRImage exr_image; + InitEXRImage(&exr_image); + status = LoadEXRImageFromMemory(&exr_image, &exr_header, buffer, size, &error); + if (status != TINYEXR_SUCCESS) + { + std::string error_string(error); + FreeEXRErrorMessage(error); + FreeEXRHeader(&exr_header); + delete[] buffer; + throw std::runtime_error("TinyEXR load error (" + std::to_string(status) + "): " + error_string); + } + + // Free file buffer + delete[] buffer; + + // Create image + image = new ::image(); + image->format(sizeof(float), exr_image.num_channels); + image->resize(static_cast(exr_image.width), static_cast(exr_image.height)); + + // Fill image pixels + float* component = static_cast(image->data()); + for (int y = exr_image.height - 1; y >= 0; --y) + { + int row_offset = y * exr_image.width; + + for (int x = 0; x < exr_image.width; ++x) + { + int pixel_index = row_offset + x; + + for (int c = exr_image.num_channels - 1; c >= 0; --c) + { + *(component++) = reinterpret_cast(exr_image.images)[c][pixel_index]; + } + } + } + + // Free EXR data + FreeEXRImage(&exr_image); + FreeEXRHeader(&exr_header); + } + else + { + // Load all other formats with stb_image + + // Set vertical flip on load in order to upload pixels correctly to OpenGL + stbi_set_flip_vertically_on_load(true); + + // Load image data + void* pixels = nullptr; + int width = 0; + int height = 0; + int channels = 0; + pixels = stbi_load_from_memory(buffer, size, &width, &height, &channels, 0); + + // Free file buffer + delete[] buffer; + + // Check if image was loaded + if (!pixels) + { + throw std::runtime_error("STBI failed to load image from memory."); + } + + // Create image + std::size_t component_size = sizeof(unsigned char); + image = new ::image(); + image->format(component_size, channels); + image->resize(static_cast(width), static_cast(height)); + std::memcpy(image->data(), pixels, image->get_size()); + + // Free loaded image data + stbi_image_free(pixels); + } + + return image; +} + diff --git a/src/engine/resources/image.cpp b/src/engine/resources/image.cpp new file mode 100644 index 0000000..8b660ec --- /dev/null +++ b/src/engine/resources/image.cpp @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include + +image::image(const image& source) +{ + *this = source; +} + +image::image(): + width(0), + height(0), + component_size(0), + channel_count(0), + pixel_size(0), + pixels(nullptr), + size(0) +{} + +image::~image() +{ + free_pixels(); +} + +image& image::operator=(const image& source) +{ + format(source.component_size, source.channel_count); + resize(source.width, source.height); + std::memcpy(pixels, source.pixels, size); + + return *this; +} + +bool image::compatible(const image& other) const +{ + return (other.component_size == component_size && other.channel_count == channel_count); +} + +void image::copy(const image& source, unsigned int w, unsigned int h, unsigned int from_x, int unsigned from_y, unsigned int to_x, unsigned int to_y) +{ + if (!compatible(source)) + { + throw std::runtime_error("Cannot copy image with mismatched format"); + } + + const unsigned char* from_pixels = static_cast(source.pixels); + unsigned char* to_pixels = static_cast(pixels); + + for (unsigned int i = 0; i < h; ++i) + { + // Calculate vertical pixel offset + unsigned int from_i = from_y + i; + unsigned int to_i = to_y + i; + + // Bounds check + if (from_i >= source.height || to_i >= height) + break; + + for (unsigned int j = 0; j < w; ++j) + { + // Calculate horizontal pixel offsets + unsigned int from_j = from_x + j; + unsigned int to_j = to_x + j; + + // Bounds check + if (from_j >= source.width || to_j >= width) + continue; + + // Calculate pixel data offset (in bytes) + std::size_t from_offset = (from_i * source.width + from_j) * pixel_size; + std::size_t to_offset = (to_i * width + to_j) * pixel_size; + + // Copy single pixel + std::memcpy(to_pixels + to_offset, from_pixels + from_offset, pixel_size); + } + } +} + +void image::format(std::size_t component_size, std::size_t channel_count) +{ + if (this->component_size == component_size && this->channel_count == channel_count) + return; + + free_pixels(); + this->component_size = component_size; + this->channel_count = channel_count; + pixel_size = component_size * channel_count; + size = width * height * pixel_size; + allocate_pixels(); +} + +void image::resize(unsigned int width, unsigned int height) +{ + if (this->width == width && this->height == height) + { + return; + } + + free_pixels(); + this->width = width; + this->height = height; + size = width * height * pixel_size; + allocate_pixels(); +} + +void image::allocate_pixels() +{ + if (size != 0) + { + pixels = new unsigned char[size]; + } +} + +void image::free_pixels() +{ + if (pixels != nullptr) + { + delete[] reinterpret_cast(pixels); + pixels = nullptr; + size = 0; + } +} diff --git a/src/engine/resources/image.hpp b/src/engine/resources/image.hpp new file mode 100644 index 0000000..1c2e470 --- /dev/null +++ b/src/engine/resources/image.hpp @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_IMAGE_HPP +#define ANTKEEPER_IMAGE_HPP + +#include +#include +#include + +/** + * Stores basic image data. + */ +class image +{ +public: + /** + * Creates a copy of another image. + * + * @param source Image from which to copy. + */ + image(const image& source); + + /// Creates an image. + image(); + + /// Destroys an image. + ~image(); + + /** + * Makes this image a copy of another image. + * + * @param source Image from which to copy. + */ + image& operator=(const image& source); + + /** + * Returns an iterator to the first pixel. + * + * @tparam T Pixel data type. + */ + /// @{ + template + inline constexpr T* begin() noexcept + { + static_assert(std::is_standard_layout::value, "Pixel iterator type is not standard-layout."); + static_assert(std::is_trivial::value, "Pixel iterator type is not trivial."); + return static_cast(pixels); + } + template + inline constexpr const T* begin() const noexcept + { + static_assert(std::is_standard_layout::value, "Pixel iterator type is not standard-layout."); + static_assert(std::is_trivial::value, "Pixel iterator type is not trivial."); + return static_cast(pixels); + } + template + inline constexpr const T* cbegin() const noexcept + { + static_assert(std::is_standard_layout::value, "Pixel iterator type is not standard-layout."); + static_assert(std::is_trivial::value, "Pixel iterator type is not trivial."); + return static_cast(pixels); + } + /// @} + + /** + * Returns an iterator to the pixel following the last pixel. + * + * @tparam T Pixel data type. + */ + /// @{ + template + inline constexpr T* end() noexcept + { + static_assert(std::is_standard_layout::value, "Pixel iterator type is not standard-layout."); + static_assert(std::is_trivial::value, "Pixel iterator type is not trivial."); + return reinterpret_cast(static_cast(pixels) + size); + } + template + inline constexpr const T* end() const noexcept + { + static_assert(std::is_standard_layout::value, "Pixel iterator type is not standard-layout."); + static_assert(std::is_trivial::value, "Pixel iterator type is not trivial."); + return reinterpret_cast(static_cast(pixels) + size); + } + template + inline constexpr const T* cend() const noexcept + { + static_assert(std::is_standard_layout::value, "Pixel iterator type is not standard-layout."); + static_assert(std::is_trivial::value, "Pixel iterator type is not trivial."); + return reinterpret_cast(static_cast(pixels) + size); + } + /// @} + + /** + * Checks whether another image has the same number of channels and pixel size as this image. + * + * @param other Image for with which to compare compatibility. + * @return `true` if the image formats are compatible, `false` otherwise. + */ + bool compatible(const image& other) const; + + /** + * Copies pixel data from another image with a compatible format into this image. + * + * @param source Source image from which to copy pixel data. + * @param w Width of the subimage to copy. + * @param h Height of the subimage to copy. + * @param from_x X-coordinate of the first pixel to copy from the source subimage. + * @param from_y Y-coordinate of the first pixel to copy from the source subimage. + * @param to_x X-coordinate of the first pixel in the destination subimage. + * @param to_y Y-coordinate of the first pixel in the destination subimage. + * + * @except std::runtime_error Cannot copy image with mismatched format. + * + * @see image::compatible(const image&) const + */ + void copy(const image& source, unsigned int w, unsigned int h, unsigned int from_x = 0, int unsigned from_y = 0, unsigned int to_x = 0, unsigned int to_y = 0); + + /** + * Changes the format of the image. Existing pixel data will be erased if the format has changed. + * + * @param component_size Size of channel components, in bytes. + * @param channel_count Number of channels in the image. + */ + void format(std::size_t component_size, std::size_t channel_count); + + /** + * Resizes the image. Existing pixel data will be erased if the size has changed. + * + * @param width New width of the image, in pixels. + * @param height New height of the image, in pixels. + */ + void resize(unsigned int width, unsigned int height); + + /// Returns the width of the image, in pixels. + unsigned int get_width() const; + + /// Returns the height of the image, in pixels. + unsigned int get_height() const; + + /// Returns the size of channel components, in bytes. + std::size_t get_component_size() const; + + /// Returns the number of color channels in the image. + std::size_t get_channel_count() const; + + /// Returns a pointer to the pixel data. + /// @{ + void* data() noexcept; + const void* data() const noexcept; + /// @} + + /// Returns the size of a single pixel, in bytes. + std::size_t get_pixel_size() const; + + /// Returns the size of the image, in bytes. + std::size_t get_size() const; + +private: + void allocate_pixels(); + void free_pixels(); + + unsigned int width; + unsigned int height; + std::size_t component_size; + std::size_t channel_count; + void* pixels; + std::size_t pixel_size; + std::size_t size; +}; + +inline unsigned int image::get_width() const +{ + return width; +} + +inline unsigned int image::get_height() const +{ + return height; +} + +inline std::size_t image::get_component_size() const +{ + return component_size; +} + +inline std::size_t image::get_channel_count() const +{ + return channel_count; +} + +inline void* image::data() noexcept +{ + return pixels; +} + +inline const void* image::data() const noexcept +{ + return pixels; +} + +inline std::size_t image::get_pixel_size() const +{ + return pixel_size; +} + +inline std::size_t image::get_size() const +{ + return size; +} + +#endif // ANTKEEPER_IMAGE_HPP diff --git a/src/engine/resources/json-loader.cpp b/src/engine/resources/json-loader.cpp new file mode 100644 index 0000000..2f09d44 --- /dev/null +++ b/src/engine/resources/json-loader.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include +#include + +template <> +json* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +{ + // Read file into buffer + std::size_t size = static_cast(PHYSFS_fileLength(file)); + std::string buffer; + buffer.resize(size); + PHYSFS_readBytes(file, &buffer[0], size); + + // Parse json from file buffer + json* data = new json(json::parse(buffer, nullptr, true, true)); + + return data; +} diff --git a/src/resources/json.hpp b/src/engine/resources/json.hpp similarity index 100% rename from src/resources/json.hpp rename to src/engine/resources/json.hpp diff --git a/src/engine/resources/material-loader.cpp b/src/engine/resources/material-loader.cpp new file mode 100644 index 0000000..93f1505 --- /dev/null +++ b/src/engine/resources/material-loader.cpp @@ -0,0 +1,457 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +template +static bool read_value(T* value, const nlohmann::json& json, const std::string& name) +{ + if (auto element = json.find(name); element != json.end()) + { + *value = element.value().get(); + return true; + } + + return false; +} + +static bool load_texture_1d_property(resource_manager* resource_manager, render::material* material, const std::string& name, const nlohmann::json& json) +{ + // If JSON element is an array + if (json.is_array()) + { + // Determine size of the array + std::size_t array_size = json.size(); + + // Create property + render::material_property* property = material->add_property(name, array_size); + + // Load textures + std::size_t i = 0; + for (const auto& element: json) + { + std::string filename = element.get(); + const gl::texture_1d* texture = resource_manager->load(filename); + property->set_value(i++, texture); + } + } + else + { + // Create property + render::material_property* property = material->add_property(name); + + // Load texture + std::string filename = json.get(); + const gl::texture_1d* texture = resource_manager->load(filename); + property->set_value(texture); + } + + return true; +} + +static bool load_texture_2d_property(resource_manager* resource_manager, render::material* material, const std::string& name, const nlohmann::json& json) +{ + // If JSON element is an array + if (json.is_array()) + { + // Determine size of the array + std::size_t array_size = json.size(); + + // Create property + render::material_property* property = material->add_property(name, array_size); + + // Load textures + std::size_t i = 0; + for (const auto& element: json) + { + std::string filename = element.get(); + const gl::texture_2d* texture = resource_manager->load(filename); + property->set_value(i++, texture); + } + } + else + { + // Create property + render::material_property* property = material->add_property(name); + + // Load texture + std::string filename = json.get(); + const gl::texture_2d* texture = resource_manager->load(filename); + property->set_value(texture); + } + + return true; +} + +static bool load_texture_cube_property(resource_manager* resource_manager, render::material* material, const std::string& name, const nlohmann::json& json) +{ + return false; +} + +template +static bool load_scalar_property(render::material* material, const std::string& name, const nlohmann::json& json) +{ + // If JSON element is an array + if (json.is_array()) + { + // Determine size of the array + std::size_t array_size = json.size(); + + // Create property + render::material_property* property = material->add_property(name, array_size); + + // Set property values + std::size_t i = 0; + for (const auto& element: json) + property->set_value(i++, element.get()); + } + else + { + // Create property + render::material_property* property = material->add_property(name); + + // Set property value + property->set_value(json.get()); + } + + return true; +} + +template +static bool load_vector_property(render::material* material, const std::string& name, std::size_t vector_size, const nlohmann::json& json) +{ + // If JSON element is an array of arrays + if (json.is_array() && json.begin().value().is_array()) + { + // Determine size of the array + std::size_t array_size = json.size(); + + // Create property + render::material_property* property = material->add_property(name, array_size); + + // For each vector in the array + std::size_t i = 0; + for (const auto& vector_element: json) + { + // Read vector elements + T value; + std::size_t j = 0; + for (const auto& value_element: vector_element) + value[j++] = value_element.get(); + + // Set property values + property->set_value(i++, value); + } + } + else + { + // Create property + render::material_property* property = material->add_property(name); + + // Read vector elements + T value; + std::size_t i = 0; + for (const auto& value_element: json) + value[i++] = value_element.get(); + + // Set property values + property->set_value(value); + } + + return true; +} + +template +static bool load_matrix_property(render::material* material, const std::string& name, std::size_t column_count, std::size_t row_count, const nlohmann::json& json) +{ + // If JSON element is an array of arrays of arrays + if (json.is_array() && json.begin().value().is_array()) + { + if (json.begin().value().begin().value().is_array()) + { + // Determine size of the array + std::size_t array_size = json.size(); + + // Create property + render::material_property* property = material->add_property(name, array_size); + + // For each matrix in the array + std::size_t i = 0; + for (const auto& matrix_element: json) + { + // Read vector elements + T value; + std::size_t j = 0; + for (const auto& column_element: matrix_element) + { + std::size_t k = 0; + for (const auto& row_element: column_element) + { + value[j][k] = row_element.get(); + ++k; + } + + ++j; + } + + // Set property values + property->set_value(i, value); + + ++i; + } + + return true; + } + else + { + // Create property + render::material_property* property = material->add_property(name); + + // Read matrix elements + T value; + std::size_t i = 0; + for (const auto& column_element: json) + { + std::size_t j = 0; + for (const auto& row_element: column_element) + { + value[i][j] = row_element.get(); + ++j; + } + + ++i; + } + + // Set property values + property->set_value(value); + + return true; + } + } + + return false; +} + +template <> +render::material* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +{ + // Read file into buffer + std::size_t size = static_cast(PHYSFS_fileLength(file)); + std::string buffer; + buffer.resize(size); + PHYSFS_readBytes(file, &buffer[0], size); + + // Parse json from file buffer + nlohmann::json json = nlohmann::json::parse(buffer, nullptr, true, true); + + // Allocate material + render::material* material = new render::material(); + + // Read shader filename + std::string shader_filename; + if (read_value(&shader_filename, json, "shader")) + { + // Load shader program + gl::shader_program* program = resource_manager->load(shader_filename); + material->set_shader_program(program); + } + + // Init material flags + std::uint32_t flags = 0; + + // Read blend mode + std::string blend_mode; + read_value(&blend_mode, json, "blend_mode"); + if (blend_mode == "opaque") + { + material->set_blend_mode(render::blend_mode::opaque); + } + else if (blend_mode == "masked") + { + material->set_blend_mode(render::blend_mode::masked); + } + else if (blend_mode == "translucent") + { + material->set_blend_mode(render::blend_mode::translucent); + } + + // Read opacity threshold + float opacity_threshold = 0.5f; + if (read_value(&opacity_threshold, json, "opacity_threshold")) + { + material->set_opacity_threshold(opacity_threshold); + } + + // Read two sided + bool two_sided = false; + read_value(&two_sided, json, "two_sided"); + material->set_two_sided(two_sided); + + // Read shadow mode + std::string shadow_mode; + read_value(&shadow_mode, json, "shadow_mode"); + if (shadow_mode == "opaque") + { + material->set_shadow_mode(render::shadow_mode::opaque); + } + else if (shadow_mode == "none") + { + material->set_shadow_mode(render::shadow_mode::none); + } + + // Read depth mode + std::string depth_mode; + read_value(&depth_mode, json, "depth_mode"); + if (depth_mode == "in_front") + flags |= MATERIAL_FLAG_X_RAY; + + // Read decal mode + std::string decal_mode; + read_value(&decal_mode, json, "decal_mode"); + if (decal_mode == "decal") + flags |= MATERIAL_FLAG_DECAL; + else if (decal_mode == "surface") + flags |= MATERIAL_FLAG_DECAL_SURFACE; + + // Set material flags + material->set_flags(flags); + + // Read material properties + if (auto properties_element = json.find("properties"); properties_element != json.end()) + { + for (const auto& property_element: properties_element.value()) + { + // Read property name + std::string name; + if (!read_value(&name, property_element, "name")) + // Ignore nameless properties + continue; + + // Read property type + std::string type; + if (!read_value(&type, property_element, "type")) + // Ignore typeless properties + continue; + + // Find value element + auto value_element = property_element.find("value"); + if (value_element == property_element.end()) + // Ignore valueless properties + continue; + + if (type == "texture_1d") + { + load_texture_1d_property(resource_manager, material, name, value_element.value()); + } + else if (type == "texture_2d") + { + load_texture_2d_property(resource_manager, material, name, value_element.value()); + } + else if (type == "texture_cube") + { + load_texture_cube_property(resource_manager, material, name, value_element.value()); + } + // If property type is a matrix + else if (type[type.size() - 2] == 'x' && + std::isdigit(type[type.size() - 3]) && + std::isdigit(type.back())) + { + std::size_t columns = std::stoul(type.substr(type.size() - 3, 1)); + std::size_t rows = std::stoul(type.substr(type.size() - 1, 1)); + + if (type.find("float") != std::string::npos) + { + if (size == 2) + load_matrix_property(material, name, columns, rows, value_element.value()); + else if (size == 3) + load_matrix_property(material, name, columns, rows, value_element.value()); + else if (size == 4) + load_matrix_property(material, name, columns, rows, value_element.value()); + } + } + // If property type is a vector + else if (std::isdigit(type.back())) + { + std::size_t size = std::stoul(type.substr(type.size() - 1, 1)); + + if (type.find("float") != std::string::npos) + { + if (size == 2) + load_vector_property(material, name, size, value_element.value()); + else if (size == 3) + load_vector_property(material, name, size, value_element.value()); + else if (size == 4) + load_vector_property(material, name, size, value_element.value()); + } + else if (type.find("uint") != std::string::npos) + { + if (size == 2) + load_vector_property(material, name, size, value_element.value()); + else if (size == 3) + load_vector_property(material, name, size, value_element.value()); + else if (size == 4) + load_vector_property(material, name, size, value_element.value()); + } + else if (type.find("int") != std::string::npos) + { + if (size == 2) + load_vector_property(material, name, size, value_element.value()); + else if (size == 3) + load_vector_property(material, name, size, value_element.value()); + else if (size == 4) + load_vector_property(material, name, size, value_element.value()); + } + else if (type.find("bool") != std::string::npos) + { + if (size == 2) + load_vector_property(material, name, size, value_element.value()); + else if (size == 3) + load_vector_property(material, name, size, value_element.value()); + else if (size == 4) + load_vector_property(material, name, size, value_element.value()); + } + } + // If property type is a scalar + else + { + if (type.find("float") != std::string::npos) + load_scalar_property(material, name, value_element.value()); + else if (type.find("uint") != std::string::npos) + load_scalar_property(material, name, value_element.value()); + else if (type.find("int") != std::string::npos) + load_scalar_property(material, name, value_element.value()); + else if (type.find("bool") != std::string::npos) + load_scalar_property(material, name, value_element.value()); + } + } + } + + // Update material tweens + material->update_tweens(); + + return material; +} diff --git a/src/engine/resources/mesh-loader.cpp b/src/engine/resources/mesh-loader.cpp new file mode 100644 index 0000000..f31952d --- /dev/null +++ b/src/engine/resources/mesh-loader.cpp @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +template <> +geom::mesh* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +{ + std::string line; + std::vector vertices; + std::vector> triangles; + + while (!PHYSFS_eof(file)) + { + // Read line + physfs_getline(file, line); + + // Tokenize line + std::vector tokens; + std::string token; + std::istringstream linestream(line); + while (linestream >> token) + tokens.push_back(token); + + // Skip empty lines and comments + if (tokens.empty() || tokens[0][0] == '#') + continue; + + if (tokens[0] == "v") + { + if (tokens.size() != 4) + { + std::stringstream stream; + stream << "resource_loader::load(): Invalid line \"" << line << "\"" << std::endl; + throw std::runtime_error(stream.str()); + } + + float3 vertex; + + std::stringstream(tokens[1]) >> vertex[0]; + std::stringstream(tokens[2]) >> vertex[1]; + std::stringstream(tokens[3]) >> vertex[2]; + + vertices.push_back(vertex); + } + else if (tokens[0] == "f") + { + if (tokens.size() != 4) + { + std::stringstream stream; + stream << "resource_loader::load(): Invalid line \"" << line << "\"" << std::endl; + throw std::runtime_error(stream.str()); + + } + + std::uint_fast32_t a, b, c; + std::stringstream(tokens[1]) >> a; + std::stringstream(tokens[2]) >> b; + std::stringstream(tokens[3]) >> c; + triangles.push_back({a - 1, b - 1, c - 1}); + } + } + + geom::mesh* mesh = new geom::mesh(); + geom::create_triangle_mesh(*mesh, vertices, triangles); + + return mesh; +} + diff --git a/src/engine/resources/model-loader.cpp b/src/engine/resources/model-loader.cpp new file mode 100644 index 0000000..58a6034 --- /dev/null +++ b/src/engine/resources/model-loader.cpp @@ -0,0 +1,334 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +inline constexpr std::uint16_t vertex_attribute_position = 0b0000000000000001; +inline constexpr std::uint16_t vertex_attribute_uv = 0b0000000000000010; +inline constexpr std::uint16_t vertex_attribute_normal = 0b0000000000000100; +inline constexpr std::uint16_t vertex_attribute_tangent = 0b0000000000001000; +inline constexpr std::uint16_t vertex_attribute_color = 0b0000000000010000; +inline constexpr std::uint16_t vertex_attribute_bone = 0b0000000000100000; +inline constexpr std::uint16_t vertex_attribute_barycentric = 0b0000000001000000; +inline constexpr std::uint16_t vertex_attribute_morph_target = 0b0000000010000000; +inline constexpr std::uint16_t vertex_attribute_index = 0b0000000100000000; + +template <> +render::model* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +{ + deserialize_context ctx(file); + + // Read vertex format + std::uint16_t vertex_format_flags = 0; + ctx.read16(reinterpret_cast(&vertex_format_flags), 1); + + // Read bone per vertex (if any) + std::uint8_t bones_per_vertex = 0; + if (vertex_format_flags & vertex_attribute_bone) + { + ctx.read8(reinterpret_cast(&bones_per_vertex), 1); + } + + // Read vertex count + std::uint32_t vertex_count = 0; + ctx.read32(reinterpret_cast(&vertex_count), 1); + + // Determine vertex size + std::size_t vertex_size = 0; + if (vertex_format_flags & vertex_attribute_position) + { + vertex_size += sizeof(float) * 3; + } + if (vertex_format_flags & vertex_attribute_uv) + { + vertex_size += sizeof(float) * 2; + } + if (vertex_format_flags & vertex_attribute_normal) + { + vertex_size += sizeof(float) * 3; + } + if (vertex_format_flags & vertex_attribute_tangent) + { + vertex_size += sizeof(float) * 4; + } + if (vertex_format_flags & vertex_attribute_color) + { + vertex_size += sizeof(float) * 4; + } + if (vertex_format_flags & vertex_attribute_bone) + { + vertex_size += sizeof(std::uint32_t) * bones_per_vertex; + vertex_size += sizeof(float) * bones_per_vertex; + } + if (vertex_format_flags & vertex_attribute_barycentric) + { + vertex_size += sizeof(float) * 3; + } + if (vertex_format_flags & vertex_attribute_morph_target) + { + vertex_size += sizeof(float) * 3; + } + + // Allocate vertex data + std::byte* vertex_data = new std::byte[vertex_count * vertex_size]; + + // Read vertices + if constexpr (std::endian::native == std::endian::little) + { + ctx.read8(vertex_data, vertex_count * vertex_size); + } + else + { + std::byte* vertex_data_offset = vertex_data; + for (std::size_t i = 0; i < vertex_count; ++i) + { + if (vertex_format_flags & vertex_attribute_position) + { + ctx.read32(vertex_data_offset, 3); + vertex_data_offset += sizeof(float) * 3; + } + if (vertex_format_flags & vertex_attribute_uv) + { + ctx.read32(vertex_data_offset, 2); + vertex_data_offset += sizeof(float) * 2; + } + if (vertex_format_flags & vertex_attribute_normal) + { + ctx.read32(vertex_data_offset, 3); + vertex_data_offset += sizeof(float) * 3; + } + if (vertex_format_flags & vertex_attribute_tangent) + { + ctx.read32(vertex_data_offset, 4); + vertex_data_offset += sizeof(float) * 4; + } + if (vertex_format_flags & vertex_attribute_color) + { + ctx.read32(vertex_data_offset, 4); + vertex_data_offset += sizeof(float) * 4; + } + if (vertex_format_flags & vertex_attribute_bone) + { + ctx.read32(vertex_data_offset, bones_per_vertex); + ctx.read32(vertex_data_offset, bones_per_vertex); + + vertex_data_offset += sizeof(std::uint32_t) * bones_per_vertex; + vertex_data_offset += sizeof(float) * bones_per_vertex; + } + if (vertex_format_flags & vertex_attribute_barycentric) + { + ctx.read32(vertex_data_offset, 3); + vertex_data_offset += sizeof(float) * 3; + } + if (vertex_format_flags & vertex_attribute_morph_target) + { + ctx.read32(vertex_data_offset, 3); + vertex_data_offset += sizeof(float) * 3; + } + } + } + + // Read geometry bounds + geom::aabb bounds; + ctx.read32(reinterpret_cast(bounds.min_point.data()), 3); + ctx.read32(reinterpret_cast(bounds.max_point.data()), 3); + + // Allocate a model + render::model* model = new render::model(); + + // Set the model bounds + model->set_bounds(bounds); + + // Resize model VBO and upload vertex data + gl::vertex_buffer* vbo = model->get_vertex_buffer(); + vbo->resize(vertex_count * vertex_size, vertex_data); + + // Free vertex data + delete[] vertex_data; + + // Bind vertex attributes to VAO + gl::vertex_array* vao = model->get_vertex_array(); + gl::vertex_attribute attribute; + attribute.buffer = vbo; + attribute.offset = 0; + attribute.stride = vertex_size; + if (vertex_format_flags & vertex_attribute_position) + { + attribute.type = gl::vertex_attribute_type::float_32; + attribute.components = 3; + vao->bind(render::vertex_attribute::position, attribute); + attribute.offset += sizeof(float) * attribute.components; + } + if (vertex_format_flags & vertex_attribute_uv) + { + attribute.type = gl::vertex_attribute_type::float_32; + attribute.components = 2; + vao->bind(render::vertex_attribute::uv, attribute); + attribute.offset += sizeof(float) * attribute.components; + } + if (vertex_format_flags & vertex_attribute_normal) + { + attribute.type = gl::vertex_attribute_type::float_32; + attribute.components = 3; + vao->bind(render::vertex_attribute::normal, attribute); + attribute.offset += sizeof(float) * attribute.components; + } + if (vertex_format_flags & vertex_attribute_tangent) + { + attribute.type = gl::vertex_attribute_type::float_32; + attribute.components = 4; + vao->bind(render::vertex_attribute::tangent, attribute); + attribute.offset += sizeof(float) * attribute.components; + } + if (vertex_format_flags & vertex_attribute_color) + { + attribute.type = gl::vertex_attribute_type::float_32; + attribute.components = 4; + vao->bind(render::vertex_attribute::color, attribute); + attribute.offset += sizeof(float) * attribute.components; + } + if (vertex_format_flags & vertex_attribute_bone) + { + attribute.type = gl::vertex_attribute_type::uint_16; + attribute.components = bones_per_vertex; + vao->bind(render::vertex_attribute::bone_index, attribute); + attribute.offset += sizeof(std::uint32_t) * attribute.components; + + attribute.type = gl::vertex_attribute_type::float_32; + attribute.components = bones_per_vertex; + vao->bind(render::vertex_attribute::bone_weight, attribute); + attribute.offset += sizeof(float) * attribute.components; + } + if (vertex_format_flags & vertex_attribute_barycentric) + { + attribute.type = gl::vertex_attribute_type::float_32; + attribute.components = 3; + vao->bind(render::vertex_attribute::barycentric, attribute); + attribute.offset += sizeof(float) * attribute.components; + } + if (vertex_format_flags & vertex_attribute_morph_target) + { + attribute.type = gl::vertex_attribute_type::float_32; + attribute.components = 3; + vao->bind(render::vertex_attribute::target, attribute); + attribute.offset += sizeof(float) * attribute.components; + } + + // Read material count + std::uint16_t material_count = 0; + ctx.read16(reinterpret_cast(&material_count), 1); + + // Read materials + for (std::uint16_t i = 0; i < material_count; ++i) + { + // Read material name length + std::uint8_t material_name_length = 0; + ctx.read8(reinterpret_cast(&material_name_length), 1); + + // Read material name + std::string material_name(static_cast(material_name_length), '\0'); + ctx.read8(reinterpret_cast(material_name.data()), material_name_length); + + // Read offset to index of first vertex + std::uint32_t material_vertex_offset = 0; + ctx.read32(reinterpret_cast(&material_vertex_offset), 1); + + // Read vertex count + std::uint32_t material_vertex_count = 0; + ctx.read32(reinterpret_cast(&material_vertex_count), 1); + + // Slugify material filename + std::string material_filename = material_name + ".mtl"; + std::replace(material_filename.begin(), material_filename.end(), '_', '-'); + + // Load material from file + render::material* material = resource_manager->load(material_filename); + + // Create model material group + render::model_group* material_group = model->add_group(material_name); + material_group->set_drawing_mode(gl::drawing_mode::triangles); + material_group->set_start_index(material_vertex_offset); + material_group->set_index_count(material_vertex_count); + material_group->set_material(material); + } + + // Read skeleton + if (vertex_format_flags & vertex_attribute_bone) + { + ::skeleton& skeleton = model->get_skeleton(); + pose& bind_pose = skeleton.bind_pose; + + // Read bone count + std::uint16_t bone_count = 0; + ctx.read16(reinterpret_cast(&bone_count), 1); + + // Read bones + for (std::uint16_t i = 0; i < bone_count; ++i) + { + // Read bone name length + std::uint8_t bone_name_length = 0; + ctx.read8(reinterpret_cast(&bone_name_length), 1); + + // Read bone name + std::string bone_name(static_cast(bone_name_length), '\0'); + ctx.read8(reinterpret_cast(bone_name.data()), bone_name_length); + + // Read parent bone index + std::uint16_t parent_bone_index = i; + ctx.read16(reinterpret_cast(&parent_bone_index), 1); + + // Construct bone identifier + ::bone bone = make_bone(i, parent_bone_index); + + // Add bone to bone map + skeleton.bone_map[bone_name] = bone; + + // Get reference to the bone's bind pose transform + auto& bone_transform = bind_pose[bone]; + + // Read bone translation + ctx.read32(reinterpret_cast(bone_transform.translation.data()), 3); + + // Read bone rotation + ctx.read32(reinterpret_cast(&bone_transform.rotation.r), 1); + ctx.read32(reinterpret_cast(bone_transform.rotation.i.data()), 3); + + // Set bone scale + bone_transform.scale = {1, 1, 1}; + + // Read bone length + float bone_length = 0.0f; + ctx.read32(reinterpret_cast(&bone_length), 1); + } + + // Calculate inverse skeleton-space bind pose + ::concatenate(skeleton.bind_pose, skeleton.inverse_bind_pose); + ::inverse(skeleton.inverse_bind_pose, skeleton.inverse_bind_pose); + } + + return model; +} diff --git a/src/engine/resources/resource-handle.cpp b/src/engine/resources/resource-handle.cpp new file mode 100644 index 0000000..80b9afe --- /dev/null +++ b/src/engine/resources/resource-handle.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include + +resource_handle_base::resource_handle_base(): + reference_count(0) +{} + diff --git a/src/resources/resource-handle.hpp b/src/engine/resources/resource-handle.hpp similarity index 100% rename from src/resources/resource-handle.hpp rename to src/engine/resources/resource-handle.hpp diff --git a/src/engine/resources/resource-loader.cpp b/src/engine/resources/resource-loader.cpp new file mode 100644 index 0000000..a4916e9 --- /dev/null +++ b/src/engine/resources/resource-loader.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include + +void physfs_getline(PHYSFS_File* file, std::string& line) +{ + line.clear(); + + for (;;) + { + char c; + const PHYSFS_sint64 status = PHYSFS_readBytes(file, &c, 1); + + if (status == 1) + { + if (c == '\r') + { + continue; + } + else if (c == '\n') + { + break; + } + else + { + line.append(1, c); + } + + } + else + { + if (PHYSFS_eof(file)) + { + break; + } + else + { + throw deserialize_error(PHYSFS_getLastError()); + } + } + } +} diff --git a/src/resources/resource-loader.hpp b/src/engine/resources/resource-loader.hpp similarity index 100% rename from src/resources/resource-loader.hpp rename to src/engine/resources/resource-loader.hpp diff --git a/src/engine/resources/resource-manager.cpp b/src/engine/resources/resource-manager.cpp new file mode 100644 index 0000000..8736a25 --- /dev/null +++ b/src/engine/resources/resource-manager.cpp @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include + +resource_manager::resource_manager() +{ + // Log PhysicsFS info + // PHYSFS_Version physfs_compiled_version; + // PHYSFS_Version physfs_linked_version; + // PHYSFS_VERSION(&physfs_compiled_version); + // PHYSFS_getLinkedVersion(&physfs_linked_version); + // debug::log::info + // ( + // "PhysicsFS compiled version: {}.{}.{}; linked version: {}.{}.{}", + // physfs_compiled_version.major, + // physfs_compiled_version.minor, + // physfs_compiled_version.patch, + // physfs_linked_version.major, + // physfs_linked_version.minor, + // physfs_linked_version.patch + // ); + + // Init PhysicsFS + debug::log::trace("Initializing PhysicsFS..."); + if (!PHYSFS_init(nullptr)) + { + debug::log::error("Failed to initialize PhysicsFS: {}", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + throw std::runtime_error("Failed to initialize PhysicsFS"); + } + else + { + debug::log::trace("Initialized PhysicsFS"); + } +} + +resource_manager::~resource_manager() +{ + debug::log::trace("Deleting cached resources..."); + + // Delete cached resources + for (auto it = resource_cache.begin(); it != resource_cache.end(); ++it) + { + delete it->second; + } + + debug::log::trace("Deleted cached resources"); + + // Deinit PhysicsFS + debug::log::trace("Deinitializing PhysicsFS..."); + if (!PHYSFS_deinit()) + { + debug::log::error("Failed to deinitialize PhysicsFS: {}", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + } + else + { + debug::log::trace("Deinitialized PhysicsFS"); + } +} + +bool resource_manager::mount(const std::filesystem::path& path) +{ + debug::log::trace("Mounting path \"{}\"...", path.string()); + if (!PHYSFS_mount(path.string().c_str(), nullptr, 1)) + { + debug::log::error("Failed to mount path \"{}\": {}", path.string(), PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + return false; + } + else + { + debug::log::trace("Mounted path \"{}\"", path.string()); + return true; + } +} + +void resource_manager::set_write_dir(const std::filesystem::path& path) +{ + if (!PHYSFS_setWriteDir(path.string().c_str())) + { + debug::log::error("Failed set write directory to \"{}\": {}", path.string(), PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + } + else + { + debug::log::trace("Set write directory to \"{}\"", path.string()); + } +} + +void resource_manager::unload(const std::filesystem::path& path) +{ + // Check if resource is in the cache + auto it = resource_cache.find(path); + if (it != resource_cache.end()) + { + // Decrement the resource handle reference count + --it->second->reference_count; + + // Free the resource if the resource handle is unreferenced + if (it->second->reference_count <= 0) + { + debug::log::trace("Unloading resource \"{}\"...", path.string()); + + delete it->second; + + debug::log::trace("Unloaded resource \"{}\"", path.string()); + } + + // Remove resource from the cache + resource_cache.erase(it); + } +} + +void resource_manager::include(const std::filesystem::path& search_path) +{ + search_paths.push_back(search_path); +} diff --git a/src/engine/resources/resource-manager.hpp b/src/engine/resources/resource-manager.hpp new file mode 100644 index 0000000..3aaef76 --- /dev/null +++ b/src/engine/resources/resource-manager.hpp @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_RESOURCES_RESOURCE_MANAGER_HPP +#define ANTKEEPER_RESOURCES_RESOURCE_MANAGER_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * Loads resources. + */ +class resource_manager +{ +public: + /** + * Creates a resource manager. + */ + resource_manager(); + + /** + * Destroys a resource manager and frees all of its resources. + */ + ~resource_manager(); + + bool mount(const std::filesystem::path& path); + + void set_write_dir(const std::filesystem::path& path); + + /** + * Adds a path to be searched when a resource is requested. + * + * @param path Search path. + */ + void include(const std::filesystem::path& path); + + /** + * Loads the requested resource. If the resource has already been loaded it will be retrieved from the resource cache and its reference count incremented. + * + * @tparam T Resource type. + * @param path Path to the resource, relative to the search paths. + * @return Pointer to the requested resource, or nullptr if the resource could not be found nor loaded. + */ + template + T* load(const std::filesystem::path& path); + + /** + * Decrements a resource's reference count and unloads the resource if it's unreferenced. + * + * @param path Path to the resource, relative to the search paths. + */ + void unload(const std::filesystem::path& path); + + /** + * Saves the specified resource. + * + * @tparam T Resource type. + * @param resource Pointer to the resource. + * @param path Path to the resource. + */ + template + void save(const T* resource, const std::filesystem::path& path); + +private: + std::map resource_cache; + std::list search_paths; +}; + +template +T* resource_manager::load(const std::filesystem::path& path) +{ + // Check if resource is in the cache + auto it = resource_cache.find(path); + if (it != resource_cache.end()) + { + //debug::log::trace("Fetched cached resource \"{}\"". path.string()); + + // Resource found + resource_handle* resource = static_cast*>(it->second); + + // Increment resource handle reference count + ++resource->reference_count; + + // Return resource data + return resource->data; + } + + debug::log::trace("Loading resource \"{}\"...", path.string()); + + // Resource not cached, look for file in search paths + T* data = nullptr; + bool found = false; + for (const std::filesystem::path& search_path: search_paths) + { + std::filesystem::path full_path = search_path / path; + + // Check if file exists + if (!PHYSFS_exists(full_path.string().c_str())) + { + continue; + } + + // File found + found = true; + + // Open file for reading + PHYSFS_File* file = PHYSFS_openRead(full_path.string().c_str()); + if (!file) + { + debug::log::error("Failed to load resource \"{}\": {}", path.string(), PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + break; + } + + // Load opened file + try + { + data = resource_loader::load(this, file, full_path); + } + catch (const std::exception& e) + { + debug::log::error("Failed to load resource \"{}\": {}", path.string(), e.what()); + } + + // Close opened file + if (!PHYSFS_close(file)) + { + debug::log::error("Failed to close resource file \"{}\": {}", path.string(), PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + } + + break; + } + + if (!data) + { + if (!found) + { + debug::log::error("Failed to load resource \"{}\": file not found", path.string()); + } + + return nullptr; + } + + // Create a resource handle for the resource data + resource_handle* resource = new resource_handle(); + resource->data = data; + resource->reference_count = 1; + + // Add resource to the cache + resource_cache[path] = resource; + + debug::log::trace("Loaded resource \"{}\"", path.string()); + + return resource->data; +} + +template +void resource_manager::save(const T* resource, const std::filesystem::path& path) +{ + debug::log::trace("Saving resource to \"{}\"", path.string()); + + // Open file for writing + PHYSFS_File* file = PHYSFS_openWrite(path.string().c_str()); + if (!file) + { + debug::log::error("Failed to save resource to \"{}\": {}", path.string(), PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + return; + } + + // Save to opened file + try + { + resource_loader::save(this, file, path, resource); + debug::log::trace("Saved resource to \"{}\"", path.string()); + } + catch (const std::exception& e) + { + debug::log::error("Failed to save resource to \"{}\": {}", e.what()); + } + + // Close opened file + if (!PHYSFS_close(file)) + { + debug::log::error("Failed to close file \"{}\": {}", path.string(), PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + } +} + +#endif // ANTKEEPER_RESOURCES_RESOURCE_MANAGER_HPP diff --git a/src/engine/resources/serialize-context.cpp b/src/engine/resources/serialize-context.cpp new file mode 100644 index 0000000..13c731e --- /dev/null +++ b/src/engine/resources/serialize-context.cpp @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include + +serialize_context::serialize_context(void* handle): + handle(handle), + m_error(false) +{} + +std::size_t serialize_context::write8(const std::byte* data, std::size_t count) +{ + const PHYSFS_sint64 status = PHYSFS_writeBytes(reinterpret_cast(handle), data, count); + + if (status < 0) + { + m_error = true; + throw serialize_error(PHYSFS_getLastError()); + //return 0; + } + + if (status != count) + { + m_error = true; + throw serialize_error(PHYSFS_getLastError()); + //return static_cast(count); + } + + return count; +} + +std::size_t serialize_context::write16_le(const std::byte* data, std::size_t count) +{ + PHYSFS_File* file = reinterpret_cast(handle); + const PHYSFS_uint16* data16 = reinterpret_cast(data); + + for (std::size_t i = 0; i < count; ++i) + { + if (!PHYSFS_writeULE16(file, *data16)) + { + m_error = true; + throw serialize_error(PHYSFS_getLastError()); + //return i; + } + + ++data16; + } + + return count; +} + +std::size_t serialize_context::write16_be(const std::byte* data, std::size_t count) +{ + PHYSFS_File* file = reinterpret_cast(handle); + const PHYSFS_uint16* data16 = reinterpret_cast(data); + + for (std::size_t i = 0; i < count; ++i) + { + if (!PHYSFS_writeUBE16(file, *data16)) + { + m_error = true; + throw serialize_error(PHYSFS_getLastError()); + //return i; + } + + ++data16; + } + + return count; +} + +std::size_t serialize_context::write32_le(const std::byte* data, std::size_t count) +{ + PHYSFS_File* file = reinterpret_cast(handle); + const PHYSFS_uint32* data32 = reinterpret_cast(data); + + for (std::size_t i = 0; i < count; ++i) + { + if (!PHYSFS_writeULE32(file, *data32)) + { + m_error = true; + throw serialize_error(PHYSFS_getLastError()); + //return i; + } + + ++data32; + } + + return count; +} + +std::size_t serialize_context::write32_be(const std::byte* data, std::size_t count) +{ + PHYSFS_File* file = reinterpret_cast(handle); + const PHYSFS_uint32* data32 = reinterpret_cast(data); + + for (std::size_t i = 0; i < count; ++i) + { + if (!PHYSFS_writeUBE32(file, *data32)) + { + m_error = true; + throw serialize_error(PHYSFS_getLastError()); + //return i; + } + + ++data32; + } + + return count; +} + +std::size_t serialize_context::write64_le(const std::byte* data, std::size_t count) +{ + PHYSFS_File* file = reinterpret_cast(handle); + const PHYSFS_uint64* data64 = reinterpret_cast(data); + + for (std::size_t i = 0; i < count; ++i) + { + if (!PHYSFS_writeULE64(file, *data64)) + { + m_error = true; + throw serialize_error(PHYSFS_getLastError()); + //return i; + } + + ++data64; + } + + return count; +} + +std::size_t serialize_context::write64_be(const std::byte* data, std::size_t count) +{ + PHYSFS_File* file = reinterpret_cast(handle); + const PHYSFS_uint64* data64 = reinterpret_cast(data); + + for (std::size_t i = 0; i < count; ++i) + { + if (!PHYSFS_writeUBE64(file, *data64)) + { + m_error = true; + throw serialize_error(PHYSFS_getLastError()); + //return i; + } + + ++data64; + } + + return count; +} diff --git a/src/resources/serialize-context.hpp b/src/engine/resources/serialize-context.hpp similarity index 100% rename from src/resources/serialize-context.hpp rename to src/engine/resources/serialize-context.hpp diff --git a/src/resources/serialize-error.hpp b/src/engine/resources/serialize-error.hpp similarity index 100% rename from src/resources/serialize-error.hpp rename to src/engine/resources/serialize-error.hpp diff --git a/src/engine/resources/serializer.cpp b/src/engine/resources/serializer.cpp new file mode 100644 index 0000000..e797b56 --- /dev/null +++ b/src/engine/resources/serializer.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include + +template <> +void serializer::serialize(const bool& value, serialize_context& ctx) +{ + const std::uint8_t temp = (value) ? 1 : 0; + ctx.write8(reinterpret_cast(&temp), 1); +}; + +template <> +void serializer::serialize(const std::uint8_t& value, serialize_context& ctx) +{ + ctx.write8(reinterpret_cast(&value), 1); +}; + +template <> +void serializer::serialize(const std::uint16_t& value, serialize_context& ctx) +{ + ctx.write16(reinterpret_cast(&value), 1); +}; + +template <> +void serializer::serialize(const std::uint32_t& value, serialize_context& ctx) +{ + ctx.write32(reinterpret_cast(&value), 1); +}; + +template <> +void serializer::serialize(const std::uint64_t& value, serialize_context& ctx) +{ + ctx.write64(reinterpret_cast(&value), 1); +}; + +template <> +void serializer::serialize(const std::int8_t& value, serialize_context& ctx) +{ + ctx.write8(reinterpret_cast(&value), 1); +}; + +template <> +void serializer::serialize(const std::int16_t& value, serialize_context& ctx) +{ + ctx.write16(reinterpret_cast(&value), 1); +}; + +template <> +void serializer::serialize(const std::int32_t& value, serialize_context& ctx) +{ + ctx.write32(reinterpret_cast(&value), 1); +}; + +template <> +void serializer::serialize(const std::int64_t& value, serialize_context& ctx) +{ + ctx.write64(reinterpret_cast(&value), 1); +}; + +template <> +void serializer::serialize(const float& value, serialize_context& ctx) +{ + ctx.write32(reinterpret_cast(&value), 1); +}; + +template <> +void serializer::serialize(const double& value, serialize_context& ctx) +{ + ctx.write64(reinterpret_cast(&value), 1); +}; + +template <> +void serializer::serialize(const std::string& value, serialize_context& ctx) +{ + const std::uint64_t length = static_cast(value.length()); + ctx.write64(reinterpret_cast(&length), 1); + ctx.write8(reinterpret_cast(value.data()), static_cast(length)); +}; + +template <> +void serializer::serialize(const std::u8string& value, serialize_context& ctx) +{ + const std::uint64_t length = static_cast(value.length()); + ctx.write64(reinterpret_cast(&length), 1); + ctx.write8(reinterpret_cast(value.data()), static_cast(length)); +}; + +template <> +void serializer::serialize(const std::u16string& value, serialize_context& ctx) +{ + const std::uint64_t length = static_cast(value.length()); + ctx.write64(reinterpret_cast(&length), 1); + ctx.write16(reinterpret_cast(value.data()), static_cast(length)); +}; + +template <> +void serializer::serialize(const std::u32string& value, serialize_context& ctx) +{ + const std::uint64_t length = static_cast(value.length()); + ctx.write64(reinterpret_cast(&length), 1); + ctx.write32(reinterpret_cast(value.data()), static_cast(length)); +}; diff --git a/src/engine/resources/serializer.hpp b/src/engine/resources/serializer.hpp new file mode 100644 index 0000000..31aca0e --- /dev/null +++ b/src/engine/resources/serializer.hpp @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_RESOURCES_SERIALIZER_HPP +#define ANTKEEPER_RESOURCES_SERIALIZER_HPP + +#include + +/** + * Specializations of serializer define the serialization process for a given type. + * + * @tparam T Serializable type. + */ +template +struct serializer +{ + /** + * Serializes a value. + * + * @param value Value to serialize. + * @param ctx Serialize context. + */ + void serialize(const T& value, serialize_context& ctx); +}; + +#endif // ANTKEEPER_RESOURCES_SERIALIZER_HPP diff --git a/src/engine/resources/shader-loader.cpp b/src/engine/resources/shader-loader.cpp new file mode 100644 index 0000000..b24e9dd --- /dev/null +++ b/src/engine/resources/shader-loader.cpp @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * Scans a text file for the presence of a `#pragma once` directive. + * + * @param source Text file to scan. + * + * @return `true` if the file contains a `#pragma once` directive, `false` otherwise. + */ +static bool has_pragma_once(const text_file& source) +{ + for (const auto& line: source) + { + std::istringstream line_stream(line); + std::string token; + + // If line contains a `#pragma once` directive + if (line_stream >> token && token == "#pragma" && + line_stream >> token && token == "once") + { + return true; + } + } + + return false; +} + +/** + * Handles `#pragma include` directives by loading the specified text files and inserting them in place. + */ +static void handle_includes(text_file& source, std::unordered_set& include_once, resource_manager* resource_manager) +{ + // For each line in the source + for (std::size_t i = 0; i < source.size(); ++i) + { + std::string token; + std::istringstream line_stream(source[i]); + + // If line contains a `#pragma include` directive + if (line_stream >> token && token == "#pragma" && + line_stream >> token && token == "include") + { + // If third token is enclosed in quotes or angled brackets + if (line_stream >> token && token.size() > 2 && + ((token.front() == '\"' && token.back() == '\"') || + (token.front() == '<' && token.back() == '>'))) + { + // Extract include path + std::string path = token.substr(1, token.length() - 2); + + // Load include file + const text_file* include_file = resource_manager->load(path); + if (!include_file) + { + source[i] = "#error file not found (" + path + ")"; + } + else + { + // If file has not been included or has no `#pragma once` directive + if (!include_once.contains(include_file)) + { + // If file has `#pragma once` directive + if (has_pragma_once(*include_file)) + { + // Add file to set of files to include once + include_once.insert(include_file); + } + + // Create a copy of the include file + text_file include_file_copy = *include_file; + + // Handle `#pragma include` directives inside include file + handle_includes(include_file_copy, include_once, resource_manager); + + // Replace #pragma include directive with include file contents + source.erase(source.begin() + i); + source.insert(source.begin() + i, include_file_copy.begin(), include_file_copy.end()); + i += include_file_copy.size() - 1; + } + } + } + else + { + source[i] = "#error malformed include directive (" + source[i] + ")"; + } + } + } +} + +template <> +gl::shader_program* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +{ + // Load shader source file + const text_file* source_file = resource_loader::load(resource_manager, file, path); + + // Make a copy of the shader source file + text_file source_file_copy = *source_file; + + // Handle `#pragma include` directives + std::unordered_set include_once; + include_once.insert(source_file); + handle_includes(source_file_copy, include_once, resource_manager); + + // Join vector of source lines into single string + std::ostringstream stream; + std::copy(source_file_copy.begin(), source_file_copy.end(), std::ostream_iterator(stream, "\n")); + + // Create shader template + render::shader_template* shader = new render::shader_template(stream.str()); + + // Build shader program + gl::shader_program* program = shader->build(render::shader_template::dictionary_type()); + + // Check if shader program was linked successfully + if (!program->was_linked()) + { + throw std::runtime_error("Shader program linking failed: " + program->get_info_log()); + } + + // Destroy shader template + delete shader; + + return program; +} + +template <> +render::shader_template* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +{ + // Load shader template source file + const text_file* source_file = resource_loader::load(resource_manager, file, path); + + // Make a copy of the shader template source file + text_file source_file_copy = *source_file; + + // Handle `#pragma include` directives + std::unordered_set include_once; + include_once.insert(source_file); + handle_includes(source_file_copy, include_once, resource_manager); + + // Join vector of source lines into single string + std::ostringstream stream; + std::copy(source_file_copy.begin(), source_file_copy.end(), std::ostream_iterator(stream, "\n")); + + // Create shader template + render::shader_template* shader = new render::shader_template(stream.str()); + + return shader; +} diff --git a/src/engine/resources/string-map-loader.cpp b/src/engine/resources/string-map-loader.cpp new file mode 100644 index 0000000..4f15969 --- /dev/null +++ b/src/engine/resources/string-map-loader.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include +#include + +template <> +i18n::string_map* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +{ + i18n::string_map* map = new i18n::string_map(); + + deserialize_context ctx(file); + deserializer().deserialize(*map, ctx); + + return map; +} + +template <> +void resource_loader::save(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path, const i18n::string_map* map) +{ + serialize_context ctx(file); + serializer().serialize(*map, ctx); +} diff --git a/src/engine/resources/string-table-loader.cpp b/src/engine/resources/string-table-loader.cpp new file mode 100644 index 0000000..fe57f94 --- /dev/null +++ b/src/engine/resources/string-table-loader.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include +#include + +template <> +i18n::string_table* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +{ + i18n::string_table* table = new i18n::string_table(); + + i18n::string_table_row row; + std::string entry; + + for (;;) + { + char c; + const PHYSFS_sint64 status = PHYSFS_readBytes(file, &c, 1); + + if (status == 1) + { + if (c == '\t') + { + row.push_back(entry); + entry.clear(); + } + else if (c == '\n') + { + row.push_back(entry); + entry.clear(); + table->push_back(row); + row.clear(); + } + else if (c != '\r') + { + entry.push_back(c); + } + } + else + { + if (PHYSFS_eof(file)) + { + if (!entry.empty()) + { + row.push_back(entry); + } + if (!row.empty()) + { + table->push_back(row); + } + break; + } + else + { + throw deserialize_error(PHYSFS_getLastError()); + } + } + } + + return table; +} diff --git a/src/engine/resources/text-file-loader.cpp b/src/engine/resources/text-file-loader.cpp new file mode 100644 index 0000000..c00f297 --- /dev/null +++ b/src/engine/resources/text-file-loader.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include + +template <> +text_file* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +{ + text_file* text = new text_file(); + std::string line; + + while (!PHYSFS_eof(file)) + { + physfs_getline(file, line); + text->push_back(line); + } + + return text; +} diff --git a/src/resources/text-file.hpp b/src/engine/resources/text-file.hpp similarity index 100% rename from src/resources/text-file.hpp rename to src/engine/resources/text-file.hpp diff --git a/src/engine/resources/texture-loader.cpp b/src/engine/resources/texture-loader.cpp new file mode 100644 index 0000000..5ca939b --- /dev/null +++ b/src/engine/resources/texture-loader.cpp @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +template <> +gl::texture_1d* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +{ + // Read file into buffer + std::size_t size = static_cast(PHYSFS_fileLength(file)); + std::string buffer; + buffer.resize(size); + PHYSFS_readBytes(file, &buffer[0], size); + + // Parse json from file buffer + nlohmann::json json = nlohmann::json::parse(buffer); + + // Read image filename + std::string image_filename; + if (auto element = json.find("image"); element != json.end()) + image_filename = element.value().get(); + + // Read color space + gl::color_space color_space = gl::color_space::linear; + if (auto element = json.find("color_space"); element != json.end()) + { + std::string value = element.value().get(); + if (value == "linear") + color_space = gl::color_space::linear; + else if (value == "srgb") + color_space = gl::color_space::srgb; + } + + // Read extension mode + gl::texture_wrapping wrapping = gl::texture_wrapping::repeat; + if (auto element = json.find("extension"); element != json.end()) + { + std::string value = element.value().get(); + if (value == "clip") + wrapping = gl::texture_wrapping::clip; + else if (value == "extend") + wrapping = gl::texture_wrapping::extend; + else if (value == "repeat") + wrapping = gl::texture_wrapping::repeat; + else if (value == "mirrored_repeat") + wrapping = gl::texture_wrapping::mirrored_repeat; + } + + // Read interpolation mode + gl::texture_min_filter min_filter = gl::texture_min_filter::linear_mipmap_linear; + gl::texture_mag_filter mag_filter = gl::texture_mag_filter::linear; + if (auto element = json.find("interpolation"); element != json.end()) + { + std::string value = element.value().get(); + if (value == "linear") + { + min_filter = gl::texture_min_filter::linear_mipmap_linear; + mag_filter = gl::texture_mag_filter::linear; + } + else if (value == "closest") + { + min_filter = gl::texture_min_filter::nearest_mipmap_nearest; + mag_filter = gl::texture_mag_filter::nearest; + } + } + + // Read max anisotropy + float max_anisotropy = 0.0f; + if (auto element = json.find("max_anisotropy"); element != json.end()) + max_anisotropy = element.value().get(); + + // Load image + ::image* image = resource_manager->load<::image>(image_filename); + + // Determine pixel type + gl::pixel_type type = (image->get_component_size() == sizeof(float)) ? gl::pixel_type::float_32 : gl::pixel_type::uint_8; + + // Determine pixel format + gl::pixel_format format; + if (image->get_channel_count() == 1) + { + format = gl::pixel_format::r; + } + else if (image->get_channel_count() == 2) + { + format = gl::pixel_format::rg; + } + else if (image->get_channel_count() == 3) + { + format = gl::pixel_format::rgb; + } + else if (image->get_channel_count() == 4) + { + format = gl::pixel_format::rgba; + } + else + { + std::stringstream stream; + stream << std::string("Texture cannot be created from an image with an unsupported number of channels (") << image->get_channel_count() << std::string(")."); + delete image; + throw std::runtime_error(stream.str().c_str()); + } + + // Create texture + gl::texture_1d* texture = new gl::texture_1d(image->get_width(), type, format, color_space, image->data()); + + // Set wrapping and filtering + texture->set_wrapping(wrapping); + texture->set_filters(min_filter, mag_filter); + texture->set_max_anisotropy(max_anisotropy); + + // Free loaded image + resource_manager->unload(image_filename); + + return texture; +} + +template <> +gl::texture_2d* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +{ + // Read file into buffer + std::size_t size = static_cast(PHYSFS_fileLength(file)); + std::string buffer; + buffer.resize(size); + PHYSFS_readBytes(file, &buffer[0], size); + + // Parse json from file buffer + nlohmann::json json = nlohmann::json::parse(buffer); + + // Read image filename + std::string image_filename; + if (auto element = json.find("image"); element != json.end()) + image_filename = element.value().get(); + + // Read color space + gl::color_space color_space = gl::color_space::linear; + if (auto element = json.find("color_space"); element != json.end()) + { + std::string value = element.value().get(); + if (value == "linear") + color_space = gl::color_space::linear; + else if (value == "srgb") + color_space = gl::color_space::srgb; + } + + // Read extension mode + gl::texture_wrapping wrapping = gl::texture_wrapping::repeat; + if (auto element = json.find("extension"); element != json.end()) + { + std::string value = element.value().get(); + if (value == "clip") + wrapping = gl::texture_wrapping::clip; + else if (value == "extend") + wrapping = gl::texture_wrapping::extend; + else if (value == "repeat") + wrapping = gl::texture_wrapping::repeat; + else if (value == "mirrored_repeat") + wrapping = gl::texture_wrapping::mirrored_repeat; + } + + // Read interpolation mode + gl::texture_min_filter min_filter = gl::texture_min_filter::linear_mipmap_linear; + gl::texture_mag_filter mag_filter = gl::texture_mag_filter::linear; + if (auto element = json.find("interpolation"); element != json.end()) + { + std::string value = element.value().get(); + if (value == "linear") + { + min_filter = gl::texture_min_filter::linear_mipmap_linear; + mag_filter = gl::texture_mag_filter::linear; + } + else if (value == "closest") + { + min_filter = gl::texture_min_filter::nearest_mipmap_nearest; + mag_filter = gl::texture_mag_filter::nearest; + } + } + + // Read max anisotropy + float max_anisotropy = 0.0f; + if (auto element = json.find("max_anisotropy"); element != json.end()) + max_anisotropy = element.value().get(); + + // Load image + ::image* image = resource_manager->load<::image>(image_filename); + + // Determine pixel type + gl::pixel_type type = (image->get_component_size() == sizeof(float)) ? gl::pixel_type::float_32 : gl::pixel_type::uint_8; + + // Determine pixel format + gl::pixel_format format; + if (image->get_channel_count() == 1) + { + format = gl::pixel_format::r; + } + else if (image->get_channel_count() == 2) + { + format = gl::pixel_format::rg; + } + else if (image->get_channel_count() == 3) + { + format = gl::pixel_format::rgb; + } + else if (image->get_channel_count() == 4) + { + format = gl::pixel_format::rgba; + } + else + { + std::stringstream stream; + stream << std::string("Texture cannot be created from an image with an unsupported number of channels (") << image->get_channel_count() << std::string(")."); + delete image; + throw std::runtime_error(stream.str().c_str()); + } + + // Create texture + gl::texture_2d* texture = new gl::texture_2d(image->get_width(), image->get_height(), type, format, color_space, image->data()); + + // Set wrapping and filtering + texture->set_wrapping(wrapping, wrapping); + texture->set_filters(min_filter, mag_filter); + texture->set_max_anisotropy(max_anisotropy); + + // Free loaded image + resource_manager->unload(image_filename); + + return texture; +} + + diff --git a/src/engine/resources/typeface-loader.cpp b/src/engine/resources/typeface-loader.cpp new file mode 100644 index 0000000..546f84b --- /dev/null +++ b/src/engine/resources/typeface-loader.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include +#include +#include +#include FT_FREETYPE_H + +template <> +type::typeface* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +{ + FT_Error error = 0; + + // Init FreeType library object + FT_Library library; + error = FT_Init_FreeType(&library); + if (error) + { + throw std::runtime_error("Failed to init FreeType library (error code " + std::to_string(error) + ")"); + } + + // Read file into buffer + std::size_t size = static_cast(PHYSFS_fileLength(file)); + unsigned char* buffer = new unsigned char[size]; + PHYSFS_readBytes(file, buffer, size); + + // Load FreeType face from buffer + FT_Face face; + error = FT_New_Memory_Face(library, buffer, size, 0, &face); + if (error) + { + delete[] buffer; + FT_Done_FreeType(library); + throw std::runtime_error("Failed to load FreeType face (error code " + std::to_string(error) + ")"); + } + + return new type::freetype::typeface(library, face, buffer); +} diff --git a/src/engine/scene/ambient-light.hpp b/src/engine/scene/ambient-light.hpp new file mode 100644 index 0000000..0063cd6 --- /dev/null +++ b/src/engine/scene/ambient-light.hpp @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_SCENE_AMBIENT_LIGHT_HPP +#define ANTKEEPER_SCENE_AMBIENT_LIGHT_HPP + +#include + +namespace scene { + +class ambient_light: public light +{ +public: + /// Returns light_type::ambient + virtual light_type get_light_type() const; +}; + +inline light_type ambient_light::get_light_type() const +{ + return light_type::ambient; +} + +} // namespace scene + +#endif // ANTKEEPER_SCENE_AMBIENT_LIGHT_HPP + diff --git a/src/engine/scene/billboard.cpp b/src/engine/scene/billboard.cpp new file mode 100644 index 0000000..0611d5a --- /dev/null +++ b/src/engine/scene/billboard.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include + +namespace scene { + +const typename billboard::aabb_type billboard::local_bounds = {{-1, -1, -1}, {1, 1, 1}}; + +billboard::billboard(): + world_bounds(local_bounds), + material(nullptr), + type(billboard_type::flat), + alignment_axis(config::global_up) +{} + +billboard::billboard(const billboard& other) +{ + *this = other; +} + +billboard& billboard::operator=(const billboard& other) +{ + material = other.material; + type = other.type; + alignment_axis = other.alignment_axis; + set_transform(other.get_transform()); + set_active(other.is_active()); + set_culling_mask(other.get_culling_mask()); + return *this; +} + +void billboard::set_material(render::material* material) +{ + this->material = material; +} + +void billboard::set_billboard_type(billboard_type type) +{ + this->type = type; +} + +void billboard::set_alignment_axis(const float3& axis) +{ + this->alignment_axis = axis; +} + +void billboard::transformed() +{ + world_bounds = aabb_type::transform(local_bounds, get_transform()); +} + +void billboard::update_tweens() +{ + object_base::update_tweens(); + if (material) + { + material->update_tweens(); + } +} + +} // namespace scene diff --git a/src/engine/scene/billboard.hpp b/src/engine/scene/billboard.hpp new file mode 100644 index 0000000..c92ea27 --- /dev/null +++ b/src/engine/scene/billboard.hpp @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_SCENE_BILLBOARD_HPP +#define ANTKEEPER_SCENE_BILLBOARD_HPP + +#include +#include +#include +#include + +namespace scene { + +/// Enumerates billboard types. +enum class billboard_type +{ + // No alignment + flat, + + // Aligns to face camera + spherical, + + // Rotates about an alignment axis to face camera + cylindrical +}; + +/** + * A 2D unit quad with one material. + */ +class billboard: public object +{ +public: + typedef geom::aabb aabb_type; + + billboard(); + billboard(const billboard& other); + billboard& operator=(const billboard& other); + + void set_material(render::material* material); + + /// Sets the billboard alignment mode. + void set_billboard_type(billboard_type type); + + /// Sets the axis around which the billboard will be rotated when the alignment is set to billboard_alignment::cylindrical. + void set_alignment_axis(const float3& axis); + + virtual const bounding_volume_type& get_local_bounds() const; + virtual const bounding_volume_type& get_world_bounds() const; + + render::material* get_material() const; + billboard_type get_billboard_type() const; + const float3& get_alignment_axis() const; + + virtual void update_tweens(); + +private: + static const aabb_type local_bounds; + + virtual void transformed(); + + + aabb_type world_bounds; + render::material* material; + billboard_type type; + float3 alignment_axis; +}; + +inline const typename object_base::bounding_volume_type& billboard::get_local_bounds() const +{ + return local_bounds; +} + +inline const typename object_base::bounding_volume_type& billboard::get_world_bounds() const +{ + return world_bounds; +} + +inline render::material* billboard::get_material() const +{ + return material; +} + +inline billboard_type billboard::get_billboard_type() const +{ + return type; +} + +inline const float3& billboard::get_alignment_axis() const +{ + return alignment_axis; +} + +} // namespace scene + +#endif // ANTKEEPER_SCENE_BILLBOARD_HPP + diff --git a/src/engine/scene/camera.cpp b/src/engine/scene/camera.cpp new file mode 100644 index 0000000..1768243 --- /dev/null +++ b/src/engine/scene/camera.cpp @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +namespace scene { + +static float4x4 interpolate_view(const camera* camera, const float4x4& x, const float4x4& y, float a) +{ + math::transform transform = camera->get_transform_tween().interpolate(a); + float3 forward = transform.rotation * config::global_forward; + float3 up = transform.rotation * config::global_up; + return math::look_at(transform.translation, transform.translation + forward, up); +} + +static float4x4 interpolate_projection(const camera* camera, const float4x4& x, const float4x4& y, float a) +{ + if (camera->is_orthographic()) + { + return math::ortho( + camera->get_clip_left_tween().interpolate(a), + camera->get_clip_right_tween().interpolate(a), + camera->get_clip_bottom_tween().interpolate(a), + camera->get_clip_top_tween().interpolate(a), + camera->get_clip_far_tween().interpolate(a), + camera->get_clip_near_tween().interpolate(a)); + } + else + { + return math::perspective( + camera->get_fov_tween().interpolate(a), + camera->get_aspect_ratio_tween().interpolate(a), + camera->get_clip_far_tween().interpolate(a), + camera->get_clip_near_tween().interpolate(a)); + } +} + +static float4x4 interpolate_view_projection(const camera* camera, const float4x4& x, const float4x4& y, float a) +{ + return camera->get_projection_tween().interpolate(a) * camera->get_view_tween().interpolate(a); +} + +camera::camera(): + compositor(nullptr), + composite_index(0), + orthographic(true), + clip_left(-1.0f, math::lerp), + clip_right(1.0f, math::lerp), + clip_bottom(-1.0f, math::lerp), + clip_top(1.0f, math::lerp), + clip_near(-1.0f, math::lerp), + clip_far(1.0f, math::lerp), + fov(math::half_pi, math::lerp), + aspect_ratio(1.0f, math::lerp), + view(math::matrix4::identity(), std::bind(&interpolate_view, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)), + projection(math::matrix4::identity(), std::bind(&interpolate_projection, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)), + view_projection(math::matrix4::identity(), std::bind(&interpolate_view_projection, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)), + exposure(0.0f, math::lerp) +{} + +geom::primitive::ray camera::pick(const float2& ndc) const +{ + const float4x4 inverse_view_projection = math::inverse(view_projection[1]); + + const float4 near = inverse_view_projection * float4{ndc[0], ndc[1], 1.0f, 1.0f}; + const float4 far = inverse_view_projection * float4{ndc[0], ndc[1], 0.0f, 1.0f}; + + const float3 origin = float3{near[0], near[1], near[2]} / near[3]; + const float3 direction = math::normalize(float3{far[0], far[1], far[2]} / far[3] - origin); + + return {origin, direction}; +} + +float3 camera::project(const float3& object, const float4& viewport) const +{ + float4 result = view_projection[1] * float4{object[0], object[1], object[2], 1.0f}; + result[0] = (result[0] / result[3]) * 0.5f + 0.5f; + result[1] = (result[1] / result[3]) * 0.5f + 0.5f; + result[2] = (result[2] / result[3]) * 0.5f + 0.5f; + + result[0] = result[0] * viewport[2] + viewport[0]; + result[1] = result[1] * viewport[3] + viewport[1]; + + return math::vector(result); +} + +float3 camera::unproject(const float3& window, const float4& viewport) const +{ + float4 result; + result[0] = ((window[0] - viewport[0]) / viewport[2]) * 2.0f - 1.0f; + result[1] = ((window[1] - viewport[1]) / viewport[3]) * 2.0f - 1.0f; + //result[2] = window[2] * 2.0f - 1.0f; z: [-1, 1] + //result[2] = window[2]; // z: [0, 1] + result[2] = 1.0f - window[2]; // z: [1, 0] + result[3] = 1.0f; + + result = math::inverse(view_projection[1]) * result; + + return math::vector(result) * (1.0f / result[3]); +} + +void camera::set_perspective(float fov, float aspect_ratio, float clip_near, float clip_far) +{ + orthographic = false; + + this->fov[1] = fov; + this->aspect_ratio[1] = aspect_ratio; + this->clip_near[1] = clip_near; + this->clip_far[1] = clip_far; + + projection[1] = math::perspective_half_z(fov, aspect_ratio, clip_far, clip_near); + + // Recalculate view-projection matrix + view_projection[1] = projection[1] * view[1]; + + // Recalculate view frustum + /// @TODO: this is a hack to fix the half z projection matrix view frustum + view_frustum.set_matrix(math::perspective(this->fov[1], this->aspect_ratio[1], this->clip_near[1], this->clip_far[1]) * view[1]); +} + +void camera::set_orthographic(float clip_left, float clip_right, float clip_bottom, float clip_top, float clip_near, float clip_far) +{ + orthographic = true; + + this->clip_left[1] = clip_left; + this->clip_right[1] = clip_right; + this->clip_bottom[1] = clip_bottom; + this->clip_top[1] = clip_top; + this->clip_near[1] = clip_near; + this->clip_far[1] = clip_far; + + projection[1] = math::ortho_half_z(clip_left, clip_right, clip_bottom, clip_top, clip_far, clip_near); + + // Recalculate view-projection matrix + view_projection[1] = projection[1] * view[1]; + + // Recalculate view frustum + view_frustum.set_matrix(view_projection[1]); +} + +void camera::set_exposure(float ev100) +{ + exposure[1] = ev100; +} + +void camera::set_compositor(render::compositor* compositor) +{ + this->compositor = compositor; +} + +void camera::set_composite_index(int index) +{ + composite_index = index; +} + +void camera::update_tweens() +{ + object_base::update_tweens(); + clip_left.update(); + clip_right.update(); + clip_bottom.update(); + clip_top.update(); + clip_near.update(); + clip_far.update(); + fov.update(); + aspect_ratio.update(); + view.update(); + projection.update(); + view_projection.update(); + exposure.update(); +} + +void camera::transformed() +{ + // Recalculate view and view-projection matrices + float3 forward = get_rotation() * config::global_forward; + float3 up = get_rotation() * config::global_up; + view[1] = math::look_at(get_translation(), get_translation() + forward, up); + view_projection[1] = projection[1] * view[1]; + + // Recalculate view frustum + /// @TODO: this is a hack to fix the half z projection matrix view frustum + if (orthographic) + view_frustum.set_matrix(view_projection[1]); + else + view_frustum.set_matrix(math::perspective(fov[1], aspect_ratio[1], clip_near[1], clip_far[1]) * view[1]); +} + +} // namespace scene diff --git a/src/engine/scene/camera.hpp b/src/engine/scene/camera.hpp new file mode 100644 index 0000000..ecc4a4a --- /dev/null +++ b/src/engine/scene/camera.hpp @@ -0,0 +1,328 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_SCENE_CAMERA_HPP +#define ANTKEEPER_SCENE_CAMERA_HPP + +#include +#include +#include +#include +#include + +namespace scene { + +/** + * + */ +class camera: public object +{ +public: + typedef geom::view_frustum view_frustum_type; + + camera(); + + /** + * Constructs a picking ray from normalized device coordinates (NDC). + * + * @param ndc NDC coordinates. + * + * @return Picking ray. + */ + geom::primitive::ray pick(const float2& ndc) const; + + /** + * Maps object coordinates to window coordinates. + * + * @param object Object coordinates. + * @param viewport Vector containing the `x`, `y`, `w`, and `h` of the viewport. + * @return Projected window coordinates. + */ + float3 project(const float3& object, const float4& viewport) const; + + /** + * Maps window coordinates to object coordinates. + * + * @param window Window coordinates. + * @param viewport Vector containing the `x`, `y`, `w`, and `h` of the viewport. + * @return Unprojected object coordinates. + */ + float3 unproject(const float3& window, const float4& viewport) const; + + /** + * Sets the camera's projection matrix using perspective projection. + * + * @param fov Vertical field of view. + * @param aspect_ratio Aspect ratio. + * @param clip_near Distance to near clipping plane. + * @param clip_far Distance to far clipping plane. + */ + void set_perspective(float fov, float aspect_ratio, float clip_near, float clip_far); + + /** + * Sets the camera's projection matrix using orthographic projection. + * + * @param clip_left Signed distance to left clipping plane. + * @param clip_right Signed distance to right clipping plane. + * @param clip_bottom Signed distance to bottom clipping plane. + * @param clip_top Signed distance to top clipping plane. + * @param clip_near Signed distance to near clipping plane. + * @param clip_far Signed distance to far clipping plane. + */ + void set_orthographic(float clip_left, float clip_right, float clip_bottom, float clip_top, float clip_near, float clip_far); + + /** + * Sets the camera's ISO 100 exposure value directly + * + * @param ev100 ISO 100 exposure value. + */ + void set_exposure(float ev100); + + void set_compositor(render::compositor* compositor); + void set_composite_index(int index); + + + virtual const bounding_volume_type& get_local_bounds() const; + virtual const bounding_volume_type& get_world_bounds() const; + + float is_orthographic() const; + float get_clip_left() const; + float get_clip_right() const; + float get_clip_bottom() const; + float get_clip_top() const; + float get_clip_near() const; + float get_clip_far() const; + float get_fov() const; + float get_aspect_ratio() const; + + /// Returns the camera's view matrix. + const float4x4& get_view() const; + + /// Returns the camera's projection matrix. + const float4x4& get_projection() const; + + /// Returns the camera's view-projection matrix. + const float4x4& get_view_projection() const; + + /// Returns the camera's view frustum. + const view_frustum_type& get_view_frustum() const; + + /// Returns the camera's ISO 100 exposure value. + float get_exposure() const; + + const render::compositor* get_compositor() const; + render::compositor* get_compositor(); + int get_composite_index() const; + + const tween& get_clip_left_tween() const; + const tween& get_clip_right_tween() const; + const tween& get_clip_bottom_tween() const; + const tween& get_clip_top_tween() const; + const tween& get_clip_near_tween() const; + const tween& get_clip_far_tween() const; + const tween& get_fov_tween() const; + const tween& get_aspect_ratio_tween() const; + const tween& get_view_tween() const; + const tween& get_projection_tween() const; + const tween& get_view_projection_tween() const; + const tween& get_exposure_tween() const; + + /// @copydoc object_base::update_tweens(); + virtual void update_tweens(); + +private: + virtual void transformed(); + + render::compositor* compositor; + int composite_index; + bool orthographic; + tween clip_left; + tween clip_right; + tween clip_bottom; + tween clip_top; + tween clip_near; + tween clip_far; + tween fov; + tween aspect_ratio; + tween view; + tween projection; + tween view_projection; + tween exposure; + view_frustum_type view_frustum; +}; + +inline const typename object_base::bounding_volume_type& camera::get_local_bounds() const +{ + /// @TODO: return local bounds, not world bounds + return view_frustum.get_bounds(); +} + +inline const typename object_base::bounding_volume_type& camera::get_world_bounds() const +{ + return view_frustum.get_bounds(); +} + +inline float camera::is_orthographic() const +{ + return orthographic; +} + +inline float camera::get_clip_left() const +{ + return clip_left[1]; +} + +inline float camera::get_clip_right() const +{ + return clip_right[1]; +} + +inline float camera::get_clip_bottom() const +{ + return clip_bottom[1]; +} + +inline float camera::get_clip_top() const +{ + return clip_top[1]; +} + +inline float camera::get_clip_near() const +{ + return clip_near[1]; +} + +inline float camera::get_clip_far() const +{ + return clip_far[1]; +} + +inline float camera::get_fov() const +{ + return fov[1]; +} + +inline float camera::get_aspect_ratio() const +{ + return aspect_ratio[1]; +} + +inline const float4x4& camera::get_view() const +{ + return view[1]; +} + +inline const float4x4& camera::get_projection() const +{ + return projection[1]; +} + +inline const float4x4& camera::get_view_projection() const +{ + return view_projection[1]; +} + +inline const typename camera::view_frustum_type& camera::get_view_frustum() const +{ + return view_frustum; +} + +inline float camera::get_exposure() const +{ + return exposure[1]; +} + +inline const render::compositor* camera::get_compositor() const +{ + return compositor; +} + +inline render::compositor* camera::get_compositor() +{ + return compositor; +} + +inline int camera::get_composite_index() const +{ + return composite_index; +} + +inline const tween& camera::get_clip_left_tween() const +{ + return clip_left; +} + +inline const tween& camera::get_clip_right_tween() const +{ + return clip_right; +} + +inline const tween& camera::get_clip_bottom_tween() const +{ + return clip_bottom; +} + +inline const tween& camera::get_clip_top_tween() const +{ + return clip_top; +} + +inline const tween& camera::get_clip_near_tween() const +{ + return clip_near; +} + +inline const tween& camera::get_clip_far_tween() const +{ + return clip_far; +} + +inline const tween& camera::get_fov_tween() const +{ + return fov; +} + +inline const tween& camera::get_aspect_ratio_tween() const +{ + return aspect_ratio; +} + +inline const tween& camera::get_view_tween() const +{ + return view; +} + +inline const tween& camera::get_projection_tween() const +{ + return projection; +} + +inline const tween& camera::get_view_projection_tween() const +{ + return view_projection; +} + +inline const tween& camera::get_exposure_tween() const +{ + return exposure; +} + +} // namespace scene + +#endif // ANTKEEPER_SCENE_CAMERA_HPP diff --git a/src/engine/scene/collection.cpp b/src/engine/scene/collection.cpp new file mode 100644 index 0000000..bebdcaa --- /dev/null +++ b/src/engine/scene/collection.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include + +namespace scene { + +void collection::add_object(object_base* object) +{ + objects.push_back(object); + object_map[object->get_object_type_id()].push_back(object); +} + +void collection::remove_object(object_base* object) +{ + objects.remove(object); + object_map[object->get_object_type_id()].remove(object); +} + +void collection::remove_objects() +{ + objects.clear(); + object_map.clear(); +} + +void collection::update_tweens() +{ + for (object_base* object: objects) + { + object->update_tweens(); + } +} + +} // namespace scene diff --git a/src/scene/collection.hpp b/src/engine/scene/collection.hpp similarity index 100% rename from src/scene/collection.hpp rename to src/engine/scene/collection.hpp diff --git a/src/engine/scene/directional-light.cpp b/src/engine/scene/directional-light.cpp new file mode 100644 index 0000000..35aba40 --- /dev/null +++ b/src/engine/scene/directional-light.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include +#include + +namespace scene { + +static float3 interpolate_direction(const float3& x, const float3& y, float a) +{ + math::quaternion q0 = math::rotation(config::global_forward, x); + math::quaternion q1 = math::rotation(config::global_forward, y); + return math::normalize(math::slerp(q0, q1, a) * config::global_forward); +} + +directional_light::directional_light(): + direction(config::global_forward, interpolate_direction), + shadow_caster(false), + shadow_framebuffer(nullptr), + shadow_bias(0.005f), + shadow_cascade_count(4), + shadow_cascade_coverage(1.0f), + shadow_cascade_distribution(0.8f), + light_texture(nullptr), + light_texture_opacity(1.0f, math::lerp), + light_texture_scale({1.0f, 1.0f}, math::lerp) +{ + shadow_cascade_distances.resize(shadow_cascade_count); + shadow_cascade_matrices.resize(shadow_cascade_count); +} + +void directional_light::set_shadow_caster(bool caster) noexcept +{ + shadow_caster = caster; +} + +void directional_light::set_shadow_framebuffer(const gl::framebuffer* framebuffer) noexcept +{ + shadow_framebuffer = framebuffer; +} + +void directional_light::set_shadow_bias(float bias) noexcept +{ + shadow_bias = bias; +} + +void directional_light::set_shadow_cascade_count(unsigned int count) noexcept +{ + shadow_cascade_count = count; + shadow_cascade_distances.resize(shadow_cascade_count); + shadow_cascade_matrices.resize(shadow_cascade_count); +} + +void directional_light::set_shadow_cascade_coverage(float factor) noexcept +{ + shadow_cascade_coverage = factor; +} + +void directional_light::set_shadow_cascade_distribution(float weight) noexcept +{ + shadow_cascade_distribution = weight; +} + +void directional_light::set_light_texture(const gl::texture_2d* texture) +{ + light_texture = texture; +} + +void directional_light::set_light_texture_opacity(float opacity) +{ + light_texture_opacity[1] = opacity; +} + +void directional_light::set_light_texture_scale(const float2& scale) +{ + light_texture_scale[1] = scale; +} + +void directional_light::update_tweens() +{ + light::update_tweens(); + direction.update(); + + if (light_texture) + { + light_texture_opacity.update(); + light_texture_scale.update(); + } +} + +void directional_light::transformed() +{ + direction[1] = math::normalize(get_transform().rotation * config::global_forward); +} + +} // namespace scene diff --git a/src/engine/scene/directional-light.hpp b/src/engine/scene/directional-light.hpp new file mode 100644 index 0000000..cb35f3e --- /dev/null +++ b/src/engine/scene/directional-light.hpp @@ -0,0 +1,265 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_SCENE_DIRECTIONAL_LIGHT_HPP +#define ANTKEEPER_SCENE_DIRECTIONAL_LIGHT_HPP + +#include +#include +#include +#include + +namespace scene { + +/** + * Light source with parallel rays and constant intensity. + */ +class directional_light: public light +{ +public: + /// Creates a directional light. + directional_light(); + + /// Returns light_type::directional. + virtual light_type get_light_type() const; + + /// Returns the normalized direction vector of the light. + const float3& get_direction() const; + + /// @copydoc object_base::update_tweens(); + virtual void update_tweens(); + + /// @name Shadow + /// @{ + + /** + * Enables or disables shadow casting. + * + * @param caster `true` if the light should cast shadows, `false` otherwise. + */ + void set_shadow_caster(bool caster) noexcept; + + /** + * Sets the shadow map framebuffer. + * + * @param framebuffer Pointer to a shadow map framebuffer. + */ + void set_shadow_framebuffer(const gl::framebuffer* framebuffer) noexcept; + + /** + * Sets the shadow bias factor for reducing self-shadowing. + * + * @param bias Shadow bias factor. + */ + void set_shadow_bias(float bias) noexcept; + + /** + * Sets the number of shadow cascades. + * + * @param count Number of shadow cascades. + */ + void set_shadow_cascade_count(unsigned int count) noexcept; + + /** + * Sets the shadow cascade coverage factor. + * + * @param factor Percentage of the view frustum clipping range covered by shadow cascades. A value of `1.0` results in full coverage of the view frustum clipping range, `0.5` results in coverage of half of the clipping range, etc. + */ + void set_shadow_cascade_coverage(float factor) noexcept; + + /** + * Sets the shadow cascade distribution. + * + * @param weight Linear interpolation weight between uniform and logarithmic cascade distributions. A weight of `0.0` results in a uniform cascade distribution, while `1.0` results in a logarithmic distribution. + */ + void set_shadow_cascade_distribution(float weight) noexcept; + + /// Returns `true` if the light casts shadows, `false` otherwise. + bool is_shadow_caster() const noexcept; + + /// Returns the shadow map framebuffer, of `nullptr` if no shadow map framebuffer is set. + const gl::framebuffer* get_shadow_framebuffer() const noexcept; + + /// Returns the shadow bias factor. + float get_shadow_bias() const noexcept; + + /// Returns the number of shadow cascades. + unsigned int get_shadow_cascade_count() const noexcept; + + /// Returns the shadow cascade coverage factor. + float get_shadow_cascade_coverage() const noexcept; + + /// Returns the shadow cascade distribution weight. + float get_shadow_cascade_distribution() const noexcept; + + /// Returns the array of shadow cascade far clipping plane distances. + float* get_shadow_cascade_distances() const noexcept; + + /// Returns the array of world-space to cascade texture-space transformation matrices. + float4x4* get_shadow_cascade_matrices() const noexcept; + + /// @} + + /// @name Light texture + /// @{ + + /** + * Sets the light texture, also known as a gobo, cucoloris, or cookie. + * + * @param texture Light texture. + */ + void set_light_texture(const gl::texture_2d* texture); + + /** + * Sets the opacity of the light texture. + * + * @param opacity Light texture opacity, on `[0, 1]`. + */ + void set_light_texture_opacity(float opacity); + + /** + * Sets the scale of the light texture. + * + * @param scale Scale of the light texture. + */ + void set_light_texture_scale(const float2& scale); + + /// Returns the light texture for this light, or `nullptr` if no light texture is set. + const gl::texture_2d* get_light_texture() const; + + /// Returns the light texture opacity. + float get_light_texture_opacity() const; + + /// Returns the light texture scale. + const float2& get_light_texture_scale() const; + + /// Returns the light direction tween. + const tween& get_direction_tween() const; + + /// Returns the light texture opacity tween. + const tween& get_light_texture_opacity_tween() const; + + /// Returns the light texture scale tween. + const tween& get_light_texture_scale_tween() const; + + /// @} + +private: + virtual void transformed(); + + tween direction; + + bool shadow_caster; + const gl::framebuffer* shadow_framebuffer; + float shadow_bias; + unsigned int shadow_cascade_count; + float shadow_cascade_coverage; + float shadow_cascade_distribution; + mutable std::vector shadow_cascade_distances; + mutable std::vector shadow_cascade_matrices; + + const gl::texture_2d* light_texture; + tween light_texture_opacity; + tween light_texture_scale; + +}; + +inline light_type directional_light::get_light_type() const +{ + return light_type::directional; +} + +inline const float3& directional_light::get_direction() const +{ + return direction[1]; +} + +inline bool directional_light::is_shadow_caster() const noexcept +{ + return shadow_caster; +} + +inline const gl::framebuffer* directional_light::get_shadow_framebuffer() const noexcept +{ + return shadow_framebuffer; +} + +inline float directional_light::get_shadow_bias() const noexcept +{ + return shadow_bias; +} + +inline unsigned int directional_light::get_shadow_cascade_count() const noexcept +{ + return shadow_cascade_count; +} + +inline float directional_light::get_shadow_cascade_coverage() const noexcept +{ + return shadow_cascade_coverage; +} + +inline float directional_light::get_shadow_cascade_distribution() const noexcept +{ + return shadow_cascade_distribution; +} + +inline float* directional_light::get_shadow_cascade_distances() const noexcept +{ + return shadow_cascade_distances.data(); +} + +inline float4x4* directional_light::get_shadow_cascade_matrices() const noexcept +{ + return shadow_cascade_matrices.data(); +} + +inline const gl::texture_2d* directional_light::get_light_texture() const +{ + return light_texture; +} + +inline float directional_light::get_light_texture_opacity() const +{ + return light_texture_opacity[1]; +} + +inline const float2& directional_light::get_light_texture_scale() const +{ + return light_texture_scale[1]; +} + +inline const tween& directional_light::get_direction_tween() const +{ + return direction; +} + +inline const tween& directional_light::get_light_texture_opacity_tween() const +{ + return light_texture_opacity; +} + +inline const tween& directional_light::get_light_texture_scale_tween() const +{ + return light_texture_scale; +} + +} // namespace scene + +#endif // ANTKEEPER_SCENE_DIRECTIONAL_LIGHT_HPP diff --git a/src/engine/scene/light.cpp b/src/engine/scene/light.cpp new file mode 100644 index 0000000..e981ba0 --- /dev/null +++ b/src/engine/scene/light.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include + +namespace scene { + +light::light(): + local_bounds{{0.0f, 0.0f, 0.0f}, 0.0f}, + world_bounds{{0.0f, 0.0f, 0.0f}, 0.0f}, + color(float3{1.0f, 1.0f, 1.0f}, math::lerp), + intensity(1.0f, math::lerp), + scaled_color(float3{1.0f, 1.0f, 1.0f}, math::lerp) +{} + +void light::set_color(const float3& color) +{ + this->color[1] = color; + scaled_color[1] = color * intensity[1]; +} + +void light::set_intensity(float intensity) +{ + this->intensity[1] = intensity; + scaled_color[1] = color[1] * intensity; +} + +void light::update_tweens() +{ + object_base::update_tweens(); + color.update(); + intensity.update(); + scaled_color.update(); +} + +void light::transformed() +{ + world_bounds = {get_translation(), 0.0f}; +} + +} // namespace scene diff --git a/src/engine/scene/light.hpp b/src/engine/scene/light.hpp new file mode 100644 index 0000000..67d00d6 --- /dev/null +++ b/src/engine/scene/light.hpp @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_SCENE_LIGHT_HPP +#define ANTKEEPER_SCENE_LIGHT_HPP + +#include +#include +#include + +namespace scene { + +/// Light object type enumerations. +enum class light_type +{ + /// Denotes an ambient light. + ambient, + + /// Denotes a directional light. + directional, + + /// Denotes a point light. + point, + + /// Denotes a spot light. + spot +}; + +/** + * Abstract base class for light objects. + */ +class light: public object +{ +public: + typedef geom::sphere sphere_type; + + /// Creates a light. + light(); + + /// Returns an enumeration denoting the light object type. + virtual light_type get_light_type() const = 0; + + /** + * Sets the color of the light. + * + * @param color Scene-linear light color. + */ + void set_color(const float3& color); + + /** + * Sets the intensity of the light. + * + * @param intensity Light intensity. + */ + void set_intensity(float intensity); + + /// Returns the local-space bounding volume of the light. + virtual const bounding_volume_type& get_local_bounds() const; + + /// Returns the world-space bounding volume of the light. + virtual const bounding_volume_type& get_world_bounds() const; + + /// Returns the light color. + const float3& get_color() const; + + /// Returns the light intensity. + float get_intensity() const; + + /// Returns the intensity-scaled light color. + const float3& get_scaled_color() const; + + const tween& get_color_tween() const; + const tween& get_intensity_tween() const; + const tween& get_scaled_color_tween() const; + + /// @copydoc object_base::update_tweens(); + virtual void update_tweens(); + +private: + virtual void transformed(); + + tween color; + tween intensity; + tween scaled_color; + sphere_type local_bounds; + sphere_type world_bounds; +}; + +inline const typename object_base::bounding_volume_type& light::get_local_bounds() const +{ + return local_bounds; +} + +inline const typename object_base::bounding_volume_type& light::get_world_bounds() const +{ + return world_bounds; +} + +inline const float3& light::get_color() const +{ + return color[1]; +} + +inline float light::get_intensity() const +{ + return intensity[1]; +} + +inline const float3& light::get_scaled_color() const +{ + return scaled_color[1]; +} + +inline const tween& light::get_color_tween() const +{ + return color; +} + +inline const tween& light::get_intensity_tween() const +{ + return intensity; +} + +inline const tween& light::get_scaled_color_tween() const +{ + return scaled_color; +} + +} // namespace scene + +#endif // ANTKEEPER_SCENE_LIGHT_HPP diff --git a/src/engine/scene/lod-group.cpp b/src/engine/scene/lod-group.cpp new file mode 100644 index 0000000..2508965 --- /dev/null +++ b/src/engine/scene/lod-group.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include + +namespace scene { + +lod_group::lod_group(std::size_t level_count): + local_bounds{{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}}, + world_bounds{{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}} +{ + resize(level_count); +} + +lod_group::lod_group(): + lod_group(1) +{} + +void lod_group::resize(std::size_t level_count) +{ + levels.resize(level_count); +} + +std::size_t lod_group::select_lod(const camera& camera) const +{ + float distance = camera.get_view_frustum().get_near().signed_distance(get_translation()); + + if (distance < 300.0f) + return 0; + else if (distance < 500.0f) + return 1; + else if (distance < 600.0f) + return 2; + + return 3; +} + +void lod_group::add_object(std::size_t level, object_base* object) +{ + levels[level].push_back(object); +} + +void lod_group::remove_object(std::size_t level, object_base* object) +{ + levels[level].remove(object); +} + +void lod_group::remove_objects(std::size_t level) +{ + levels[level].clear(); +} + +void lod_group::update_bounds() +{ + world_bounds = {get_translation(), get_translation()}; +} + +void lod_group::transformed() +{ + update_bounds(); +} + +} // namespace scene diff --git a/src/engine/scene/lod-group.hpp b/src/engine/scene/lod-group.hpp new file mode 100644 index 0000000..aebc4f4 --- /dev/null +++ b/src/engine/scene/lod-group.hpp @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_SCENE_LOD_GROUP_HPP +#define ANTKEEPER_SCENE_LOD_GROUP_HPP + +#include +#include +#include +#include + +namespace scene { + +class camera; + +class lod_group: public object +{ +public: + typedef geom::aabb aabb_type; + + /** + * Creates a LOD group. + * + * @param level_count Number of detail levels in the group. + */ + lod_group(std::size_t level_count); + + /// Creates a LOD group with one level of detail. + lod_group(); + + /** + * Resizes the LOD group to accommodate the specified number of detail levels. + * + * @param level_count Number of desired detail levels in the group. + */ + void resize(std::size_t level_count); + + /** + * Selects the appropriate level of detail for a camera. + * + * @param camera Camera for which the LOD should be selected. + * @return Selected level of detail. + */ + std::size_t select_lod(const camera& camera) const; + + /** + * Adds an object to the LOD group. + * + * @param level Level of detail of the object to add. + * @param object Object to add. + */ + void add_object(std::size_t level, object_base* object); + + /** + * Removes an object from the LOD group. + * + * @param level Level of detail of the object to remove. + * @param object Object to remove. + */ + void remove_object(std::size_t level, object_base* object); + + /** + * Removes all objects with the specified level of detail. + * + * @param level Level of detail of the objects to remove. + */ + void remove_objects(std::size_t level); + + virtual const bounding_volume_type& get_local_bounds() const; + virtual const bounding_volume_type& get_world_bounds() const; + + /// Returns the number of detail levels in the group. + std::size_t get_level_count() const; + + /** + * Returns a list containing all objects in the LOD group with the specified detail level. + * + * @param level Level of detail. + * @return List of all objects in the group with the specified detail level. + */ + const std::list& get_objects(std::size_t level) const; + +private: + void update_bounds(); + virtual void transformed(); + + aabb_type local_bounds; + aabb_type world_bounds; + std::vector> levels; +}; + +inline const typename object_base::bounding_volume_type& lod_group::get_local_bounds() const +{ + return local_bounds; +} + +inline const typename object_base::bounding_volume_type& lod_group::get_world_bounds() const +{ + return world_bounds; +} + +inline std::size_t lod_group::get_level_count() const +{ + return levels.size(); +} + +inline const std::list& lod_group::get_objects(std::size_t level) const +{ + return levels[level]; +} + +} // namespace scene + +#endif // ANTKEEPER_SCENE_LOD_GROUP_HPP + diff --git a/src/engine/scene/model-instance.cpp b/src/engine/scene/model-instance.cpp new file mode 100644 index 0000000..d5456e2 --- /dev/null +++ b/src/engine/scene/model-instance.cpp @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include + +namespace scene { + +model_instance::model_instance(render::model* model): + model(nullptr), + local_bounds{{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}}, + world_bounds{{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}}, + instanced(false), + instance_count(0) +{ + set_model(model); + update_bounds(); +} + +model_instance::model_instance(): + model_instance(nullptr) +{} + +model_instance::model_instance(const model_instance& other) +{ + *this = other; +} + +model_instance& model_instance::operator=(const model_instance& other) +{ + local_bounds = other.local_bounds; + world_bounds = other.world_bounds; + model = other.model; + pose = other.pose; + materials = other.materials; + instanced = other.instanced; + instance_count = other.instance_count; + return *this; +} + +void model_instance::set_model(render::model* model) +{ + this->model = model; + + if (model) + { + materials.resize(model->get_groups()->size()); + reset_materials(); + + pose = model->get_skeleton().bind_pose; + ::concatenate(pose, pose); + } + else + { + pose.clear(); + } + + update_bounds(); +} + +void model_instance::set_material(std::size_t group_index, render::material* material) +{ + materials[group_index] = material; +} + +void model_instance::set_instanced(bool instanced, std::size_t instance_count) +{ + this->instanced = instanced; + this->instance_count = (instanced) ? instance_count : 0; +} + +void model_instance::reset_materials() +{ + std::fill(materials.begin(), materials.end(), nullptr); +} + +void model_instance::update_bounds() +{ + if (model) + { + local_bounds = aabb_type::transform(model->get_bounds(), get_transform()); + transformed(); + } + else + { + local_bounds = {{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}}; + world_bounds = {get_translation(), get_translation()}; + } +} + +void model_instance::transformed() +{ + world_bounds = aabb_type::transform(local_bounds, get_transform()); +} + +void model_instance::update_tweens() +{ + object_base::update_tweens(); + + // Update model material tweens + if (model) + { + for (render::model_group* group: *model->get_groups()) + { + render::material* material = group->get_material(); + if (material) + { + material->update_tweens(); + } + } + } + + // Update material override tweens + for (render::material* material: materials) + { + if (material) + { + material->update_tweens(); + } + } +} + +} // namespace scene diff --git a/src/engine/scene/model-instance.hpp b/src/engine/scene/model-instance.hpp new file mode 100644 index 0000000..30d2e72 --- /dev/null +++ b/src/engine/scene/model-instance.hpp @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_SCENE_MODEL_INSTANCE_HPP +#define ANTKEEPER_SCENE_MODEL_INSTANCE_HPP + +#include +#include +#include +#include +#include + +namespace scene { + +class model_instance: public object +{ +public: + typedef geom::aabb aabb_type; + + explicit model_instance(render::model* model); + model_instance(); + model_instance(const model_instance& other); + model_instance& operator=(const model_instance& other); + + /** + * Sets the model with which this model instance is associated. This will reset the pose and all overwritten materials. + */ + void set_model(render::model* model); + + /** + * Overwrites the material of a model group for this model instance. + * + * @param group_index Index of a model group. + * @param material Pointer to the material which should overwrite the model group's material. A value of `nullptr` indicates the material will not be overwritten. + */ + void set_material(std::size_t group_index, render::material* material); + + void set_instanced(bool instanced, std::size_t instance_count = 1); + + /** + * Resets all overwritten materials. + */ + void reset_materials(); + + virtual const bounding_volume_type& get_local_bounds() const; + virtual const bounding_volume_type& get_world_bounds() const; + + const render::model* get_model() const; + render::model* get_model(); + + /// Returns the skeletal animation pose of this model. + const pose& get_pose() const; + + /// @copydoc model_instance::get_pose() const + pose& get_pose(); + + const std::vector* get_materials() const; + + bool is_instanced() const; + std::size_t get_instance_count() const; + + virtual void update_tweens(); + + void update_bounds(); + +private: + virtual void transformed(); + + render::model* model; + ::pose pose; + std::vector materials; + aabb_type local_bounds; + aabb_type world_bounds; + bool instanced; + std::size_t instance_count; +}; + +inline const typename object_base::bounding_volume_type& model_instance::get_local_bounds() const +{ + return local_bounds; +} + +inline const typename object_base::bounding_volume_type& model_instance::get_world_bounds() const +{ + return world_bounds; +} + +inline const render::model* model_instance::get_model() const +{ + return model; +} + +inline render::model* model_instance::get_model() +{ + return model; +} + +inline const pose& model_instance::get_pose() const +{ + return pose; +} + +inline pose&model_instance::get_pose() +{ + return pose; +} + +inline const std::vector* model_instance::get_materials() const +{ + return &materials; +} + +inline bool model_instance::is_instanced() const +{ + return instanced; +} + +inline std::size_t model_instance::get_instance_count() const +{ + return instance_count; +} + +} // namespace scene + +#endif // ANTKEEPER_SCENE_MODEL_INSTANCE_HPP + diff --git a/src/engine/scene/object.cpp b/src/engine/scene/object.cpp new file mode 100644 index 0000000..13e3437 --- /dev/null +++ b/src/engine/scene/object.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include + +namespace scene { + +typename object_base::transform_type object_base::interpolate_transforms(const transform_type& x, const transform_type& y, float a) +{ + return + { + math::lerp(x.translation, y.translation, a), + math::nlerp(x.rotation, y.rotation, a), + math::lerp(x.scale, y.scale, a), + }; +} + +object_base::object_base(): + active(true), + transform(math::transform::identity, interpolate_transforms), + culling_mask(nullptr) +{} + +void object_base::set_culling_mask(const bounding_volume_type* culling_mask) +{ + this->culling_mask = culling_mask; +} + +std::size_t object_base::next_object_type_id() +{ + static std::atomic id{0}; + return id++; +} + +void object_base::render(const render::context& ctx, render::queue& queue) const +{} + +void object_base::update_tweens() +{ + transform.update(); +} + +void object_base::look_at(const vector_type& position, const vector_type& target, const vector_type& up) +{ + transform[1].translation = position; + transform[1].rotation = math::look_rotation(math::normalize(math::sub(target, position)), up); + transformed(); +} + +void object_base::transformed() +{} + +} // namespace scene diff --git a/src/engine/scene/object.hpp b/src/engine/scene/object.hpp new file mode 100644 index 0000000..1da35da --- /dev/null +++ b/src/engine/scene/object.hpp @@ -0,0 +1,268 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_SCENE_OBJECT_HPP +#define ANTKEEPER_SCENE_OBJECT_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace scene { + +/** + * Internal base class for scene objects. + */ +class object_base +{ +public: + typedef math::vector vector_type; + typedef math::quaternion quaternion_type; + typedef math::transform transform_type; + typedef geom::bounding_volume bounding_volume_type; + + /// Returns the type ID for this scene object type. + virtual const std::size_t get_object_type_id() const = 0; + + /** + * Creates a scene object base. + */ + object_base(); + + /** + * Destroys a scene object base. + */ + virtual ~object_base() = default; + + /** + * Adds a render operation describing this object to a render queue. + * + * @param ctx Render context. + * @param queue Render queue. + * + * @see render::context + * @see render::operation + */ + virtual void render(const render::context& ctx, render::queue& queue) const; + + /** + * Updates all tweens in the scene object. + */ + virtual void update_tweens(); + + /** + * Activates or deactivates the scene object. + */ + void set_active(bool active); + + /** + * + */ + void look_at(const vector_type& position, const vector_type& target, const vector_type& up); + + /** + * Sets the scene object's transform. + */ + void set_transform(const transform_type& transform); + + /** + * Sets the scene object's translation. + */ + void set_translation(const vector_type& translation); + + /** + * Sets the scene object's rotation. + */ + void set_rotation(const quaternion_type& rotation); + + /** + * Sets the scene object's scale. + */ + void set_scale(const vector_type& scale); + + /** + * Sets a culling mask for the object, which will be used for view-frustum culling instead of the object's bounds. + */ + void set_culling_mask(const bounding_volume_type* culling_mask); + + /// Returns whether the scene object is active. + bool is_active() const; + + /** + * Returns the transform. + */ + const transform_type& get_transform() const; + + /** + * Returns the transform's translation vector. + */ + const vector_type& get_translation() const; + + /** + * Returns the transform's rotation quaternion. + */ + const quaternion_type& get_rotation() const; + + /** + * Returns the transform's scale vector. + */ + const vector_type& get_scale() const; + + /** + * Returns the transform tween. + */ + const tween& get_transform_tween() const; + tween& get_transform_tween(); + + /** + * Returns the local-space (untransformed) bounds of the object. + */ + virtual const bounding_volume_type& get_local_bounds() const = 0; + + /** + * Returns the world-space (transformed) bounds of the object. + */ + virtual const bounding_volume_type& get_world_bounds() const = 0; + + /** + * Returns the culling mask of the object. + */ + const bounding_volume_type* get_culling_mask() const; + +protected: + static std::size_t next_object_type_id(); + +private: + /// Interpolates between two transforms. + static transform_type interpolate_transforms(const transform_type& x, const transform_type& y, float a); + + /** + * Called every time the scene object's tranform is changed. + */ + virtual void transformed(); + + bool active; + tween transform; + const bounding_volume_type* culling_mask; +}; + +inline void object_base::set_active(bool active) +{ + this->active = active; +} + +inline void object_base::set_transform(const transform_type& transform) +{ + this->transform[1] = transform; + transformed(); +} + +inline void object_base::set_translation(const vector_type& translation) +{ + transform[1].translation = translation; + transformed(); +} + +inline void object_base::set_rotation(const quaternion_type& rotation) +{ + transform[1].rotation = rotation; + transformed(); +} + +inline void object_base::set_scale(const vector_type& scale) +{ + transform[1].scale = scale; + transformed(); +} + +inline bool object_base::is_active() const +{ + return active; +} + +inline const typename object_base::transform_type& object_base::get_transform() const +{ + return transform[1]; +} + +inline const typename object_base::vector_type& object_base::get_translation() const +{ + return get_transform().translation; +} + +inline const typename object_base::quaternion_type& object_base::get_rotation() const +{ + return get_transform().rotation; +} + +inline const typename object_base::vector_type& object_base::get_scale() const +{ + return get_transform().scale; +} + +inline const tween& object_base::get_transform_tween() const +{ + return transform; +} + +inline tween& object_base::get_transform_tween() +{ + return transform; +} + +inline const typename object_base::bounding_volume_type* object_base::get_culling_mask() const +{ + return culling_mask; +} + +/** + * Abstract base class for lights, cameras, model instances, and other scene objects. + * + * @tparam T This should be the same class that's inheriting from the scene object, in order to give it a valid type-specific ID. + */ +template +class object: public object_base +{ +public: + /// Unique type ID for this scene object type. + static const std::atomic object_type_id; + + /// @copydoc object_base::get_object_type_id() const + virtual const std::size_t get_object_type_id() const final; +}; + +template +const std::atomic object::object_type_id{object_base::next_object_type_id()}; + +template +inline const std::size_t object::get_object_type_id() const +{ + return object_type_id; +} + +} // namespace scene + +#endif // ANTKEEPER_SCENE_OBJECT_HPP + diff --git a/src/engine/scene/point-light.cpp b/src/engine/scene/point-light.cpp new file mode 100644 index 0000000..9e62f8e --- /dev/null +++ b/src/engine/scene/point-light.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include + +namespace scene { + +point_light::point_light(): + attenuation(float3{1, 0, 0}, math::lerp) +{} + +void point_light::set_attenuation(const float3& attenuation) +{ + this->attenuation[1] = attenuation; +} + +void point_light::update_tweens() +{ + light::update_tweens(); + attenuation.update(); +} + +} // namespace scene diff --git a/src/engine/scene/point-light.hpp b/src/engine/scene/point-light.hpp new file mode 100644 index 0000000..326a5b2 --- /dev/null +++ b/src/engine/scene/point-light.hpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_SCENE_POINT_LIGHT_HPP +#define ANTKEEPER_SCENE_POINT_LIGHT_HPP + +#include +#include + +namespace scene { + +/** + * Light source that radiates outward from a point. + */ +class point_light: public light +{ +public: + point_light(); + + /// Returns light_type::point + virtual light_type get_light_type() const; + + /** + * Sets the attenuation factors of the light. + * + * @param attenuation Vector containing the constant, linear, and quadratic attenuation factors, as x, y, and z, respectively. + */ + void set_attenuation(const float3& attenuation); + + /// Returns the attenuation factors of the light. + const float3& get_attenuation() const; + + /// Returns the attenuation tween. + const tween& get_attenuation_tween() const; + + /// @copydoc object_base::update_tweens(); + virtual void update_tweens(); + +private: + tween attenuation; +}; + +inline light_type point_light::get_light_type() const +{ + return light_type::point; +} + +inline const float3& point_light::get_attenuation() const +{ + return attenuation[1]; +} + +inline const tween& point_light::get_attenuation_tween() const +{ + return attenuation; +} + +} // namespace scene + +#endif // ANTKEEPER_SCENE_POINT_LIGHT_HPP + diff --git a/src/engine/scene/scene.hpp b/src/engine/scene/scene.hpp new file mode 100644 index 0000000..559dc85 --- /dev/null +++ b/src/engine/scene/scene.hpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_SCENE_HPP +#define ANTKEEPER_SCENE_HPP + +/// 3D scene +namespace scene {} + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif // ANTKEEPER_SCENE_HPP diff --git a/src/engine/scene/spot-light.cpp b/src/engine/scene/spot-light.cpp new file mode 100644 index 0000000..4a5811e --- /dev/null +++ b/src/engine/scene/spot-light.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include +#include +#include + +namespace scene { + +static float3 interpolate_direction(const float3& x, const float3& y, float a) +{ + math::quaternion q0 = math::rotation(config::global_forward, x); + math::quaternion q1 = math::rotation(config::global_forward, y); + return math::normalize(math::slerp(q0, q1, a) * config::global_forward); +} + +spot_light::spot_light(): + direction(config::global_forward, interpolate_direction), + attenuation(float3{1, 0, 0}, math::lerp), + cutoff(float2{math::pi, math::pi}, math::lerp), + cosine_cutoff(float2{std::cos(math::pi), std::cos(math::pi)}, math::lerp) +{} + +void spot_light::set_attenuation(const float3& attenuation) +{ + this->attenuation[1] = attenuation; +} + +void spot_light::set_cutoff(const float2& cutoff) +{ + this->cutoff[1] = cutoff; + this->cosine_cutoff[1] = {std::cos(cutoff.x()), std::cos(cutoff.y())}; +} + +void spot_light::update_tweens() +{ + light::update_tweens(); + direction.update(); + attenuation.update(); + cutoff.update(); + cosine_cutoff.update(); +} + +void spot_light::transformed() +{ + direction[1] = math::normalize(get_transform().rotation * config::global_forward); +} + +} // namespace scene diff --git a/src/engine/scene/spot-light.hpp b/src/engine/scene/spot-light.hpp new file mode 100644 index 0000000..f46f442 --- /dev/null +++ b/src/engine/scene/spot-light.hpp @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_SCENE_SPOT_LIGHT_HPP +#define ANTKEEPER_SCENE_SPOT_LIGHT_HPP + +#include +#include + +namespace scene { + +/** + * Directional cone light source. + */ +class spot_light: public light +{ +public: + /// Creates a spot light. + spot_light(); + + /// Returns light_type::spot + virtual light_type get_light_type() const; + + /** + * Sets the attenuation factors of the light. + * + * @param attenuation Vector containing the constant, linear, and quadratic attenuation factors, as x, y, and z, respectively. + */ + void set_attenuation(const float3& attenuation); + + /** + * Sets the spot light cutoff angles. + * + * @param cutoff Vector containing the inner and outer cutoff angles, as x and y, respectively. + */ + void set_cutoff(const float2& cutoff); + + /// Returns the direction vector. + const float3& get_direction() const; + + /// Returns the attenuation factors of the light. + const float3& get_attenuation() const; + + /// Returns the spot light cutoff angles. + const float2& get_cutoff() const; + + /// Returns the cosine of the spot light cutoff angles. + const float2& get_cosine_cutoff() const; + + /// Returns the direction tween. + const tween& get_direction_tween() const; + + /// Returns the attenuation tween. + const tween& get_attenuation_tween() const; + + /// Returns the cutoff tween. + const tween& get_cutoff_tween() const; + + /// Returns the cosine cutoff tween. + const tween& get_cosine_cutoff_tween() const; + + /// @copydoc object_base::update_tweens(); + virtual void update_tweens(); + +private: + virtual void transformed(); + + tween direction; + tween attenuation; + tween cutoff; + tween cosine_cutoff; +}; + +inline light_type spot_light::get_light_type() const +{ + return light_type::spot; +} + +inline const float3& spot_light::get_direction() const +{ + return direction[1]; +} + +inline const float3& spot_light::get_attenuation() const +{ + return attenuation[1]; +} + +inline const float2& spot_light::get_cutoff() const +{ + return cutoff[1]; +} + +inline const float2& spot_light::get_cosine_cutoff() const +{ + return cosine_cutoff[1]; +} + +inline const tween& spot_light::get_direction_tween() const +{ + return direction; +} + +inline const tween& spot_light::get_attenuation_tween() const +{ + return attenuation; +} + +inline const tween& spot_light::get_cutoff_tween() const +{ + return cutoff; +} + +inline const tween& spot_light::get_cosine_cutoff_tween() const +{ + return cosine_cutoff; +} + +} // namespace scene + +#endif // ANTKEEPER_SCENE_SPOT_LIGHT_HPP + diff --git a/src/engine/scene/text.cpp b/src/engine/scene/text.cpp new file mode 100644 index 0000000..bfb75f4 --- /dev/null +++ b/src/engine/scene/text.cpp @@ -0,0 +1,341 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include +#include + +namespace scene { + +text::text(): + local_bounds{{0, 0, 0}, {0, 0, 0}}, + world_bounds{{0, 0, 0}, {0, 0, 0}}, + material(nullptr), + font(nullptr), + direction(type::text_direction::ltr), + content_u8(std::string()), + content_u32(std::u32string()), + color({0.0f, 0.0f, 0.0f, 1.0f}), + vertex_stride(0), + vertex_count(0), + vao(nullptr), + vbo(nullptr) +{ + // Allocate VBO and VAO + vbo = new gl::vertex_buffer(0, nullptr, gl::buffer_usage::static_draw); + vao = new gl::vertex_array(); + + // Calculate vertex stride + vertex_stride = (3 + 2 + 4) * sizeof(float); + + // Init vertex attribute offset + std::size_t attribute_offset = 0; + + // Define vertex position attribute + gl::vertex_attribute position_attribute; + position_attribute.buffer = vbo; + position_attribute.offset = attribute_offset; + position_attribute.stride = vertex_stride; + position_attribute.type = gl::vertex_attribute_type::float_32; + position_attribute.components = 3; + attribute_offset += position_attribute.components * sizeof(float); + + // Define vertex UV attribute + gl::vertex_attribute uv_attribute; + uv_attribute.buffer = vbo; + uv_attribute.offset = attribute_offset; + uv_attribute.stride = vertex_stride; + uv_attribute.type = gl::vertex_attribute_type::float_32; + uv_attribute.components = 2; + attribute_offset += uv_attribute.components * sizeof(float); + + // Define vertex color attribute + gl::vertex_attribute color_attribute; + color_attribute.buffer = vbo; + color_attribute.offset = attribute_offset; + color_attribute.stride = vertex_stride; + color_attribute.type = gl::vertex_attribute_type::float_32; + color_attribute.components = 4; + attribute_offset += color_attribute.components * sizeof(float); + + // Bind vertex attributes to VAO + vao->bind(render::vertex_attribute::position, position_attribute); + vao->bind(render::vertex_attribute::uv, uv_attribute); + vao->bind(render::vertex_attribute::color, color_attribute); + + // Init render operation + render_op.material = nullptr; + render_op.bone_count = 0; + render_op.skinning_palette = nullptr; + render_op.vertex_array = vao; + render_op.drawing_mode = gl::drawing_mode::triangles; + render_op.start_index = 0; + render_op.index_count = 0; + render_op.instance_count = 0; +} + +text::~text() +{ + // Free VAO and VBO + delete vao; + delete vbo; +} + +void text::render(const render::context& ctx, render::queue& queue) const +{ + if (!vertex_count) + return; + + render_op.transform = math::matrix_cast(get_transform_tween().interpolate(ctx.alpha)); + render_op.depth = ctx.clip_near.signed_distance(math::vector(render_op.transform[3])); + queue.push_back(render_op); +} + +void text::refresh() +{ + update_content(); +} + +void text::set_material(render::material* material) +{ + this->material = material; + render_op.material = material; +} + +void text::set_font(const type::bitmap_font* font) +{ + if (this->font != font) + { + this->font = font; + + // Update text in VBO + update_content(); + } +} + +void text::set_direction(type::text_direction direction) +{ + if (this->direction != direction) + { + this->direction = direction; + + // Update text in VBO + update_content(); + } +} + +void text::set_content(const std::string& content) +{ + // If content has changed + if (content_u8 != content) + { + // Update UTF-8 content + content_u8 = content; + + // Convert UTF-8 content to UTF-32 + content_u32 = type::unicode::u32(content_u8); + + // Update text in VBO + update_content(); + } +} + +void text::set_color(const float4& color) +{ + this->color = color; + + // Update color in VBO + update_color(); +} + +void text::transformed() +{ + world_bounds = aabb_type::transform(local_bounds, get_transform()); +} + +void text::update_tweens() +{ + object_base::update_tweens(); + if (material) + { + material->update_tweens(); + } +} + +void text::update_content() +{ + // If no valid font or no text, clear vertex count + if (!font || content_u32.empty()) + { + vertex_count = 0; + render_op.index_count = vertex_count; + local_bounds = {{0, 0, 0}, {0, 0, 0}}; + transformed(); + return; + } + + // Calculate new vertex count and minimum vertex buffer size + std::size_t vertex_count = content_u32.length() * 6; + std::size_t min_vertex_buffer_size = vertex_count * vertex_stride; + + // Expand vertex data buffer to accommodate vertices + if (vertex_data.size() < min_vertex_buffer_size) + vertex_data.resize(min_vertex_buffer_size); + + // Get font metrics and bitmap + const type::font_metrics& font_metrics = font->get_font_metrics(); + const image& font_bitmap = font->get_bitmap(); + + // Init pen position + float2 pen_position = {0.0f, 0.0f}; + + // Reset local-space bounds + local_bounds = {{0, 0, 0}, {0, 0, 0}}; + + // Generate vertex data + char32_t previous_code = 0; + float* v = reinterpret_cast(vertex_data.data()); + for (char32_t code: content_u32) + { + // Apply kerning + if (previous_code) + { + pen_position.x() += font->get_kerning(previous_code, code).x(); + } + + if (font->contains(code)) + { + // Get glyph + const type::bitmap_glyph& glyph = font->get_glyph(code); + + // Calculate vertex positions + float2 positions[6]; + positions[0] = pen_position + glyph.metrics.horizontal_bearing; + positions[1] = {positions[0].x(), positions[0].y() - glyph.metrics.height}; + positions[2] = {positions[0].x() + glyph.metrics.width, positions[1].y()}; + positions[3] = {positions[2].x(), positions[0].y()}; + positions[4] = positions[0]; + positions[5] = positions[2]; + + // Calculate vertex UVs + float2 uvs[6]; + uvs[0] = {static_cast(glyph.position.x()), static_cast(glyph.position.y())}; + uvs[1] = {uvs[0].x(), uvs[0].y() + glyph.metrics.height}; + uvs[2] = {uvs[0].x() + glyph.metrics.width, uvs[1].y()}; + uvs[3] = {uvs[2].x(), uvs[0].y()}; + uvs[4] = uvs[0]; + uvs[5] = uvs[2]; + + for (int i = 0; i < 6; ++i) + { + // Round positions + positions[i].x() = std::round(positions[i].x()); + positions[i].y() = std::round(positions[i].y()); + + // Normalize UVs + uvs[i].x() = uvs[i].x() / static_cast(font_bitmap.get_width()); + uvs[i].y() = uvs[i].y() / static_cast(font_bitmap.get_height()); + } + + // Add vertex to vertex data buffer + for (int i = 0; i < 6; ++i) + { + *(v++) = positions[i].x(); + *(v++) = positions[i].y(); + *(v++) = 0.0f; + *(v++) = uvs[i].x(); + *(v++) = uvs[i].y(); + *(v++) = color[0]; + *(v++) = color[1]; + *(v++) = color[2]; + *(v++) = color[3]; + } + + // Advance pen position + pen_position.x() += glyph.metrics.horizontal_advance; + + // Update local-space bounds + for (int i = 0; i < 4; ++i) + { + const float2& position = positions[i]; + for (int j = 0; j < 2; ++j) + { + local_bounds.min_point[j] = std::min(local_bounds.min_point[j], position[j]); + local_bounds.max_point[j] = std::max(local_bounds.max_point[j], position[j]); + } + } + } + else + { + // Glyph not in font, zero vertex data + for (std::size_t i = 0; i < (6 * 9); ++i) + *(v++) = 0.0f; + } + + // Handle newlines + if (code == static_cast('\n')) + { + pen_position.x() = 0.0f; + pen_position.y() -= font_metrics.linegap; + } + + // Update previous UTF-32 character code + previous_code = code; + } + + // Resize VBO, if necessary, and upload vertex data + if (vertex_count > this->vertex_count) + { + this->vertex_count = vertex_count; + vbo->resize(min_vertex_buffer_size, vertex_data.data()); + } + else + { + vbo->write(0, min_vertex_buffer_size, vertex_data.data()); + } + + // Update vertex count + this->vertex_count = vertex_count; + render_op.index_count = vertex_count; + + // Update world-space bounds + transformed(); +} + +void text::update_color() +{ + float* v = reinterpret_cast(vertex_data.data()); + for (std::size_t i = 0; i < vertex_count; ++i) + { + // Skip vertex position (vec3) and vertex UV (vec2) + v += (3 + 2); + + // Update vertex color + *(v++) = color[0]; + *(v++) = color[1]; + *(v++) = color[2]; + *(v++) = color[3]; + } + + // Update VBO + vbo->write(0, vertex_count * vertex_stride, vertex_data.data()); +} + +} // namespace scene diff --git a/src/engine/scene/text.hpp b/src/engine/scene/text.hpp new file mode 100644 index 0000000..c66d036 --- /dev/null +++ b/src/engine/scene/text.hpp @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_SCENE_TEXT_HPP +#define ANTKEEPER_SCENE_TEXT_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace scene { + +/** + * Text scene object. + */ +class text: public object +{ +public: + typedef geom::aabb aabb_type; + + /// Constructs a text object. + text(); + + /// Destructs a text object. + ~text(); + + /// @copydoc scene::object_base::render(const render::context&, render::queue&) const + virtual void render(const render::context& ctx, render::queue& queue) const; + + /** + * Manually updates the text object if its font has been updated or altered in any way. + */ + void refresh(); + + /** + * Sets the text material. + * + * @param material Text material. + */ + void set_material(render::material* material); + + /** + * Sets the text font. + * + * @param font Pointer to a font. + */ + void set_font(const type::bitmap_font* font); + + /** + * Sets the direction of the text. + * + * @param direction Text direction. + */ + void set_direction(type::text_direction direction); + + /** + * Sets the text content. + * + * @param content UTF-8 string of text. + */ + void set_content(const std::string& content); + + /** + * Sets the text color. + * + * Text color is passed to the text's material shader as a vertex color. + * + * @param color RGBA color. + */ + void set_color(const float4& color); + + /// Returns the text material. + render::material* get_material() const; + + /// Returns the text font. + const type::bitmap_font* get_font() const; + + /// Returns the text direction. + const type::text_direction& get_direction() const; + + /// Returns the text content. + const std::string& get_content() const; + + /// Returns the text color. + const float4& get_color() const; + + /// @copydoc scene::object::get_local_bounds() const + virtual const bounding_volume_type& get_local_bounds() const; + + /// @copydoc scene::object::get_world_bounds() const + virtual const bounding_volume_type& get_world_bounds() const; + + /// @copydoc scene::object::update_tweens() + virtual void update_tweens(); + +private: + void update_content(); + void update_color(); + + virtual void transformed(); + + mutable render::operation render_op; + aabb_type local_bounds; + aabb_type world_bounds; + render::material* material; + const type::bitmap_font* font; + type::text_direction direction; + std::string content_u8; + std::u32string content_u32; + float4 color; + std::size_t vertex_stride; + std::size_t vertex_count; + std::vector vertex_data; + gl::vertex_array* vao; + gl::vertex_buffer* vbo; +}; + +inline render::material* text::get_material() const +{ + return material; +} + +inline const type::bitmap_font* text::get_font() const +{ + return font; +} + +inline const type::text_direction& text::get_direction() const +{ + return direction; +} + +inline const std::string& text::get_content() const +{ + return content_u8; +} + +inline const float4& text::get_color() const +{ + return color; +} + +inline const typename object_base::bounding_volume_type& text::get_local_bounds() const +{ + return local_bounds; +} + +inline const typename object_base::bounding_volume_type& text::get_world_bounds() const +{ + return world_bounds; +} + +} // namespace scene + +#endif // ANTKEEPER_SCENE_TEXT_HPP diff --git a/src/engine/type/bitmap-font.cpp b/src/engine/type/bitmap-font.cpp new file mode 100644 index 0000000..f9d7c47 --- /dev/null +++ b/src/engine/type/bitmap-font.cpp @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include + +namespace type { + +bitmap_font::bitmap_font(const font_metrics& metrics): + font(metrics) +{} + +bitmap_font::bitmap_font() +{} + +bool bitmap_font::contains(char32_t code) const +{ + return glyphs.count(code) != 0; +} + +void bitmap_font::insert(char32_t code, const bitmap_glyph& glyph) +{ + glyphs[code] = glyph; +} + +void bitmap_font::remove(char32_t code) +{ + if (auto it = glyphs.find(code); it != glyphs.end()) + glyphs.erase(it); +} + +void bitmap_font::clear() +{ + glyphs.clear(); +} + +bool bitmap_font::pack(bool resize) +{ + // Returns the smallest power of two that is not smaller than @p x. + auto ceil2 = [](unsigned int x) -> unsigned int + { + if (x <= 1) + return 1; + unsigned int y = 2; + --x; + while (x >>= 1) + y <<= 1; + return y; + }; + + // Calculate initial size of the font bitmap + unsigned int bitmap_w; + unsigned int bitmap_h; + if (resize) + { + // Find the maximum glyph dimensions + unsigned int max_glyph_w = 0; + unsigned int max_glyph_h = 0; + for (auto it = glyphs.begin(); it != glyphs.end(); ++it) + { + max_glyph_w = std::max(max_glyph_w, it->second.bitmap.get_width()); + max_glyph_h = std::max(max_glyph_h, it->second.bitmap.get_height()); + } + + // Find minimum power of two dimensions that can accommodate maximum glyph dimensions + bitmap_w = ceil2(max_glyph_w); + bitmap_h = ceil2(max_glyph_h); + } + else + { + bitmap_w = bitmap.get_width(); + bitmap_h = bitmap.get_height(); + } + + bool packed = false; + geom::rect_pack glyph_pack(bitmap_w, bitmap_h); + std::unordered_map::node_type*> glyph_map; + + while (!packed) + { + // For each glyph + for (auto it = glyphs.begin(); it != glyphs.end(); ++it) + { + // Attempt to pack glyph bitmap + const auto* node = glyph_pack.pack(it->second.bitmap.get_width(), it->second.bitmap.get_height()); + + // Abort if packing failed + if (!node) + break; + + // Map pack node to glyph character code + glyph_map[it->first] = node; + } + + // Check if not all glyphs were packed + if (glyph_map.size() != glyphs.size()) + { + if (!resize) + { + // No resize, packing failed + packed = false; + break; + } + + // Clear glyph map + glyph_map.clear(); + + // Clear glyph pack + glyph_pack.clear(); + + // Resize glyph pack + if (bitmap_w > bitmap_h) + bitmap_h = ceil2(++bitmap_h); + else + bitmap_w = ceil2(++bitmap_w); + glyph_pack.resize(bitmap_w, bitmap_h); + } + else + { + packed = true; + } + } + + // Copy glyph bitmaps into font bitmap + if (packed) + { + // Resize font bitmap + bitmap.resize(bitmap_w, bitmap_h); + + // For each glyph + for (auto it = glyphs.begin(); it != glyphs.end(); ++it) + { + // Find rect pack node corresponding to the glyph + const auto* node = glyph_map[it->first]; + + // Copy glyph bitmap data into font bitmap + image& glyph_bitmap = it->second.bitmap; + bitmap.copy(glyph_bitmap, glyph_bitmap.get_width(), glyph_bitmap.get_height(), 0, 0, node->bounds.min.x(), node->bounds.min.y()); + + // Record coordinates of glyph bitmap within font bitmap + it->second.position = {node->bounds.min.x(), node->bounds.min.y()}; + + // Clear glyph bitmap data + glyph_bitmap.resize(0, 0); + + } + } + + return packed; +} + +void bitmap_font::unpack(bool resize) +{ + for (auto it = glyphs.begin(); it != glyphs.end(); ++it) + { + bitmap_glyph& glyph = it->second; + + // Get glyph dimensions + unsigned int glyph_width = static_cast(glyph.metrics.width + 0.5f); + unsigned int glyph_height = static_cast(glyph.metrics.height + 0.5f); + + // Reformat glyph bitmap if necessary + if (!glyph.bitmap.compatible(bitmap)) + glyph.bitmap.format(bitmap.get_component_size(), bitmap.get_channel_count()); + + // Resize glyph bitmap if necessary + if (glyph.bitmap.get_width() != glyph_width || glyph.bitmap.get_height() != glyph_height) + glyph.bitmap.resize(glyph_width, glyph_height); + + // Copy pixel data from font bitmap to glyph bitmap + glyph.bitmap.copy(bitmap, glyph_width, glyph_height, glyph.position.x(), glyph.position.y()); + } + + // Free font bitmap pixel data + if (resize) + { + bitmap.resize(0, 0); + } +} + +const glyph_metrics& bitmap_font::get_glyph_metrics(char32_t code) const +{ + if (auto it = glyphs.find(code); it != glyphs.end()) + return it->second.metrics; + throw std::invalid_argument("Cannot fetch metrics of unknown bitmap glyph"); +} + +const bitmap_glyph& bitmap_font::get_glyph(char32_t code) const +{ + if (auto it = glyphs.find(code); it != glyphs.end()) + return it->second; + throw std::invalid_argument("Cannot get unknown bitmap glyph"); +} + +bitmap_glyph& bitmap_font::get_glyph(char32_t code) +{ + if (auto it = glyphs.find(code); it != glyphs.end()) + return it->second; + throw std::invalid_argument("Cannot get unknown bitmap glyph"); +} + +} // namespace type diff --git a/src/engine/type/bitmap-font.hpp b/src/engine/type/bitmap-font.hpp new file mode 100644 index 0000000..d615119 --- /dev/null +++ b/src/engine/type/bitmap-font.hpp @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_TYPE_BITMAP_FONT_HPP +#define ANTKEEPER_TYPE_BITMAP_FONT_HPP + +#include +#include +#include +#include + +namespace type { + +/** + * Raster font in which glyphs are stored as arrays of pixels. + * + * @see type::font + * @see type::font_metrics + * @see type::bitmap_glyph + * @see image + */ +class bitmap_font: public font +{ +public: + /** + * Creates a bitmap font and sets its metrics. + * + * @param metrics Metrics describing the font. + */ + bitmap_font(const font_metrics& metrics); + + /// Creates an empty bitmap font. + bitmap_font(); + + /// Destroys a bitmap font. + virtual ~bitmap_font() = default; + + /// @copydoc font::contains(char32_t) const + virtual bool contains(char32_t code) const; + + /** + * Inserts a glyph into the font. + * + * @param code UTF-32 character code of the glyph to insert. + * @param glyph Bitmap glyph data. + */ + void insert(char32_t code, const bitmap_glyph& glyph); + + /** + * Removes a glyph from the font. + * + * @param code UTF-32 character code of the glyph to remove. + */ + void remove(char32_t code); + + /** + * Removes all glyphs from the font. + */ + void clear(); + + /** + * Packs all glyph bitmaps into the font bitmap. + * + * @param resize Automatically resize the font bitmap to contain all glyphs. Bitmap size will start at the closest power of two to the largest glyph, then its dimensions will increase to the next power of two until its large enough that all glyphs can be contained. + * @return `true` if all glyphs were successfully packed, `false` otherwise. + * + * @except std::runtime_error Glyph bitmap format doesn't match font bitmap format. + * @except std::runtime_error Not enough space in font bitmap to pack glyph. + */ + bool pack(bool resize = true); + + /** + * Unpacks all glyph bitmaps from the font bitmap. + * + * @param resize Automatically resizes the font bitmap to zero. + */ + void unpack(bool resize = true); + + /// Returns a reference to the bitmap containing glyph pixel data. + const image& get_bitmap() const; + + /// @copydoc bitmap_font::get_bitmap() const + image& get_bitmap(); + + /** + * @copydoc font::get_glyph_metrics(char32_t) const + * + * @except std::invalid_argument Cannot fetch metrics of unknown bitmap glyph + */ + virtual const glyph_metrics& get_glyph_metrics(char32_t code) const; + + /** + * Returns a reference to the glyph corresponding to a UTF-32 character code. + * + * @param code UTF-32 character code of a glyph. + * @return Reference to the corresponding glyph. + * + * @except std::invalid_argument Cannot get unknown bitmap glyph + */ + const bitmap_glyph& get_glyph(char32_t code) const; + + /// @copydoc bitmap_font::get_glyph(char32_t) const + bitmap_glyph& get_glyph(char32_t code); + + /** + * Returns a reference to the glyph corresponding to a UTF-32 character code, performing an insertion if such glyph does not already exist. + * + * @param code UTF-32 character code of a glyph. + * @return Reference to the corresponding glyph. + */ + bitmap_glyph& operator[](char32_t code); + +private: + std::unordered_map glyphs; + image bitmap; +}; + +inline const image& bitmap_font::get_bitmap() const +{ + return bitmap; +} + +inline image& bitmap_font::get_bitmap() +{ + return bitmap; +} + +inline bitmap_glyph& bitmap_font::operator[](char32_t code) +{ + return glyphs[code]; +} + +} // namespace type + +#endif // ANTKEEPER_TYPE_BITMAP_FONT_HPP diff --git a/src/engine/type/bitmap-glyph.hpp b/src/engine/type/bitmap-glyph.hpp new file mode 100644 index 0000000..de2bf29 --- /dev/null +++ b/src/engine/type/bitmap-glyph.hpp @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_TYPE_BITMAP_GLYPH_HPP +#define ANTKEEPER_TYPE_BITMAP_GLYPH_HPP + +#include +#include +#include + +namespace type { + +/** + * Single glyph in a bitmap font. + * + * @see type::bitmap_font + */ +struct bitmap_glyph +{ + /// Metrics describing the glyph. + glyph_metrics metrics; + + /// Bitmap representing the glyph. + image bitmap; + + /// Position of the packed glyph bitmap within the font bitmap. + uint2 position; +}; + +} // namespace type + +#endif // ANTKEEPER_TYPE_BITMAP_GLYPH_HPP diff --git a/src/type/font-metrics.hpp b/src/engine/type/font-metrics.hpp similarity index 100% rename from src/type/font-metrics.hpp rename to src/engine/type/font-metrics.hpp diff --git a/src/engine/type/font.cpp b/src/engine/type/font.cpp new file mode 100644 index 0000000..e6dd88c --- /dev/null +++ b/src/engine/type/font.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include + +namespace type { + +font::font(const font_metrics& metrics): + metrics(metrics) +{} + +font::font() +{} + +font::~font() +{} + +void font::kern(char32_t first, char32_t second, const float2& offset) +{ + kerning_table[first][second] = offset; +} + +void font::set_font_metrics(const font_metrics& metrics) +{ + this->metrics = metrics; +} + +const float2& font::get_kerning(char32_t first, char32_t second) const +{ + if (auto it_first = kerning_table.find(first); it_first != kerning_table.end()) + if (auto it_second = it_first->second.find(second); it_second != it_first->second.end()) + return it_second->second; + + static const float2 no_kerning = {0.0f, 0.0f}; + return no_kerning; +} + +} // namespace type diff --git a/src/engine/type/font.hpp b/src/engine/type/font.hpp new file mode 100644 index 0000000..9f92573 --- /dev/null +++ b/src/engine/type/font.hpp @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_TYPE_FONT_HPP +#define ANTKEEPER_TYPE_FONT_HPP + +#include +#include +#include + +namespace type { + +/** + * Abstract base class for fonts. + * + * @see type::font_metrics + * @see type::glyph_metrics + * @see type::bitmap_font + */ +class font +{ +public: + /** + * Creates a font and sets its metrics. + * + * @param metrics Metrics describing the font. + */ + font(const font_metrics& metrics); + + /// Creates an empty font. + font(); + + /// Destroys a font. + virtual ~font(); + + /** + * Returns `true` if the font contains a glyph with the given character code. + * + * @param code UTF-32 character code of a glyph. + * @return `true` if the font contains the glyph, `false` otherwise. + */ + virtual bool contains(char32_t code) const = 0; + + /** + * Sets the kerning offset for a pair of glyphs. + * + * @param first UTF-32 character code of the first glyph. + * @param second UTF-32 character code of the second glyph. + * @param offset Kerning offset. + */ + void kern(char32_t first, char32_t second, const float2& offset); + + /** + * Sets the font metrics + * + * @param metrics Font metrics. + */ + void set_font_metrics(const font_metrics& metrics); + + /** + * Returns metrics describing a glyph. + * + * @param code UTF-32 character code of a glyph. + * @return Metrics describing the glyph. + */ + virtual const glyph_metrics& get_glyph_metrics(char32_t code) const = 0; + + /** + * Returns the kerning offset for a pair of glyphs. + * + * @param first UTF-32 character code of the first glyph. + * @param second UTF-32 character code of the second glyph. + * @return Kerning offset. + */ + const float2& get_kerning(char32_t first, char32_t second) const; + + /// Returns the font's kerning table. + const kerning_table& get_kerning_table() const; + + /// Returns metrics describing the font. + const font_metrics& get_font_metrics() const; + +protected: + font_metrics metrics; + kerning_table kerning_table; +}; + +inline const kerning_table& font::get_kerning_table() const +{ + return kerning_table; +} + +inline const font_metrics& font::get_font_metrics() const +{ + return metrics; +} + +} // namespace type + +#endif // ANTKEEPER_TYPE_FONT_HPP diff --git a/src/engine/type/freetype/typeface.cpp b/src/engine/type/freetype/typeface.cpp new file mode 100644 index 0000000..465a556 --- /dev/null +++ b/src/engine/type/freetype/typeface.cpp @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include + +namespace type { +namespace freetype { + +typeface::typeface(FT_Library library, FT_Face face, unsigned char* buffer): + library(library), + face(face), + buffer(buffer), + height(-1.0f) +{ + /// Build charset + FT_UInt index; + FT_ULong c = FT_Get_First_Char(face, &index); + while (index) + { + this->charset.insert(static_cast(c)); + c = FT_Get_Next_Char(face, c, &index); + } +} + +typeface::~typeface() +{ + FT_Done_Face(face); + delete[] buffer; + FT_Done_FreeType(library); +} + +bool typeface::has_kerning() const +{ + return FT_HAS_KERNING(face); +} + +bool typeface::get_metrics(float height, font_metrics& metrics) const +{ + // Set font size + set_face_pixel_size(height); + + // Get font metrics + metrics.size = height; + metrics.ascent = face->size->metrics.ascender / 64.0f; + metrics.descent = face->size->metrics.descender / 64.0f; + metrics.linespace = face->size->metrics.height / 64.0f; + metrics.linegap = metrics.linespace - (metrics.ascent - metrics.descent); + metrics.underline_position = FT_MulFix(face->underline_position, face->size->metrics.y_scale) / 64.0f; + metrics.underline_thickness = FT_MulFix(face->underline_thickness, face->size->metrics.y_scale) / 64.0f; + metrics.max_horizontal_advance = face->size->metrics.max_advance / 64.0f; + metrics.max_vertical_advance = FT_MulFix(face->max_advance_height, face->size->metrics.y_scale) / 64.0f; + + return true; +} + +bool typeface::get_metrics(float height, char32_t code, glyph_metrics& metrics) const +{ + // Set font size + set_face_pixel_size(height); + + // Get index of glyph + FT_UInt glyph_index = FT_Get_Char_Index(face, static_cast(code)); + + // Load glyph and render bitmap + FT_Error error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT); + if (error) + { + throw std::runtime_error("FreeType failed to load glyph (error code " + std::to_string(error) + ")"); + } + + // Get glyph metrics + metrics.width = face->glyph->metrics.width / 64.0f; + metrics.height = face->glyph->metrics.height / 64.0f; + metrics.horizontal_bearing.x() = face->glyph->metrics.horiBearingX / 64.0f; + metrics.horizontal_bearing.y() = face->glyph->metrics.horiBearingY / 64.0f; + metrics.vertical_bearing.x() = face->glyph->metrics.vertBearingX / 64.0f; + metrics.vertical_bearing.y() = face->glyph->metrics.vertBearingY / 64.0f; + metrics.horizontal_advance = face->glyph->metrics.horiAdvance / 64.0f; + metrics.vertical_advance = face->glyph->metrics.vertAdvance / 64.0f; + + return true; +} + +bool typeface::get_bitmap(float height, char32_t code, image& bitmap) const +{ + // Set font size + set_face_pixel_size(height); + + // Get index of glyph + FT_UInt glyph_index = FT_Get_Char_Index(face, static_cast(code)); + + // Load glyph and render bitmap + FT_Error error = FT_Load_Glyph(face, glyph_index, FT_LOAD_RENDER | FT_LOAD_TARGET_(FT_RENDER_MODE_NORMAL)); + if (error) + { + throw std::runtime_error("FreeType failed to load glyph (error code " + std::to_string(error) + ")"); + } + + // Format and resize bitmap + bitmap.resize(0, 0); + bitmap.format(sizeof(unsigned char), 1); + bitmap.resize(face->glyph->bitmap.width, face->glyph->bitmap.rows); + + // Copy glyph bitmap data in bitmap + std::memcpy(bitmap.data(), face->glyph->bitmap.buffer, bitmap.get_size()); + + return true; +} + +bool typeface::get_kerning(float height, char32_t first, char32_t second, float2& offset) const +{ + // Check if typeface has kerning information + if (!has_kerning()) + return false; + + // Set font size + set_face_pixel_size(height); + + // Get indices of the two glyphs + FT_UInt first_index = FT_Get_Char_Index(face, static_cast(first)); + FT_UInt second_index = FT_Get_Char_Index(face, static_cast(second)); + + // Get kerning vector + FT_Vector kerning; + FT_Error error = FT_Get_Kerning(face, first_index, second_index, FT_KERNING_DEFAULT, &kerning); + if (error) + { + throw std::runtime_error("FreeType failed to get kerning vector (error code " + std::to_string(error) + ")"); + } + + offset = float2{static_cast(kerning.x), static_cast(kerning.y)} / 64.0f; + + return true; +} + +void typeface::set_face_pixel_size(float height) const +{ + if (this->height == height) + return; + + FT_Error error = FT_Set_Pixel_Sizes(face, 0, static_cast(height)); + if (error) + { + throw std::runtime_error("FreeType failed to set face size (error code " + std::to_string(error) + ")"); + } + + this->height = height; +} + +} // namespace freetype +} // namespace type diff --git a/src/engine/type/freetype/typeface.hpp b/src/engine/type/freetype/typeface.hpp new file mode 100644 index 0000000..b2a84fb --- /dev/null +++ b/src/engine/type/freetype/typeface.hpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_TYPE_FREETYPE_TYPEFACE_HPP +#define ANTKEEPER_TYPE_FREETYPE_TYPEFACE_HPP + +#include +#include +#include FT_FREETYPE_H + +namespace type { +namespace freetype { + +/** + * Typeface implementation using the FreeType library. + * + * @see type::typeface + */ +class typeface: public type::typeface +{ +public: + /** + * Creates a FreeType typeface. + * + * @param library Pointer to a FreeType library instance. + * @param face Pointer to the FreeType object instance. + * @param buffer Pointer to file buffer containing FreeType face data. + */ + typeface(FT_Library library, FT_Face face, unsigned char* buffer); + + /// Destroys a FreeType typeface. + virtual ~typeface(); + + /// @copydoc type::typeface::has_kerning() const + virtual bool has_kerning() const; + + /// @copydoc type::typeface::get_metrics(float, font_metrics&) const + virtual bool get_metrics(float height, font_metrics& metrics) const; + + /// @copydoc type::typeface::get_metrics(float, char32_t, glyph_metrics&) const + virtual bool get_metrics(float height, char32_t code, glyph_metrics& metrics) const; + + /// @copydoc type::typeface::get_bitmap(float, char32_t, image&) const + virtual bool get_bitmap(float height, char32_t code, image& bitmap) const; + + /// @copydoc type::typeface::get_kerning(float, char32_t, char32_t, float2&) const + virtual bool get_kerning(float height, char32_t first, char32_t second, float2& offset) const; + +private: + void set_face_pixel_size(float height) const; + + FT_Library library; + FT_Face face; + unsigned char* buffer; + mutable float height; +}; + +} // namespace freetype +} // namespace type + +#endif // ANTKEEPER_TYPE_FREETYPE_TYPEFACE_HPP diff --git a/src/engine/type/glyph-metrics.hpp b/src/engine/type/glyph-metrics.hpp new file mode 100644 index 0000000..9d27243 --- /dev/null +++ b/src/engine/type/glyph-metrics.hpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_TYPE_GLYPH_METRICS_HPP +#define ANTKEEPER_TYPE_GLYPH_METRICS_HPP + +#include + +namespace type { + +/** + * Metrics describing properties of a glyph. + */ +struct glyph_metrics +{ + /// Horizontal extent of the glyph. + float width; + + /// Vertical extent of the glyph. + float height; + + /// Offset from the pen position to the glyph's top-left edge, in horizontal layouts. + float2 horizontal_bearing; + + /// Offset from the pen position to the glph's top-left edge, in vertical layouts. + float2 vertical_bearing; + + /// Distance to move the pen position after the glyph has been rendered, in horizontal layouts. + float horizontal_advance; + + /// Distance to move the pen position after the glyph has been rendered, in vertical layouts. + float vertical_advance; +}; + +} // namespace type + +#endif // ANTKEEPER_TYPE_GLYPH_METRICS_HPP diff --git a/src/engine/type/kerning-table.hpp b/src/engine/type/kerning-table.hpp new file mode 100644 index 0000000..e73dd65 --- /dev/null +++ b/src/engine/type/kerning-table.hpp @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_TYPE_KERNING_TABLE_HPP +#define ANTKEEPER_TYPE_KERNING_TABLE_HPP + +#include +#include + +namespace type { + +/// Table containing kerning offsets for pairs of glyphs. +typedef std::unordered_map> kerning_table; + +} // namespace type + +#endif // ANTKEEPER_TYPE_KERNING_TABLE_HPP diff --git a/src/type/text-direction.hpp b/src/engine/type/text-direction.hpp similarity index 100% rename from src/type/text-direction.hpp rename to src/engine/type/text-direction.hpp diff --git a/src/engine/type/type.hpp b/src/engine/type/type.hpp new file mode 100644 index 0000000..b2f3127 --- /dev/null +++ b/src/engine/type/type.hpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_TYPE_HPP +#define ANTKEEPER_TYPE_HPP + +/// Text and typography. +namespace type {} + +#include +#include +#include +#include +#include +#include +#include +#include + +#endif // ANTKEEPER_TYPE_HPP diff --git a/src/engine/type/typeface.cpp b/src/engine/type/typeface.cpp new file mode 100644 index 0000000..3eebb70 --- /dev/null +++ b/src/engine/type/typeface.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include + +namespace type { + +typeface::typeface(typeface_style style, int weight): + style(style), + weight(weight) +{} + +typeface::typeface(): + style(typeface_style::normal), + weight(400) +{} + +void typeface::set_style(typeface_style style) +{ + this->style = style; +} + +void typeface::set_weight(int weight) +{ + this->weight = weight; +} + +} // namespace type diff --git a/src/engine/type/typeface.hpp b/src/engine/type/typeface.hpp new file mode 100644 index 0000000..4554c32 --- /dev/null +++ b/src/engine/type/typeface.hpp @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_TYPE_TYPEFACE_HPP +#define ANTKEEPER_TYPE_TYPEFACE_HPP + +#include +#include +#include +#include +#include + +namespace type { + +/// Emumerates typeface styles. +enum class typeface_style +{ + /// Normal typeface style. + normal, + + /// Italic typeface style. + italic, + + /// Oblique typeface style. + oblique +}; + +/** + * Abstract base class for a typeface, which corresponds to a single digital font file. + * + * @see type::font + */ +class typeface +{ +public: + /** + * Creates a typeface, setting its style and weight. + * + * @param style Typeface style. + * @param weight Typeface weight. + */ + typeface(typeface_style style, int weight); + + /// Creates an empty typeface. + typeface(); + + /// Destroys a typeface. + virtual ~typeface() = default; + + /** + * Sets the style of the typeface. + * + * @param style Typeface style. + */ + void set_style(typeface_style style); + + /** + * Sets the weight of the typeface. + * + * @param weight Typeface weight. + */ + void set_weight(int weight); + + /// Returns the style of the typeface. + [[nodiscard]] inline typeface_style get_style() const noexcept + { + return style; + } + + /// Returns the weight of the typeface. + [[nodiscard]] inline int get_weight() const noexcept + { + return weight; + } + + /// Returns `true` if the typeface contains kerning information, `false` otherwise. + virtual bool has_kerning() const = 0; + + /** + * Gets metrics for a font of the specified size. + * + * @param[in] height Height of the font, in pixels. + * @param[out] metrics Font metrics. + * @return `true` if font metrics were returned, `false` otherwise. + */ + virtual bool get_metrics(float height, font_metrics& metrics) const = 0; + + /** + * Gets metrics for a glyph in a font of the specified size. + * + * @param[in] height Height of the font, in pixels. + * @param[in] code UTF-32 character code of a glyph. + * @param[out] metrics Glyph metrics. + * @return `true` if glyph metrics were returned, `false` otherwise. + */ + virtual bool get_metrics(float height, char32_t code, glyph_metrics& metrics) const = 0; + + /** + * Gets a bitmap of a glyph in a font of the specified size. + * + * @param[in] height Height of the font, in pixels. + * @param[in] code UTF-32 character code of a glyph. + * @param[out] bitmap Glyph bitmap data. + * @return `true` if glyph bitmap data was returned, `false` otherwise. + */ + virtual bool get_bitmap(float height, char32_t code, image& bitmap) const = 0; + + /** + * Gets the kerning offset for a pair of glyphs. + * + * @param[in] height Height of the font, in pixels. + * @param[in] first UTF-32 character code of the first glyph. + * @param[in] second UTF-32 character code of the second glyph. + * @param[out] offset Kerning offset. + * @return `true` if a kerning offset was returned, `false` otherwise. + */ + virtual bool get_kerning(float height, char32_t first, char32_t second, float2& offset) const = 0; + + /// Returns the set of characters supported by the typeface. + [[nodiscard]] inline const std::unordered_set& get_charset() const noexcept + { + return charset; + } + +protected: + std::unordered_set charset; + +private: + typeface_style style; + int weight; +}; + +} // namespace type + +#endif // ANTKEEPER_TYPE_TYPEFACE_HPP diff --git a/src/engine/type/unicode/block.cpp b/src/engine/type/unicode/block.cpp new file mode 100644 index 0000000..5c45c89 --- /dev/null +++ b/src/engine/type/unicode/block.cpp @@ -0,0 +1,347 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include + +namespace type { +namespace unicode { + +const block block::basic_latin{0x0, 0x7F}; +const block block::latin_1_supplement{0x80, 0xFF}; +const block block::latin_extended_a{0x0100, 0x017F}; +const block block::latin_extended_b{0x0180, 0x024F}; +const block block::ipa_extensions{0x0250, 0x02AF}; +const block block::spacing_modifier_letters{0x02B0, 0x02FF}; +const block block::combining_diacritical_marks{0x0300, 0x036F}; +const block block::greek_and_coptic{0x0370, 0x03FF}; +const block block::cyrillic{0x0400, 0x04FF}; +const block block::cyrillic_supplement{0x0500, 0x052F}; +const block block::armenian{0x0530, 0x058F}; +const block block::hebrew{0x0590, 0x05FF}; +const block block::arabic{0x0600, 0x06FF}; +const block block::syriac{0x0700, 0x074F}; +const block block::arabic_supplement{0x0750, 0x077F}; +const block block::thaana{0x0780, 0x07BF}; +const block block::nko{0x07C0, 0x07FF}; +const block block::samaritan{0x0800, 0x083F}; +const block block::mandaic{0x0840, 0x085F}; +const block block::syriac_supplement{0x0860, 0x086F}; +const block block::arabic_extended_b{0x0870, 0x089F}; +const block block::arabic_extended_a{0x08A0, 0x08FF}; +const block block::devanagari{0x0900, 0x097F}; +const block block::bengali{0x0980, 0x09FF}; +const block block::gurmukhi{0x0A00, 0x0A7F}; +const block block::gujarati{0x0A80, 0x0AFF}; +const block block::oriya{0x0B00, 0x0B7F}; +const block block::tamil{0x0B80, 0x0BFF}; +const block block::telugu{0x0C00, 0x0C7F}; +const block block::kannada{0x0C80, 0x0CFF}; +const block block::malayalam{0x0D00, 0x0D7F}; +const block block::sinhala{0x0D80, 0x0DFF}; +const block block::thai{0x0E00, 0x0E7F}; +const block block::lao{0x0E80, 0x0EFF}; +const block block::tibetan{0x0F00, 0x0FFF}; +const block block::myanmar{0x1000, 0x109F}; +const block block::georgian{0x10A0, 0x10FF}; +const block block::hangul_jamo{0x1100, 0x11FF}; +const block block::ethiopic{0x1200, 0x137F}; +const block block::ethiopic_supplement{0x1380, 0x139F}; +const block block::cherokee{0x13A0, 0x13FF}; +const block block::unified_canadian_aboriginal_syllabics{0x1400, 0x167F}; +const block block::ogham{0x1680, 0x169F}; +const block block::runic{0x16A0, 0x16FF}; +const block block::tagalog{0x1700, 0x171F}; +const block block::hanunoo{0x1720, 0x173F}; +const block block::buhid{0x1740, 0x175F}; +const block block::tagbanwa{0x1760, 0x177F}; +const block block::khmer{0x1780, 0x17FF}; +const block block::mongolian{0x1800, 0x18AF}; +const block block::unified_canadian_aboriginal_syllabics_extended{0x18B0, 0x18FF}; +const block block::limbu{0x1900, 0x194F}; +const block block::tai_le{0x1950, 0x197F}; +const block block::new_tai_lue{0x1980, 0x19DF}; +const block block::khmer_symbols{0x19E0, 0x19FF}; +const block block::buginese{0x1A00, 0x1A1F}; +const block block::tai_tham{0x1A20, 0x1AAF}; +const block block::combining_diacritical_marks_extended{0x1AB0, 0x1AFF}; +const block block::balinese{0x1B00, 0x1B7F}; +const block block::sundanese{0x1B80, 0x1BBF}; +const block block::batak{0x1BC0, 0x1BFF}; +const block block::lepcha{0x1C00, 0x1C4F}; +const block block::ol_chiki{0x1C50, 0x1C7F}; +const block block::cyrillic_extended_c{0x1C80, 0x1C8F}; +const block block::georgian_extended{0x1C90, 0x1CBF}; +const block block::sundanese_supplement{0x1CC0, 0x1CCF}; +const block block::vedic_extensions{0x1CD0, 0x1CFF}; +const block block::phonetic_extensions{0x1D00, 0x1D7F}; +const block block::phonetic_extensions_supplement{0x1D80, 0x1DBF}; +const block block::combining_diacritical_marks_supplement{0x1DC0, 0x1DFF}; +const block block::latin_extended_additional{0x1E00, 0x1EFF}; +const block block::greek_extended{0x1F00, 0x1FFF}; +const block block::general_punctuation{0x2000, 0x206F}; +const block block::superscripts_and_subscripts{0x2070, 0x209F}; +const block block::currency_symbols{0x20A0, 0x20CF}; +const block block::combining_diacritical_marks_for_symbols{0x20D0, 0x20FF}; +const block block::letterlike_symbols{0x2100, 0x214F}; +const block block::number_forms{0x2150, 0x218F}; +const block block::arrows{0x2190, 0x21FF}; +const block block::mathematical_operators{0x2200, 0x22FF}; +const block block::miscellaneous_technical{0x2300, 0x23FF}; +const block block::control_pictures{0x2400, 0x243F}; +const block block::optical_character_recognition{0x2440, 0x245F}; +const block block::enclosed_alphanumerics{0x2460, 0x24FF}; +const block block::box_drawing{0x2500, 0x257F}; +const block block::block_elements{0x2580, 0x259F}; +const block block::geometric_shapes{0x25A0, 0x25FF}; +const block block::miscellaneous_symbols{0x2600, 0x26FF}; +const block block::dingbats{0x2700, 0x27BF}; +const block block::miscellaneous_mathematical_symbols_a{0x27C0, 0x27EF}; +const block block::supplemental_arrows_a{0x27F0, 0x27FF}; +const block block::braille_patterns{0x2800, 0x28FF}; +const block block::supplemental_arrows_b{0x2900, 0x297F}; +const block block::miscellaneous_mathematical_symbols_b{0x2980, 0x29FF}; +const block block::supplemental_mathematical_operators{0x2A00, 0x2AFF}; +const block block::miscellaneous_symbols_and_arrows{0x2B00, 0x2BFF}; +const block block::glagolitic{0x2C00, 0x2C5F}; +const block block::latin_extended_c{0x2C60, 0x2C7F}; +const block block::coptic{0x2C80, 0x2CFF}; +const block block::georgian_supplement{0x2D00, 0x2D2F}; +const block block::tifinagh{0x2D30, 0x2D7F}; +const block block::ethiopic_extended{0x2D80, 0x2DDF}; +const block block::cyrillic_extended_a{0x2DE0, 0x2DFF}; +const block block::supplemental_punctuation{0x2E00, 0x2E7F}; +const block block::cjk_radicals_supplement{0x2E80, 0x2EFF}; +const block block::kangxi_radicals{0x2F00, 0x2FDF}; +const block block::ideographic_description_characters{0x2FF0, 0x2FFF}; +const block block::cjk_symbols_and_punctuation{0x3000, 0x303F}; +const block block::hiragana{0x3040, 0x309F}; +const block block::katakana{0x30A0, 0x30FF}; +const block block::bopomofo{0x3100, 0x312F}; +const block block::hangul_compatibility_jamo{0x3130, 0x318F}; +const block block::kanbun{0x3190, 0x319F}; +const block block::bopomofo_extended{0x31A0, 0x31BF}; +const block block::cjk_strokes{0x31C0, 0x31EF}; +const block block::katakana_phonetic_extensions{0x31F0, 0x31FF}; +const block block::enclosed_cjk_letters_and_months{0x3200, 0x32FF}; +const block block::cjk_compatibility{0x3300, 0x33FF}; +const block block::cjk_unified_ideographs_extension_a{0x3400, 0x4DBF}; +const block block::yijing_hexagram_symbols{0x4DC0, 0x4DFF}; +const block block::cjk_unified_ideographs{0x4E00, 0x9FFF}; +const block block::yi_syllables{0xA000, 0xA48F}; +const block block::yi_radicals{0xA490, 0xA4CF}; +const block block::lisu{0xA4D0, 0xA4FF}; +const block block::vai{0xA500, 0xA63F}; +const block block::cyrillic_extended_b{0xA640, 0xA69F}; +const block block::bamum{0xA6A0, 0xA6FF}; +const block block::modifier_tone_letters{0xA700, 0xA71F}; +const block block::latin_extended_d{0xA720, 0xA7FF}; +const block block::syloti_nagri{0xA800, 0xA82F}; +const block block::common_indic_number_forms{0xA830, 0xA83F}; +const block block::phags_pa{0xA840, 0xA87F}; +const block block::saurashtra{0xA880, 0xA8DF}; +const block block::devanagari_extended{0xA8E0, 0xA8FF}; +const block block::kayah_li{0xA900, 0xA92F}; +const block block::rejang{0xA930, 0xA95F}; +const block block::hangul_jamo_extended_a{0xA960, 0xA97F}; +const block block::javanese{0xA980, 0xA9DF}; +const block block::myanmar_extended_b{0xA9E0, 0xA9FF}; +const block block::cham{0xAA00, 0xAA5F}; +const block block::myanmar_extended_a{0xAA60, 0xAA7F}; +const block block::tai_viet{0xAA80, 0xAADF}; +const block block::meetei_mayek_extensions{0xAAE0, 0xAAFF}; +const block block::ethiopic_extended_a{0xAB00, 0xAB2F}; +const block block::latin_extended_e{0xAB30, 0xAB6F}; +const block block::cherokee_supplement{0xAB70, 0xABBF}; +const block block::meetei_mayek{0xABC0, 0xABFF}; +const block block::hangul_syllables{0xAC00, 0xD7AF}; +const block block::hangul_jamo_extended_b{0xD7B0, 0xD7FF}; +const block block::high_surrogates{0xD800, 0xDB7F}; +const block block::high_private_use_surrogates{0xDB80, 0xDBFF}; +const block block::low_surrogates{0xDC00, 0xDFFF}; +const block block::private_use_area{0xE000, 0xF8FF}; +const block block::cjk_compatibility_ideographs{0xF900, 0xFAFF}; +const block block::alphabetic_presentation_forms{0xFB00, 0xFB4F}; +const block block::arabic_presentation_forms_a{0xFB50, 0xFDFF}; +const block block::variation_selectors{0xFE00, 0xFE0F}; +const block block::vertical_forms{0xFE10, 0xFE1F}; +const block block::combining_half_marks{0xFE20, 0xFE2F}; +const block block::cjk_compatibility_forms{0xFE30, 0xFE4F}; +const block block::small_form_variants{0xFE50, 0xFE6F}; +const block block::arabic_presentation_forms_b{0xFE70, 0xFEFF}; +const block block::halfwidth_and_fullwidth_forms{0xFF00, 0xFFEF}; +const block block::specials{0xFFF0, 0xFFFF}; +const block block::linear_b_syllabary{0x10000, 0x1007F}; +const block block::linear_b_ideograms{0x10080, 0x100FF}; +const block block::aegean_numbers{0x10100, 0x1013F}; +const block block::ancient_greek_numbers{0x10140, 0x1018F}; +const block block::ancient_symbols{0x10190, 0x101CF}; +const block block::phaistos_disc{0x101D0, 0x101FF}; +const block block::lycian{0x10280, 0x1029F}; +const block block::carian{0x102A0, 0x102DF}; +const block block::coptic_epact_numbers{0x102E0, 0x102FF}; +const block block::old_italic{0x10300, 0x1032F}; +const block block::gothic{0x10330, 0x1034F}; +const block block::old_permic{0x10350, 0x1037F}; +const block block::ugaritic{0x10380, 0x1039F}; +const block block::old_persian{0x103A0, 0x103DF}; +const block block::deseret{0x10400, 0x1044F}; +const block block::shavian{0x10450, 0x1047F}; +const block block::osmanya{0x10480, 0x104AF}; +const block block::osage{0x104B0, 0x104FF}; +const block block::elbasan{0x10500, 0x1052F}; +const block block::caucasian_albanian{0x10530, 0x1056F}; +const block block::vithkuqi{0x10570, 0x105BF}; +const block block::linear_a{0x10600, 0x1077F}; +const block block::latin_extended_f{0x10780, 0x107BF}; +const block block::cypriot_syllabary{0x10800, 0x1083F}; +const block block::imperial_aramaic{0x10840, 0x1085F}; +const block block::palmyrene{0x10860, 0x1087F}; +const block block::nabataean{0x10880, 0x108AF}; +const block block::hatran{0x108E0, 0x108FF}; +const block block::phoenician{0x10900, 0x1091F}; +const block block::lydian{0x10920, 0x1093F}; +const block block::meroitic_hieroglyphs{0x10980, 0x1099F}; +const block block::meroitic_cursive{0x109A0, 0x109FF}; +const block block::kharoshthi{0x10A00, 0x10A5F}; +const block block::old_south_arabian{0x10A60, 0x10A7F}; +const block block::old_north_arabian{0x10A80, 0x10A9F}; +const block block::manichaean{0x10AC0, 0x10AFF}; +const block block::avestan{0x10B00, 0x10B3F}; +const block block::inscriptional_parthian{0x10B40, 0x10B5F}; +const block block::inscriptional_pahlavi{0x10B60, 0x10B7F}; +const block block::psalter_pahlavi{0x10B80, 0x10BAF}; +const block block::old_turkic{0x10C00, 0x10C4F}; +const block block::old_hungarian{0x10C80, 0x10CFF}; +const block block::hanifi_rohingya{0x10D00, 0x10D3F}; +const block block::rumi_numeral_symbols{0x10E60, 0x10E7F}; +const block block::yezidi{0x10E80, 0x10EBF}; +const block block::old_sogdian{0x10F00, 0x10F2F}; +const block block::sogdian{0x10F30, 0x10F6F}; +const block block::old_uyghur{0x10F70, 0x10FAF}; +const block block::chorasmian{0x10FB0, 0x10FDF}; +const block block::elymaic{0x10FE0, 0x10FFF}; +const block block::brahmi{0x11000, 0x1107F}; +const block block::kaithi{0x11080, 0x110CF}; +const block block::sora_sompeng{0x110D0, 0x110FF}; +const block block::chakma{0x11100, 0x1114F}; +const block block::mahajani{0x11150, 0x1117F}; +const block block::sharada{0x11180, 0x111DF}; +const block block::sinhala_archaic_numbers{0x111E0, 0x111FF}; +const block block::khojki{0x11200, 0x1124F}; +const block block::multani{0x11280, 0x112AF}; +const block block::khudawadi{0x112B0, 0x112FF}; +const block block::grantha{0x11300, 0x1137F}; +const block block::newa{0x11400, 0x1147F}; +const block block::tirhuta{0x11480, 0x114DF}; +const block block::siddham{0x11580, 0x115FF}; +const block block::modi{0x11600, 0x1165F}; +const block block::mongolian_supplement{0x11660, 0x1167F}; +const block block::takri{0x11680, 0x116CF}; +const block block::ahom{0x11700, 0x1174F}; +const block block::dogra{0x11800, 0x1184F}; +const block block::warang_citi{0x118A0, 0x118FF}; +const block block::dives_akuru{0x11900, 0x1195F}; +const block block::nandinagari{0x119A0, 0x119FF}; +const block block::zanabazar_square{0x11A00, 0x11A4F}; +const block block::soyombo{0x11A50, 0x11AAF}; +const block block::unified_canadian_aboriginal_syllabics_extended_a{0x11AB0, 0x11ABF}; +const block block::pau_cin_hau{0x11AC0, 0x11AFF}; +const block block::bhaiksuki{0x11C00, 0x11C6F}; +const block block::marchen{0x11C70, 0x11CBF}; +const block block::masaram_gondi{0x11D00, 0x11D5F}; +const block block::gunjala_gondi{0x11D60, 0x11DAF}; +const block block::makasar{0x11EE0, 0x11EFF}; +const block block::lisu_supplement{0x11FB0, 0x11FBF}; +const block block::tamil_supplement{0x11FC0, 0x11FFF}; +const block block::cuneiform{0x12000, 0x123FF}; +const block block::cuneiform_numbers_and_punctuation{0x12400, 0x1247F}; +const block block::early_dynastic_cuneiform{0x12480, 0x1254F}; +const block block::cypro_minoan{0x12F90, 0x12FFF}; +const block block::egyptian_hieroglyphs{0x13000, 0x1342F}; +const block block::egyptian_hieroglyph_format_controls{0x13430, 0x1343F}; +const block block::anatolian_hieroglyphs{0x14400, 0x1467F}; +const block block::bamum_supplement{0x16800, 0x16A3F}; +const block block::mro{0x16A40, 0x16A6F}; +const block block::tangsa{0x16A70, 0x16ACF}; +const block block::bassa_vah{0x16AD0, 0x16AFF}; +const block block::pahawh_hmong{0x16B00, 0x16B8F}; +const block block::medefaidrin{0x16E40, 0x16E9F}; +const block block::miao{0x16F00, 0x16F9F}; +const block block::ideographic_symbols_and_punctuation{0x16FE0, 0x16FFF}; +const block block::tangut{0x17000, 0x187FF}; +const block block::tangut_components{0x18800, 0x18AFF}; +const block block::khitan_small_script{0x18B00, 0x18CFF}; +const block block::tangut_supplement{0x18D00, 0x18D7F}; +const block block::kana_extended_b{0x1AFF0, 0x1AFFF}; +const block block::kana_supplement{0x1B000, 0x1B0FF}; +const block block::kana_extended_a{0x1B100, 0x1B12F}; +const block block::small_kana_extension{0x1B130, 0x1B16F}; +const block block::nushu{0x1B170, 0x1B2FF}; +const block block::duployan{0x1BC00, 0x1BC9F}; +const block block::shorthand_format_controls{0x1BCA0, 0x1BCAF}; +const block block::znamenny_musical_notation{0x1CF00, 0x1CFCF}; +const block block::byzantine_musical_symbols{0x1D000, 0x1D0FF}; +const block block::musical_symbols{0x1D100, 0x1D1FF}; +const block block::ancient_greek_musical_notation{0x1D200, 0x1D24F}; +const block block::mayan_numerals{0x1D2E0, 0x1D2FF}; +const block block::tai_xuan_jing_symbols{0x1D300, 0x1D35F}; +const block block::counting_rod_numerals{0x1D360, 0x1D37F}; +const block block::mathematical_alphanumeric_symbols{0x1D400, 0x1D7FF}; +const block block::sutton_signwriting{0x1D800, 0x1DAAF}; +const block block::latin_extended_g{0x1DF00, 0x1DFFF}; +const block block::glagolitic_supplement{0x1E000, 0x1E02F}; +const block block::nyiakeng_puachue_hmong{0x1E100, 0x1E14F}; +const block block::toto{0x1E290, 0x1E2BF}; +const block block::wancho{0x1E2C0, 0x1E2FF}; +const block block::ethiopic_extended_b{0x1E7E0, 0x1E7FF}; +const block block::mende_kikakui{0x1E800, 0x1E8DF}; +const block block::adlam{0x1E900, 0x1E95F}; +const block block::indic_siyaq_numbers{0x1EC70, 0x1ECBF}; +const block block::ottoman_siyaq_numbers{0x1ED00, 0x1ED4F}; +const block block::arabic_mathematical_alphabetic_symbols{0x1EE00, 0x1EEFF}; +const block block::mahjong_tiles{0x1F000, 0x1F02F}; +const block block::domino_tiles{0x1F030, 0x1F09F}; +const block block::playing_cards{0x1F0A0, 0x1F0FF}; +const block block::enclosed_alphanumeric_supplement{0x1F100, 0x1F1FF}; +const block block::enclosed_ideographic_supplement{0x1F200, 0x1F2FF}; +const block block::miscellaneous_symbols_and_pictographs{0x1F300, 0x1F5FF}; +const block block::emoticons{0x1F600, 0x1F64F}; +const block block::ornamental_dingbats{0x1F650, 0x1F67F}; +const block block::transport_and_map_symbols{0x1F680, 0x1F6FF}; +const block block::alchemical_symbols{0x1F700, 0x1F77F}; +const block block::geometric_shapes_extended{0x1F780, 0x1F7FF}; +const block block::supplemental_arrows_c{0x1F800, 0x1F8FF}; +const block block::supplemental_symbols_and_pictographs{0x1F900, 0x1F9FF}; +const block block::chess_symbols{0x1FA00, 0x1FA6F}; +const block block::symbols_and_pictographs_extended_a{0x1FA70, 0x1FAFF}; +const block block::symbols_for_legacy_computing{0x1FB00, 0x1FBFF}; +const block block::cjk_unified_ideographs_extension_b{0x20000, 0x2A6DF}; +const block block::cjk_unified_ideographs_extension_c{0x2A700, 0x2B73F}; +const block block::cjk_unified_ideographs_extension_d{0x2B740, 0x2B81F}; +const block block::cjk_unified_ideographs_extension_e{0x2B820, 0x2CEAF}; +const block block::cjk_unified_ideographs_extension_f{0x2CEB0, 0x2EBEF}; +const block block::cjk_compatibility_ideographs_supplement{0x2F800, 0x2FA1F}; +const block block::cjk_unified_ideographs_extension_g{0x30000, 0x3134F}; +const block block::tags{0xE0000, 0xE007F}; +const block block::variation_selectors_supplement{0xE0100, 0xE01EF}; +const block block::supplementary_private_use_area_a{0xF0000, 0xFFFFF}; +const block block::supplementary_private_use_area_b{0x100000, 0x10FFFF}; + +} // namespace unicode +} // namespace type diff --git a/src/type/unicode/block.hpp b/src/engine/type/unicode/block.hpp similarity index 100% rename from src/type/unicode/block.hpp rename to src/engine/type/unicode/block.hpp diff --git a/src/engine/type/unicode/convert.cpp b/src/engine/type/unicode/convert.cpp new file mode 100644 index 0000000..f1605eb --- /dev/null +++ b/src/engine/type/unicode/convert.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include + +namespace type { +namespace unicode { + +std::u32string u32(const std::string& u8) +{ + std::wstring_convert, char32_t> convert; + return convert.from_bytes(u8); +} + +std::string u8(const std::u32string& u32) +{ + std::wstring_convert, char32_t> convert; + return convert.to_bytes(u32); +} + +} // namespace unicode +} // namespace type diff --git a/src/type/unicode/convert.hpp b/src/engine/type/unicode/convert.hpp similarity index 100% rename from src/type/unicode/convert.hpp rename to src/engine/type/unicode/convert.hpp diff --git a/src/engine/type/unicode/unicode.hpp b/src/engine/type/unicode/unicode.hpp new file mode 100644 index 0000000..3de54a0 --- /dev/null +++ b/src/engine/type/unicode/unicode.hpp @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_TYPE_UNICODE_HPP +#define ANTKEEPER_TYPE_UNICODE_HPP + +namespace type { + +/// Unicode-related functions and data. +namespace unicode {} + +} // namespace type + +#include +#include + +#endif // ANTKEEPER_TYPE_UNICODE_HPP diff --git a/src/ui/ui-element.hpp b/src/engine/ui/ui-element.hpp similarity index 100% rename from src/ui/ui-element.hpp rename to src/engine/ui/ui-element.hpp diff --git a/src/ui/ui.hpp b/src/engine/ui/ui.hpp similarity index 100% rename from src/ui/ui.hpp rename to src/engine/ui/ui.hpp diff --git a/src/utility/ansi.hpp b/src/engine/utility/ansi.hpp similarity index 100% rename from src/utility/ansi.hpp rename to src/engine/utility/ansi.hpp diff --git a/src/utility/bit-math.hpp b/src/engine/utility/bit-math.hpp similarity index 100% rename from src/utility/bit-math.hpp rename to src/engine/utility/bit-math.hpp diff --git a/src/engine/utility/dict.cpp b/src/engine/utility/dict.cpp new file mode 100644 index 0000000..1389ed0 --- /dev/null +++ b/src/engine/utility/dict.cpp @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace hash::literals; + +template +static void serialize_any(const std::any& any, serialize_context& ctx) +{ + serializer().serialize(std::any_cast(any), ctx); +} + +template +static void deserialize_any(std::any& any, deserialize_context& ctx) +{ + T value; + deserializer().deserialize(value, ctx); + any = std::move(value); +} + +/** + * Serializes a dict with an unsigned 32-bit key. + * + * @param[in] dict Dict to serialize. + * @param[in,out] ctx Serialize context. + * + * @throw serialize_error Write error. + * @throw serialize_error Unsupported dict value type. + */ +template <> +void serializer>::serialize(const dict& dict, serialize_context& ctx) +{ + // Map type indices to tuples containing a type hash and serialize function pointer + static const std::unordered_map + < + std::type_index, + std::tuple + < + std::uint32_t, + void (*)(const std::any&, serialize_context&) + > + > type_map + { + {std::type_index(typeid(bool)), {"bool"_fnv1a32, &serialize_any}}, + {std::type_index(typeid(std::uint8_t)), {"uint8"_fnv1a32, &serialize_any}}, + {std::type_index(typeid(std::uint16_t)), {"uint16"_fnv1a32, &serialize_any}}, + {std::type_index(typeid(std::uint32_t)), {"uint32"_fnv1a32, &serialize_any}}, + {std::type_index(typeid(std::uint64_t)), {"uint64"_fnv1a32, &serialize_any}}, + {std::type_index(typeid(std::int8_t)), {"int8"_fnv1a32, &serialize_any}}, + {std::type_index(typeid(std::int16_t)), {"int16"_fnv1a32, &serialize_any}}, + {std::type_index(typeid(std::int32_t)), {"int32"_fnv1a32, &serialize_any}}, + {std::type_index(typeid(std::int64_t)), {"int64"_fnv1a32, &serialize_any}}, + {std::type_index(typeid(float)), {"float"_fnv1a32, &serialize_any}}, + {std::type_index(typeid(double)), {"double"_fnv1a32, &serialize_any}}, + {std::type_index(typeid(std::string)), {"string"_fnv1a32, &serialize_any}}, + {std::type_index(typeid(std::u8string)), {"u8string"_fnv1a32, &serialize_any}}, + {std::type_index(typeid(std::u16string)), {"u16string"_fnv1a32, &serialize_any}}, + {std::type_index(typeid(std::u32string)), {"u32string"_fnv1a32, &serialize_any}} + }; + + // Write dict size + std::uint64_t size = static_cast(dict.size()); + ctx.write64(reinterpret_cast(&size), 1); + + // Write dict entries + for (const auto& [key, value]: dict) + { + if (auto i = type_map.find(value.type()); i != type_map.end()) + { + const auto& [type_hash, type_serializer] = i->second; + + // Write entry type hash and key + ctx.write32(reinterpret_cast(&type_hash), 1); + ctx.write32(reinterpret_cast(&key), 1); + + // Serialize entry value + type_serializer(value, ctx); + } + else + { + throw serialize_error("Unsupported dict value type"); + } + } +} + +/** + * Deserializes a dict with an unsigned 32-bit key. + * + * @param[out] dict Dict to serialize. + * @param[in,out] ctx Deserialize context. + * + * @throw deserialize_error Read error. + * @throw deserialize_error Unsupported dict value type. + */ +template <> +void deserializer>::deserialize(dict& dict, deserialize_context& ctx) +{ + // Map type hashes to deserialize function pointers + static const std::unordered_map + < + std::uint32_t, + void (*)(std::any&, deserialize_context&) + > type_map + { + {"bool"_fnv1a32, &deserialize_any}, + {"uint8"_fnv1a32, &deserialize_any}, + {"uint16"_fnv1a32, &deserialize_any}, + {"uint32"_fnv1a32, &deserialize_any}, + {"uint64"_fnv1a32, &deserialize_any}, + {"int8"_fnv1a32, &deserialize_any}, + {"int16"_fnv1a32, &deserialize_any}, + {"int32"_fnv1a32, &deserialize_any}, + {"int64"_fnv1a32, &deserialize_any}, + {"float"_fnv1a32, &deserialize_any}, + {"double"_fnv1a32, &deserialize_any}, + {"string"_fnv1a32, &deserialize_any}, + {"u8string"_fnv1a32, &deserialize_any}, + {"u16string"_fnv1a32, &deserialize_any}, + {"u32string"_fnv1a32, &deserialize_any} + }; + + dict.clear(); + + // Read dict size + std::uint64_t size = 0; + ctx.read64(reinterpret_cast(&size), 1); + + // Read dict entries + for (std::size_t i = 0; i < size; ++i) + { + // Read entry type hash + std::uint32_t type_hash = 0; + ctx.read32(reinterpret_cast(&type_hash), 1); + + if (auto i = type_map.find(type_hash); i != type_map.end()) + { + // Read entry key + std::uint32_t key = 0; + ctx.read32(reinterpret_cast(&key), 1); + + // Deserialize entry value + i->second(dict[key], ctx); + } + else + { + throw deserialize_error("Unsupported dict value type"); + } + } +} diff --git a/src/utility/dict.hpp b/src/engine/utility/dict.hpp similarity index 100% rename from src/utility/dict.hpp rename to src/engine/utility/dict.hpp diff --git a/src/engine/utility/fundamental-types.hpp b/src/engine/utility/fundamental-types.hpp new file mode 100644 index 0000000..a509a36 --- /dev/null +++ b/src/engine/utility/fundamental-types.hpp @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_FUNDAMENTAL_TYPES_HPP +#define ANTKEEPER_FUNDAMENTAL_TYPES_HPP + +#include +#include + +/// 2D vector of bools +using bool2 = math::vector; + +/// 3D vector of bools +using bool3 = math::vector; + +/// 4D vector of bools +using bool4 = math::vector; + +/// 2D vector of chars +using char2 = math::vector; + +/// 3D vector of chars +using char3 = math::vector; + +/// 4D vector of chars +using char4 = math::vector; + +/// 2D vector of unsigned chars +using uchar2 = math::vector; + +/// 3D vector of unsigned chars +using uchar3 = math::vector; + +/// 4D vector of unsigned chars +using uchar4 = math::vector; + +/// 2D vector of shorts +using short2 = math::vector; + +/// 3D vector of shorts +using short3 = math::vector; + +/// 4D vector of shorts +using short4 = math::vector; + +/// 2D vector of unsigned shorts +using ushort2 = math::vector; + +/// 3D vector of unsigned shorts +using ushort3 = math::vector; + +/// 4D vector of unsigned shorts +using ushort4 = math::vector; + +/// 2D vector of ints +using int2 = math::vector; + +/// 3D vector of ints +using int3 = math::vector; + +/// 4D vector of ints +using int4 = math::vector; + +/// 2D vector of unsigned ints +using uint2 = math::vector; + +/// 3D vector of unsigned ints +using uint3 = math::vector; + +/// 4D vector of unsigned ints +using uint4 = math::vector; + +/// 2D vector of longs +using long2 = math::vector; + +/// 3D vector of longs +using long3 = math::vector; + +/// 4D vector of longs +using long4 = math::vector; + +/// 2D vector of unsigned longs +using ulong2 = math::vector; + +/// 3D vector of unsigned longs +using ulong3 = math::vector; + +/// 4D vector of unsigned longs +using ulong4 = math::vector; + +/// 2D vector of floats +using float2 = math::vector; + +/// 3D vector of floats +using float3 = math::vector; + +/// 4D vector of floats +using float4 = math::vector; + +/// 2D vector of doubles +using double2 = math::vector; + +/// 3D vector of doubles +using double3 = math::vector; + +/// 4D vector of doubles +using double4 = math::vector; + +/// 2x2 matrix of bools +using bool2x2 = math::matrix; + +/// 3x3 matrix of bools +using bool3x3 = math::matrix; + +/// 4x4 matrix of bools +using bool4x4 = math::matrix; + +/// 2x2 matrix of chars +using char2x2 = math::matrix; + +/// 3x3 matrix of chars +using char3x3 = math::matrix; + +/// 4x4 matrix of chars +using char4x4 = math::matrix; + +/// 2x2 matrix of unsigned chars +using uchar2x2 = math::matrix; + +/// 3x3 matrix of unsigned chars +using uchar3x3 = math::matrix; + +/// 4x4 matrix of unsigned chars +using uchar4x4 = math::matrix; + +/// 2x2 matrix of shorts +using short2x2 = math::matrix; + +/// 3x3 matrix of shorts +using short3x3 = math::matrix; + +/// 4x4 matrix of shorts +using short4x4 = math::matrix; + +/// 2x2 matrix of unsigned shorts +using ushort2x2 = math::matrix; + +/// 3x3 matrix of unsigned shorts +using ushort3x3 = math::matrix; + +/// 4x4 matrix of unsigned shorts +using ushort4x4 = math::matrix; + +/// 2x2 matrix of ints +using int2x2 = math::matrix; + +/// 3x3 matrix of ints +using int3x3 = math::matrix; + +/// 4x4 matrix of ints +using int4x4 = math::matrix; + +/// 2x2 matrix of unsigned ints +using uint2x2 = math::matrix; + +/// 3x3 matrix of unsigned ints +using uint3x3 = math::matrix; + +/// 4x4 matrix of unsigned ints +using uint4x4 = math::matrix; + +/// 2x2 matrix of longs +using long2x2 = math::matrix; + +/// 3x3 matrix of longs +using long3x3 = math::matrix; + +/// 4x4 matrix of longs +using long4x4 = math::matrix; + +/// 2x2 matrix of unsigned longs +using ulong2x2 = math::matrix; + +/// 3x3 matrix of unsigned longs +using ulong3x3 = math::matrix; + +/// 4x4 matrix of unsigned longs +using ulong4x4 = math::matrix; + +/// 2x2 matrix of floats +using float2x2 = math::matrix; + +/// 3x3 matrix of floats +using float3x3 = math::matrix; + +/// 4x4 matrix of floats +using float4x4 = math::matrix; + +/// 2x2 matrix of doubles +using double2x2 = math::matrix; + +/// 3x3 matrix of doubles +using double3x3 = math::matrix; + +/// 4x4 matrix of doubles +using double4x4 = math::matrix; + +#endif // ANTKEEPER_FUNDAMENTAL_TYPES_HPP diff --git a/src/engine/utility/hash.hpp b/src/engine/utility/hash.hpp new file mode 100644 index 0000000..7e59417 --- /dev/null +++ b/src/engine/utility/hash.hpp @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_UTILITY_HASH_HPP +#define ANTKEEPER_UTILITY_HASH_HPP + +/** + * Hash functions. + */ +namespace hash {} + +#include +#include + +#endif // ANTKEEPER_UTILITY_HASH_HPP diff --git a/src/utility/hash/fnv1a.hpp b/src/engine/utility/hash/fnv1a.hpp similarity index 100% rename from src/utility/hash/fnv1a.hpp rename to src/engine/utility/hash/fnv1a.hpp diff --git a/src/utility/hash/literals.hpp b/src/engine/utility/hash/literals.hpp similarity index 100% rename from src/utility/hash/literals.hpp rename to src/engine/utility/hash/literals.hpp diff --git a/src/engine/utility/paths.cpp b/src/engine/utility/paths.cpp new file mode 100644 index 0000000..394d549 --- /dev/null +++ b/src/engine/utility/paths.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include +#include + +#if defined(_WIN32) + #include + #include +#else + #include + #include + #include + #include +#endif + +std::filesystem::path get_executable_path() +{ + std::filesystem::path executable_path; + + #if defined(_WIN32) + // Get executable path on Windows + std::wstring path(MAX_PATH, L'\0'); + GetModuleFileNameW(GetModuleHandleW(nullptr), path.data(), MAX_PATH); + path.erase(std::find(path.begin(), path.end(), L'\0'), path.end()); + executable_path = path; + #else + // Get executable path on Linux + char path[PATH_MAX]; + ssize_t length = ::readlink("/proc/self/exe", path, sizeof(path) - 1); + if (length != -1) + { + path[length] = '\0'; + executable_path = path; + } + #endif + + return executable_path; +} + +std::filesystem::path get_executable_data_path() +{ + #if defined(_WIN32) + return get_executable_path().parent_path(); + #else + return get_executable_path().parent_path().parent_path() / "share"; + #endif +} + +std::filesystem::path get_local_config_path() +{ + std::filesystem::path local_config_path; + + #if defined(_WIN32) + + std::wstring path(MAX_PATH, L'\0'); + if (SHGetFolderPathW(nullptr, CSIDL_LOCAL_APPDATA, nullptr, SHGFP_TYPE_CURRENT, path.data()) == S_OK) + { + path.erase(std::find(path.begin(), path.end(), L'\0'), path.end()); + local_config_path = path; + } + + // Windows Vista+ + // wchar_t* path_buffer = nullptr; + // if (SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_DEFAULT, nullptr, &path_buffer) == S_OK) + // { + // local_config_path = std::filesystem::path(path_buffer); + // CoTaskMemFree(static_cast(path_buffer)); + // } + + #else + // Determine home path + std::filesystem::path home_path = getpwuid(getuid())->pw_dir; + + // Determine config path + char* xdg_config_home = std::getenv("XDG_CONFIG_HOME"); + if (!xdg_config_home) + { + // Default to $HOME/.config/ as per: + // https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html#variables + local_config_path = home_path / ".config/"; + } + else + { + local_config_path = xdg_config_home; + } + #endif + + return local_config_path; +} + +std::filesystem::path get_shared_config_path() +{ + #if defined(_WIN32) + std::filesystem::path shared_config_path; + + std::wstring path(MAX_PATH, L'\0'); + if (SHGetFolderPathW(nullptr, CSIDL_APPDATA, nullptr, SHGFP_TYPE_CURRENT, path.data()) == S_OK) + { + path.erase(std::find(path.begin(), path.end(), L'\0'), path.end()); + shared_config_path = path; + } + + // Windows Vista+ + // wchar_t* path_buffer = nullptr; + // if (SHGetKnownFolderPath(FOLDERID_RoamingAppData, KF_FLAG_DEFAULT, nullptr, &path_buffer) == S_OK) + // { + // shared_config_path = path_buffer; + // CoTaskMemFree(static_cast(path_buffer)); + // } + + return shared_config_path; + #else + return get_local_config_path(); + #endif +} diff --git a/src/utility/paths.hpp b/src/engine/utility/paths.hpp similarity index 100% rename from src/utility/paths.hpp rename to src/engine/utility/paths.hpp diff --git a/src/utility/state-machine.hpp b/src/engine/utility/state-machine.hpp similarity index 100% rename from src/utility/state-machine.hpp rename to src/engine/utility/state-machine.hpp diff --git a/src/engine/utility/uuid.cpp b/src/engine/utility/uuid.cpp new file mode 100644 index 0000000..7230633 --- /dev/null +++ b/src/engine/utility/uuid.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include + +std::string uuid::string() const +{ + static const char* hex = "0123456789abcdef"; + + std::string str(32, '0'); + + char* c = str.data(); + for (std::byte byte: data) + { + *(c++) = hex[static_cast(byte) >> 4]; + *(c++) = hex[static_cast(byte) & 15]; + } + + return str; +} + +std::ostream& operator<<(std::ostream& os, const uuid& id) +{ + os << id.string(); + return os; +} diff --git a/src/utility/uuid.hpp b/src/engine/utility/uuid.hpp similarity index 100% rename from src/utility/uuid.hpp rename to src/engine/utility/uuid.hpp diff --git a/src/entity/archetype.cpp b/src/entity/archetype.cpp deleted file mode 100644 index 6587688..0000000 --- a/src/entity/archetype.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "entity/archetype.hpp" -#include "entity/clone.hpp" - -namespace entity { - -entity::id archetype::create(entity::registry& registry) const -{ - entt::handle instance_handle(registry, registry.create()); - - for (const auto& function: stamps) - function(instance_handle); - - return instance_handle.entity(); -} - -void archetype::stamp(entt::handle& handle) const -{ - for (const auto& function: stamps) - function(handle); -} - -} // namespace entity diff --git a/src/entity/archetype.hpp b/src/entity/archetype.hpp deleted file mode 100644 index 3e1a4aa..0000000 --- a/src/entity/archetype.hpp +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_ENTITY_ARCHETYPE_HPP -#define ANTKEEPER_ENTITY_ARCHETYPE_HPP - -#include "entity/registry.hpp" -#include "entity/id.hpp" -#include -#include - -namespace entity { - -/** - * Entity type template. - */ -struct archetype -{ - /// List of stamp functions which construct instances of the archetype's components. - std::list> stamps; - - /** - * Creates an instance of this archetype. - * - * @param registry Registry in which to create an entity. - * - * @return Entity ID of the created instance. - */ - entity::id create(entity::registry& registry) const; - - void stamp(entt::handle& handle) const; -}; - -} // namespace entity - -#endif // ANTKEEPER_ENTITY_ARCHETYPE_HPP diff --git a/src/entity/clone.cpp b/src/entity/clone.cpp deleted file mode 100644 index 29bf842..0000000 --- a/src/entity/clone.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "entity/clone.hpp" - -namespace entity { - -void clone(entity::registry& registry, entity::id source, entity::id destination) -{ - for (auto&& it: registry.storage()) - { - if (auto& storage = it.second; storage.contains(source)) - { - storage.emplace(destination, storage.get(source)); - } - } -} - -} // namespace entity diff --git a/src/entity/clone.hpp b/src/entity/clone.hpp deleted file mode 100644 index ada86ea..0000000 --- a/src/entity/clone.hpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_ENTITY_CLONE_HPP -#define ANTKEEPER_ENTITY_CLONE_HPP - -#include "entity/registry.hpp" -#include "entity/id.hpp" - -namespace entity { - -/** - * Clones all the components of an entity. - * - * @param registry Entity registry. - * @param source Source entity ID. - * @param destination Destination entity ID. - */ -void clone(entity::registry& registry, entity::id source, entity::id destination); - -} // namespace entity - -#endif // ANTKEEPER_ENTITY_CLONE_HPP diff --git a/src/entity/commands.cpp b/src/entity/commands.cpp deleted file mode 100644 index 1d36063..0000000 --- a/src/entity/commands.cpp +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "entity/commands.hpp" -#include "game/component/model.hpp" -#include "game/component/transform.hpp" -#include "game/component/celestial-body.hpp" -#include "game/component/terrain.hpp" -#include "math/quaternion.hpp" -#include - -namespace entity { -namespace command { - -void translate(entity::registry& registry, entity::id eid, const float3& translation) -{ - const game::component::transform* transform = registry.try_get(eid); - if (transform) - { - registry.patch - ( - eid, - [&translation](auto& transform) - { - transform.local.translation += translation; - } - ); - } -} - -void rotate(entity::registry& registry, entity::id eid, float angle, const float3& axis) -{ - const game::component::transform* transform = registry.try_get(eid); - if (transform) - { - registry.patch - ( - eid, - [angle, &axis](auto& transform) - { - transform.local.rotation = math::normalize(math::angle_axis(angle, axis) * transform.local.rotation); - } - ); - } -} - -void move_to(entity::registry& registry, entity::id eid, const float3& position) -{ - const game::component::transform* transform = registry.try_get(eid); - if (transform) - { - registry.patch - ( - eid, - [&position](auto& transform) - { - transform.local.translation = position; - } - ); - } -} - -void warp_to(entity::registry& registry, entity::id eid, const float3& position) -{ - const game::component::transform* transform = registry.try_get(eid); - if (transform) - { - registry.patch - ( - eid, - [&position](auto& transform) - { - transform.local.translation = position; - transform.warp = true; - } - ); - } -} - -void set_scale(entity::registry& registry, entity::id eid, const float3& scale) -{ - const game::component::transform* transform = registry.try_get(eid); - if (transform) - { - registry.patch - ( - eid, - [&scale](auto& transform) - { - transform.local.scale = scale; - } - ); - } -} - -void set_transform(entity::registry& registry, entity::id eid, const math::transform& transform, bool warp) -{ - const game::component::transform* transform_component = registry.try_get(eid); - if (transform_component) - { - registry.patch - ( - eid, - [&other_transform = transform, warp](auto& transform) - { - transform.local = other_transform; - transform.warp = warp; - } - ); - } -} - -void place(entity::registry& registry, entity::id eid, entity::id celestial_body_id, double altitude, double latitude, double longitude) -{ - /* - if (registry.has(eid)) - { - double x = 0.0; - double y = altitude; - double z = 0.0; - - if (registry.has(celestial_body_id)) - { - const game::component::celestial_body& celestial_body = registry.get(celestial_body_id); - - x = longitude * math::two_pi * celestial_body.radius; - z = -latitude * math::two_pi * celestial_body.radius; - - if (registry.has(celestial_body_id)) - { - const game::component::terrain& terrain = registry.get(celestial_body_id); - - if (terrain.elevation != nullptr) - { - y += terrain.elevation(latitude, longitude); - } - } - } - - game::component::transform& transform = registry.get(eid); - transform.local.translation = math::vector(double3{x, y, z}); - transform.warp = true; - } - */ -} - -void assign_render_layers(entity::registry& registry, entity::id eid, unsigned int layers) -{ - const game::component::model* model = registry.try_get(eid); - if (model) - { - registry.patch - ( - eid, - [layers](auto& model) - { - model.layers = layers; - } - ); - } -} - -math::transform get_local_transform(entity::registry& registry, entity::id eid) -{ - const game::component::transform* transform = registry.try_get(eid); - if (transform) - { - return transform->local; - } - - return math::transform::identity; -} - -math::transform get_world_transform(entity::registry& registry, entity::id eid) -{ - const game::component::transform* transform = registry.try_get(eid); - if (transform) - { - return transform->world; - } - - return math::transform::identity; -} - -} // namespace command -} // namespace entity diff --git a/src/entity/commands.hpp b/src/entity/commands.hpp deleted file mode 100644 index 215c939..0000000 --- a/src/entity/commands.hpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_ENTITY_COMMANDS_HPP -#define ANTKEEPER_ENTITY_COMMANDS_HPP - -#include "entity/id.hpp" -#include "entity/registry.hpp" -#include "utility/fundamental-types.hpp" -#include "math/transform-type.hpp" - -namespace entity { - -/// Commands which operate on entity::id components -namespace command { - -void translate(entity::registry& registry, entity::id eid, const float3& translation); -void rotate(entity::registry& registry, entity::id eid, float angle, const float3& axis); -void move_to(entity::registry& registry, entity::id eid, const float3& position); -void warp_to(entity::registry& registry, entity::id eid, const float3& position); -void set_scale(entity::registry& registry, entity::id eid, const float3& scale); -void set_transform(entity::registry& registry, entity::id eid, const math::transform& transform, bool warp = false); -void place(entity::registry& registry, entity::id eid, entity::id celestial_body_id, double altitude, double latitude, double longitude); -void assign_render_layers(entity::registry& registry, entity::id eid, unsigned int layers); -math::transform get_local_transform(entity::registry& registry, entity::id eid); -math::transform get_world_transform(entity::registry& registry, entity::id eid); - -} // namespace command -} // namespace entity - -#endif // ANTKEEPER_ENTITY_COMMANDS_HPP - diff --git a/src/entity/ebt.cpp b/src/entity/ebt.cpp deleted file mode 100644 index fade560..0000000 --- a/src/entity/ebt.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "entity/ebt.hpp" -#include "game/component/transform.hpp" -#include - -namespace entity { -namespace ebt { - -status print(context& context, const std::string& text) -{ - std::cout << text; - return status::success; -} - -status print_eid(context& context) -{ - std::cout << static_cast(context.entity_id) << std::endl; - return status::success; -} - -status warp_to(context& context, float x, float y, float z) -{ - auto& transform = context.registry->get(context.entity_id); - - context.registry->patch - ( - context.entity_id, - [x, y, z](auto& component) - { - component.local.translation = {x, y, z}; - component.warp = true; - } - ); - - return status::success; -} - -bool is_carrying_food(const context& context) -{ - return false; -} - -} // namespace ebt -} // namespace entity diff --git a/src/entity/ebt.hpp b/src/entity/ebt.hpp deleted file mode 100644 index beb5c2e..0000000 --- a/src/entity/ebt.hpp +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_ENTITY_EBT_HPP -#define ANTKEEPER_ENTITY_EBT_HPP - -#include "ai/bt/bt.hpp" -#include "entity/id.hpp" -#include "entity/registry.hpp" - -namespace entity { - -/// Entity behavior tree (EBT) nodes and context. -namespace ebt { - -/** - * EBT context which references an entity and its registry. - */ -struct context -{ - entity::registry* registry; - entity::id entity_id; -}; - -typedef ai::bt::status status; -typedef ai::bt::node node; -typedef ai::bt::leaf_node leaf_node; -typedef ai::bt::decorator_node decorator_node; -typedef ai::bt::composite_node composite_node; -typedef ai::bt::action action; -typedef ai::bt::condition condition; -typedef ai::bt::inverter inverter; -typedef ai::bt::repeater repeater; -typedef ai::bt::succeeder succeeder; -typedef ai::bt::sequence sequence; -typedef ai::bt::selector selector; - -// Actions -status print(context& context, const std::string& text); -status print_eid(context& context); -status warp_to(context& context, float x, float y, float z); - -// Conditions -bool is_carrying_food(const context& context); - -} // namespace ebt -} // namespace entity - -#endif // ANTKEEPER_ENTITY_EBT_HPP - diff --git a/src/entity/ecs.hpp b/src/entity/ecs.hpp deleted file mode 100644 index 229c7a1..0000000 --- a/src/entity/ecs.hpp +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_ENTITY_HPP -#define ANTKEEPER_ENTITY_HPP - -/// Entity-component-system (ECS) -namespace entity {} - -#include "archetype.hpp" -#include "commands.hpp" -#include "id.hpp" -#include "registry.hpp" - -#endif // ANTKEEPER_ENTITY_HPP diff --git a/src/event/channel.hpp b/src/event/channel.hpp deleted file mode 100644 index aa5c26e..0000000 --- a/src/event/channel.hpp +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_EVENT_CHANNEL_HPP -#define ANTKEEPER_EVENT_CHANNEL_HPP - -#include -#include -#include -#include -#include "event/subscriber.hpp" -#include "event/subscription.hpp" -#include "event/queue.hpp" - -namespace event { - -template -class publisher; - -/** - * Channel through which messages are published. - * - * @tparam T Message type. - */ -template -class channel -{ -public: - /// Message type. - typedef T message_type; - - /// Subscriber function object type. - typedef subscriber subscriber_type; - - /** - * Subscribes a function object to messages published through this channel. - * - * @param subscriber Subscriber function object which will received published messages. - * - * @return Shared subscription object which will unsubscribe the subscriber on destruction. - */ - [[nodiscard]] std::shared_ptr subscribe(subscriber_type&& subscriber) - { - // Construct shared pointer to subscriber function - std::shared_ptr shared_subscriber = std::make_shared(std::move(subscriber)); - - // Append subscriber to subscriber list and store iterator - auto iterator = subscribers.insert(subscribers.end(), shared_subscriber); - - // Construct and return a shared subscription object which removes the subscriber from the subscriber list when unsubscribed or destructed - return std::make_shared - ( - std::static_pointer_cast(shared_subscriber), - [this, iterator = std::move(iterator)] - { - this->subscribers.erase(iterator); - } - ); - } - - /** - * Subscribes a message queue to messages published through this channel. - * - * @param queue Message queue which will received published messages. - * - * @return Shared subscription object which will unsubscribe the queue on destruction. - */ - [[nodiscard]] std::shared_ptr subscribe(event::queue& queue) - { - return subscribe - ( - [&queue](const message_type& message) - { - queue.enqueue(message); - } - ); - } - -private: - friend class publisher; - - /// List of subscribers. - std::list> subscribers; -}; - -} // namespace event - -#endif // ANTKEEPER_EVENT_CHANNEL_HPP diff --git a/src/event/event.hpp b/src/event/event.hpp deleted file mode 100644 index 8f45bfd..0000000 --- a/src/event/event.hpp +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_EVENT_HPP -#define ANTKEEPER_EVENT_HPP - -/// Publish-subscribe messaging. -namespace event {} - -#include "event/channel.hpp" -#include "event/publisher.hpp" -#include "event/queue.hpp" -#include "event/subscriber.hpp" -#include "event/subscription.hpp" - -#endif // ANTKEEPER_EVENT_HPP diff --git a/src/event/publisher.hpp b/src/event/publisher.hpp deleted file mode 100644 index fcd8f2a..0000000 --- a/src/event/publisher.hpp +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_EVENT_PUBLISHER_HPP -#define ANTKEEPER_EVENT_PUBLISHER_HPP - -#include -#include -#include "event/channel.hpp" - -namespace event { - -/** - * Publishes messages to subscribers. - * - * @tparam T Message type. - */ -template -class publisher -{ -public: - /// Message type. - typedef T message_type; - - /// Channel type. - typedef channel channel_type; - - /** - * Publishes a message. - * - * @tparam ExecutionPolicy Execution policy type. - * - * @param policy Execution policy to use. - * @param message Message to publish. - */ - /// @{ - template - void publish(ExecutionPolicy&& policy, const message_type& message) const - { - std::for_each - ( - policy, - std::begin(m_channel.subscribers), - std::end(m_channel.subscribers), - [&](const auto& subscriber) - { - (*subscriber)(message); - } - ); - } - - void publish(const message_type& message) const - { - publish(std::execution::seq, message); - } - /// @} - - /** - * Returns the channel through which messages are published. - */ - /// @{ - [[nodiscard]] inline const channel_type& channel() const noexcept - { - return m_channel; - } - - [[nodiscard]] inline channel_type& channel() noexcept - { - return m_channel; - } - /// @} - -private: - channel_type m_channel; -}; - -} // namespace event - -#endif // ANTKEEPER_EVENT_PUBLISHER_HPP diff --git a/src/event/queue.hpp b/src/event/queue.hpp deleted file mode 100644 index b2cd36d..0000000 --- a/src/event/queue.hpp +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_EVENT_QUEUE_HPP -#define ANTKEEPER_EVENT_QUEUE_HPP - -#include "event/subscriber.hpp" -#include "event/subscription.hpp" -#include -#include -#include -#include -#include -#include -#include - -namespace event { - -/** - * Collects messages from publishers to be forwarded to subscribers when desired. - */ -class queue -{ -public: - /** - * Subscribes a function object to messages published by this queue. - * - * @tparam T Message type. - * - * @param subscriber Function object to subscribe. - * - * @return Shared subscription object which will unsubscribe the subscriber on destruction. - * - * @TODO This function should be available through an interface class which does not expose the queue's message-sending functions, such as event::channel for publishers. - */ - template - [[nodiscard]] std::shared_ptr subscribe(subscriber&& subscriber) - { - // Allocate shared pointer to std::any object containing subscriber - std::shared_ptr shared_subscriber = std::make_shared(std::make_any>(std::move(subscriber))); - - // Append subscriber to subscriber list and store iterator - auto iterator = subscribers.emplace(std::type_index(typeid(T)), shared_subscriber); - - // Construct and return a shared subscription object which removes the subscriber from the subscriber list when unsubscribed or destructed - return std::make_shared - ( - std::static_pointer_cast(shared_subscriber), - [this, iterator = std::move(iterator)]() - { - this->subscribers.erase(iterator); - } - ); - } - - /** - * Adds a message to the queue, to be distributed later. - * - * @tparam T Message type. - * - * @param message Message to enqueue. - */ - template - void enqueue(const T& message) - { - messages.emplace_back - ( - [this, message]() - { - this->forward(message); - } - ); - } - - /** - * Forwards queued messages, in FIFO order, to subscribers. - */ - void flush() - { - while (!messages.empty()) - { - messages.front()(); - messages.pop_front(); - } - } - - /** - * Removes all messages from the queue. - */ - void clear() - { - messages.clear(); - } - - /** - * Returns `true` if there are no messages in the queue, `false` otherwise. - */ - [[nodiscard]] inline bool empty() const noexcept - { - return messages.empty(); - } - -private: - /** - * Forwards a message to subscribers of the message type. - * - * @tparam T Message type. - * - * @param message Message to forward. - */ - template - void forward(const T& message) const - { - // For each subscriber of the given message type - const auto range = subscribers.equal_range(std::type_index(typeid(T))); - for (auto i = range.first; i != range.second; ++i) - { - // Send message to subscriber - std::any_cast>(*(i->second))(message); - } - } - - std::multimap> subscribers; - std::list> messages; -}; - -} // namespace event - -#endif // ANTKEEPER_EVENT_QUEUE_HPP diff --git a/src/event/subscription.cpp b/src/event/subscription.cpp deleted file mode 100644 index a772dfb..0000000 --- a/src/event/subscription.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "event/subscription.hpp" -#include - -namespace event { - -subscription::subscription(std::weak_ptr&& subscriber, unsubscribe_type&& unsubscriber): - subscriber(std::move(subscriber)), - unsubscriber(std::move(unsubscriber)) -{} - -subscription::~subscription() -{ - unsubscribe(); -} - -bool subscription::expired() const noexcept -{ - return subscriber.expired(); -} - -void subscription::unsubscribe() -{ - if (!expired()) - { - unsubscriber(); - } -} - -} // namespace event diff --git a/src/event/subscription.hpp b/src/event/subscription.hpp deleted file mode 100644 index aea7068..0000000 --- a/src/event/subscription.hpp +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_EVENT_SUBSCRIPTION_HPP -#define ANTKEEPER_EVENT_SUBSCRIPTION_HPP - -#include -#include - -namespace event { - -/** - * Unsubscribes a subscriber on destruction. - */ -class subscription -{ -public: - /// Unsubscribe function object type. - typedef std::function unsubscribe_type; - - /** - * Constructs a subscription. - * - * @param subscriber Weak pointer to the subscriber. - * @param unsubscriber Unsubscribe function object. - */ - subscription(std::weak_ptr&& subscriber, unsubscribe_type&& unsubscriber); - - /** - * Unsubscribes the subscriber and destructs the subscription. - */ - ~subscription(); - - /** - * Returns `true` if the subscription is no longer active, `false` otherwise. - */ - [[nodiscard]] bool expired() const noexcept; - - /** - * Unsubscribes the subscriber. - */ - void unsubscribe(); - -private: - std::weak_ptr subscriber; - unsubscribe_type unsubscriber; -}; - -} // namespace event - -#endif // ANTKEEPER_EVENT_SUBSCRIPTION_HPP diff --git a/src/game/ant/caste.hpp b/src/game/ant/caste.hpp index cf29fc0..5d47ca2 100644 --- a/src/game/ant/caste.hpp +++ b/src/game/ant/caste.hpp @@ -20,7 +20,8 @@ #ifndef ANTKEEPER_GAME_ANT_CASTE_HPP #define ANTKEEPER_GAME_ANT_CASTE_HPP -namespace game { +#include + namespace ant { /** @@ -28,7 +29,7 @@ namespace ant { * * @see https://www.antwiki.org/wiki/Caste_Terminology */ -enum class caste +enum class caste: std::uint8_t { /// Queen caste type. queen, @@ -44,6 +45,5 @@ enum class caste }; } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_CASTE_HPP diff --git a/src/game/ant/cladogenesis.cpp b/src/game/ant/cladogenesis.cpp index 49482ff..3c3b700 100644 --- a/src/game/ant/cladogenesis.cpp +++ b/src/game/ant/cladogenesis.cpp @@ -19,7 +19,6 @@ #include "game/ant/cladogenesis.hpp" -namespace game { namespace ant { genome* cladogenesis(const gene_pool& pool, std::random_device& rng) @@ -55,4 +54,3 @@ genome* cladogenesis(const gene_pool& pool, std::random_device& rng) } } // namespace ant -} // namespace game diff --git a/src/game/ant/cladogenesis.hpp b/src/game/ant/cladogenesis.hpp index 7f00ed5..510a0f0 100644 --- a/src/game/ant/cladogenesis.hpp +++ b/src/game/ant/cladogenesis.hpp @@ -24,7 +24,6 @@ #include "game/ant/gene-pool.hpp" #include -namespace game { namespace ant { /** @@ -38,6 +37,5 @@ namespace ant { genome* cladogenesis(const gene_pool& pool, std::random_device& rng); } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_CLADOGENESIS_HPP diff --git a/src/game/ant/gene-frequency-table.hpp b/src/game/ant/gene-frequency-table.hpp index c66a51b..fe92f8c 100644 --- a/src/game/ant/gene-frequency-table.hpp +++ b/src/game/ant/gene-frequency-table.hpp @@ -23,7 +23,6 @@ #include #include -namespace game { namespace ant { /** @@ -61,6 +60,5 @@ struct gene_frequency_table }; } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_GENE_FREQUENCY_TABLE_HPP diff --git a/src/game/ant/gene-pool.hpp b/src/game/ant/gene-pool.hpp index 213e124..1b9ac07 100644 --- a/src/game/ant/gene-pool.hpp +++ b/src/game/ant/gene-pool.hpp @@ -44,7 +44,6 @@ #include "game/ant/gene/waist.hpp" #include "game/ant/gene/wings.hpp" -namespace game { namespace ant { /** @@ -80,6 +79,5 @@ struct gene_pool }; } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_GENE_POOL_HPP diff --git a/src/game/ant/gene/antennae.hpp b/src/game/ant/gene/antennae.hpp index 4548c37..02b1ff1 100644 --- a/src/game/ant/gene/antennae.hpp +++ b/src/game/ant/gene/antennae.hpp @@ -23,7 +23,6 @@ #include "game/ant/phene/antennae.hpp" #include "game/ant/gene/polyphenic-gene.hpp" -namespace game { namespace ant { namespace gene { @@ -32,6 +31,5 @@ typedef polyphenic_gene antennae; } // namespace gene } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_GENE_ANTENNAE_HPP diff --git a/src/game/ant/gene/body-size.hpp b/src/game/ant/gene/body-size.hpp index 8a66507..02e47d3 100644 --- a/src/game/ant/gene/body-size.hpp +++ b/src/game/ant/gene/body-size.hpp @@ -23,7 +23,6 @@ #include "game/ant/phene/body-size.hpp" #include "game/ant/gene/polyphenic-gene.hpp" -namespace game { namespace ant { namespace gene { @@ -32,6 +31,5 @@ typedef polyphenic_gene body_size; } // namespace gene } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_GENE_BODY_SIZE_HPP diff --git a/src/game/ant/gene/cocoon.hpp b/src/game/ant/gene/cocoon.hpp index ec18920..8a1be19 100644 --- a/src/game/ant/gene/cocoon.hpp +++ b/src/game/ant/gene/cocoon.hpp @@ -23,7 +23,6 @@ #include "game/ant/phene/cocoon.hpp" #include "game/ant/gene/monophenic-gene.hpp" -namespace game { namespace ant { namespace gene { @@ -32,6 +31,5 @@ typedef monophenic_gene cocoon; } // namespace gene } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_GENE_COCOON_HPP diff --git a/src/game/ant/gene/diet.hpp b/src/game/ant/gene/diet.hpp index f2f0a60..501dcbd 100644 --- a/src/game/ant/gene/diet.hpp +++ b/src/game/ant/gene/diet.hpp @@ -23,7 +23,6 @@ #include "game/ant/phene/diet.hpp" #include "game/ant/gene/monophenic-gene.hpp" -namespace game { namespace ant { namespace gene { @@ -32,6 +31,5 @@ typedef monophenic_gene diet; } // namespace gene } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_GENE_DIET_HPP diff --git a/src/game/ant/gene/egg.hpp b/src/game/ant/gene/egg.hpp index 0b0ca6a..db703cd 100644 --- a/src/game/ant/gene/egg.hpp +++ b/src/game/ant/gene/egg.hpp @@ -23,7 +23,6 @@ #include "game/ant/phene/egg.hpp" #include "game/ant/gene/monophenic-gene.hpp" -namespace game { namespace ant { namespace gene { @@ -32,6 +31,5 @@ typedef monophenic_gene egg; } // namespace gene } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_GENE_EGG_HPP diff --git a/src/game/ant/gene/eyes.hpp b/src/game/ant/gene/eyes.hpp index b5d7e60..cd24783 100644 --- a/src/game/ant/gene/eyes.hpp +++ b/src/game/ant/gene/eyes.hpp @@ -23,7 +23,6 @@ #include "game/ant/phene/eyes.hpp" #include "game/ant/gene/polyphenic-gene.hpp" -namespace game { namespace ant { namespace gene { @@ -32,6 +31,5 @@ typedef polyphenic_gene eyes; } // namespace gene } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_GENE_EYES_HPP diff --git a/src/game/ant/gene/foraging-time.hpp b/src/game/ant/gene/foraging-time.hpp index 1c281ca..7658911 100644 --- a/src/game/ant/gene/foraging-time.hpp +++ b/src/game/ant/gene/foraging-time.hpp @@ -23,7 +23,6 @@ #include "game/ant/phene/foraging-time.hpp" #include "game/ant/gene/monophenic-gene.hpp" -namespace game { namespace ant { namespace gene { @@ -32,6 +31,5 @@ typedef monophenic_gene foraging_time; } // namespace gene } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_GENE_FORAGING_TIME_HPP diff --git a/src/game/ant/gene/founding-mode.hpp b/src/game/ant/gene/founding-mode.hpp index 77eacba..17ba4a1 100644 --- a/src/game/ant/gene/founding-mode.hpp +++ b/src/game/ant/gene/founding-mode.hpp @@ -23,7 +23,6 @@ #include "game/ant/phene/founding-mode.hpp" #include "game/ant/gene/monophenic-gene.hpp" -namespace game { namespace ant { namespace gene { @@ -32,6 +31,5 @@ typedef monophenic_gene founding_mode; } // namespace gene } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_GENE_FOUNDING_MODE_HPP diff --git a/src/game/ant/gene/gaster.hpp b/src/game/ant/gene/gaster.hpp index 64fda97..1911f4d 100644 --- a/src/game/ant/gene/gaster.hpp +++ b/src/game/ant/gene/gaster.hpp @@ -23,7 +23,6 @@ #include "game/ant/phene/gaster.hpp" #include "game/ant/gene/polyphenic-gene.hpp" -namespace game { namespace ant { namespace gene { @@ -32,6 +31,5 @@ typedef polyphenic_gene gaster; } // namespace gene } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_GENE_GASTER_HPP diff --git a/src/game/ant/gene/head.hpp b/src/game/ant/gene/head.hpp index e42a973..341f62f 100644 --- a/src/game/ant/gene/head.hpp +++ b/src/game/ant/gene/head.hpp @@ -23,7 +23,6 @@ #include "game/ant/phene/head.hpp" #include "game/ant/gene/polyphenic-gene.hpp" -namespace game { namespace ant { namespace gene { @@ -32,6 +31,5 @@ typedef polyphenic_gene head; } // namespace gene } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_GENE_HEAD_HPP diff --git a/src/game/ant/gene/larva.hpp b/src/game/ant/gene/larva.hpp index e7340df..4d9b4ce 100644 --- a/src/game/ant/gene/larva.hpp +++ b/src/game/ant/gene/larva.hpp @@ -23,7 +23,6 @@ #include "game/ant/phene/larva.hpp" #include "game/ant/gene/monophenic-gene.hpp" -namespace game { namespace ant { namespace gene { @@ -32,6 +31,5 @@ typedef monophenic_gene larva; } // namespace gene } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_GENE_LARVA_HPP diff --git a/src/game/ant/gene/legs.hpp b/src/game/ant/gene/legs.hpp index 2ec62d2..d4c484f 100644 --- a/src/game/ant/gene/legs.hpp +++ b/src/game/ant/gene/legs.hpp @@ -23,7 +23,6 @@ #include "game/ant/phene/legs.hpp" #include "game/ant/gene/polyphenic-gene.hpp" -namespace game { namespace ant { namespace gene { @@ -32,6 +31,5 @@ typedef polyphenic_gene legs; } // namespace gene } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_GENE_LEGS_HPP diff --git a/src/game/ant/gene/loader/antennae-loader.cpp b/src/game/ant/gene/loader/antennae-loader.cpp index 85a8c06..6992071 100644 --- a/src/game/ant/gene/loader/antennae-loader.cpp +++ b/src/game/ant/gene/loader/antennae-loader.cpp @@ -18,14 +18,14 @@ */ #include "game/ant/gene/loader/gene-loader.hpp" -#include "resources/resource-loader.hpp" -#include "resources/resource-manager.hpp" -#include "resources/json.hpp" +#include +#include +#include #include "game/ant/gene/antennae.hpp" -#include "render/model.hpp" +#include #include -using namespace game::ant; +using namespace ::ant; static void deserialize_antennae_phene(phene::antennae& phene, const json& phene_element, resource_manager* resource_manager) { diff --git a/src/game/ant/gene/loader/body-size-loader.cpp b/src/game/ant/gene/loader/body-size-loader.cpp index 73903d2..63bd63a 100644 --- a/src/game/ant/gene/loader/body-size-loader.cpp +++ b/src/game/ant/gene/loader/body-size-loader.cpp @@ -18,13 +18,13 @@ */ #include "game/ant/gene/loader/gene-loader.hpp" -#include "resources/resource-loader.hpp" -#include "resources/resource-manager.hpp" -#include "resources/json.hpp" +#include +#include +#include #include "game/ant/gene/body-size.hpp" #include -using namespace game::ant; +using namespace ::ant; static void deserialize_body_size_phene(phene::body_size& phene, const json& phene_element, resource_manager* resource_manager) { diff --git a/src/game/ant/gene/loader/cocoon-loader.cpp b/src/game/ant/gene/loader/cocoon-loader.cpp index f86925a..f784378 100644 --- a/src/game/ant/gene/loader/cocoon-loader.cpp +++ b/src/game/ant/gene/loader/cocoon-loader.cpp @@ -18,14 +18,14 @@ */ #include "game/ant/gene/loader/gene-loader.hpp" -#include "resources/resource-loader.hpp" -#include "resources/resource-manager.hpp" -#include "resources/json.hpp" +#include +#include +#include #include "game/ant/gene/cocoon.hpp" -#include "render/model.hpp" +#include #include -using namespace game::ant; +using namespace ::ant; static void deserialize_cocoon_phene(phene::cocoon& phene, const json& phene_element, resource_manager* resource_manager) { diff --git a/src/game/ant/gene/loader/diet-loader.cpp b/src/game/ant/gene/loader/diet-loader.cpp index b478517..80d37a9 100644 --- a/src/game/ant/gene/loader/diet-loader.cpp +++ b/src/game/ant/gene/loader/diet-loader.cpp @@ -18,15 +18,15 @@ */ #include "game/ant/gene/loader/gene-loader.hpp" -#include "resources/resource-loader.hpp" -#include "resources/resource-manager.hpp" -#include "resources/json.hpp" +#include +#include +#include #include "game/ant/gene/diet.hpp" -#include "math/angles.hpp" -#include "math/numbers.hpp" +#include +#include #include -using namespace game::ant; +using namespace ::ant; static void deserialize_diet_phene(phene::diet& phene, const json& phene_element, resource_manager* resource_manager) { diff --git a/src/game/ant/gene/loader/egg-loader.cpp b/src/game/ant/gene/loader/egg-loader.cpp index 2727608..0d1a8ab 100644 --- a/src/game/ant/gene/loader/egg-loader.cpp +++ b/src/game/ant/gene/loader/egg-loader.cpp @@ -18,14 +18,14 @@ */ #include "game/ant/gene/loader/gene-loader.hpp" -#include "resources/resource-loader.hpp" -#include "resources/resource-manager.hpp" -#include "resources/json.hpp" +#include +#include +#include #include "game/ant/gene/egg.hpp" -#include "render/model.hpp" +#include #include -using namespace game::ant; +using namespace ::ant; static void deserialize_egg_phene(phene::egg& phene, const json& phene_element, resource_manager* resource_manager) { diff --git a/src/game/ant/gene/loader/eyes-loader.cpp b/src/game/ant/gene/loader/eyes-loader.cpp index 6682435..3c2fe35 100644 --- a/src/game/ant/gene/loader/eyes-loader.cpp +++ b/src/game/ant/gene/loader/eyes-loader.cpp @@ -18,14 +18,14 @@ */ #include "game/ant/gene/loader/gene-loader.hpp" -#include "resources/resource-loader.hpp" -#include "resources/resource-manager.hpp" -#include "resources/json.hpp" +#include +#include +#include #include "game/ant/gene/eyes.hpp" -#include "render/model.hpp" +#include #include -using namespace game::ant; +using namespace ::ant; static void deserialize_eyes_phene(phene::eyes& phene, const json& phene_element, resource_manager* resource_manager) { diff --git a/src/game/ant/gene/loader/foraging-time-loader.cpp b/src/game/ant/gene/loader/foraging-time-loader.cpp index e8bde42..0c8bb77 100644 --- a/src/game/ant/gene/loader/foraging-time-loader.cpp +++ b/src/game/ant/gene/loader/foraging-time-loader.cpp @@ -18,15 +18,15 @@ */ #include "game/ant/gene/loader/gene-loader.hpp" -#include "resources/resource-loader.hpp" -#include "resources/resource-manager.hpp" -#include "resources/json.hpp" +#include +#include +#include #include "game/ant/gene/foraging-time.hpp" -#include "math/angles.hpp" -#include "math/numbers.hpp" +#include +#include #include -using namespace game::ant; +using namespace ::ant; static void deserialize_foraging_time_phene(phene::foraging_time& phene, const json& phene_element, resource_manager* resource_manager) { diff --git a/src/game/ant/gene/loader/founding-mode-loader.cpp b/src/game/ant/gene/loader/founding-mode-loader.cpp index 54e703f..0daa1ea 100644 --- a/src/game/ant/gene/loader/founding-mode-loader.cpp +++ b/src/game/ant/gene/loader/founding-mode-loader.cpp @@ -18,15 +18,15 @@ */ #include "game/ant/gene/loader/gene-loader.hpp" -#include "resources/resource-loader.hpp" -#include "resources/resource-manager.hpp" -#include "resources/json.hpp" +#include +#include +#include #include "game/ant/gene/founding-mode.hpp" -#include "math/angles.hpp" -#include "math/numbers.hpp" +#include +#include #include -using namespace game::ant; +using namespace ::ant; static void deserialize_founding_mode_phene(phene::founding_mode& phene, const json& phene_element, resource_manager* resource_manager) { diff --git a/src/game/ant/gene/loader/gaster-loader.cpp b/src/game/ant/gene/loader/gaster-loader.cpp index 5d2878e..621f0ce 100644 --- a/src/game/ant/gene/loader/gaster-loader.cpp +++ b/src/game/ant/gene/loader/gaster-loader.cpp @@ -18,14 +18,14 @@ */ #include "game/ant/gene/loader/gene-loader.hpp" -#include "resources/resource-loader.hpp" -#include "resources/resource-manager.hpp" -#include "resources/json.hpp" +#include +#include +#include #include "game/ant/gene/gaster.hpp" -#include "render/model.hpp" +#include #include -using namespace game::ant; +using namespace ::ant; static void deserialize_gaster_phene(phene::gaster& phene, const json& phene_element, resource_manager* resource_manager) { diff --git a/src/game/ant/gene/loader/gene-loader.hpp b/src/game/ant/gene/loader/gene-loader.hpp index e9c377d..f20c69c 100644 --- a/src/game/ant/gene/loader/gene-loader.hpp +++ b/src/game/ant/gene/loader/gene-loader.hpp @@ -22,10 +22,9 @@ #include "game/ant/gene/monophenic-gene.hpp" #include "game/ant/gene/polyphenic-gene.hpp" -#include "resources/json.hpp" -#include "resources/resource-manager.hpp" +#include +#include -namespace game { namespace ant { namespace gene { @@ -82,6 +81,5 @@ void deserialize_gene(polyphenic_gene& gene, void (*deserialize_phene)(T&, co } // namespace gene } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_GENE_GENE_LOADER_HPP diff --git a/src/game/ant/gene/loader/head-loader.cpp b/src/game/ant/gene/loader/head-loader.cpp index f52cdb5..99ffb19 100644 --- a/src/game/ant/gene/loader/head-loader.cpp +++ b/src/game/ant/gene/loader/head-loader.cpp @@ -18,14 +18,14 @@ */ #include "game/ant/gene/loader/gene-loader.hpp" -#include "resources/resource-loader.hpp" -#include "resources/resource-manager.hpp" -#include "resources/json.hpp" +#include +#include +#include #include "game/ant/gene/head.hpp" -#include "render/model.hpp" +#include #include -using namespace game::ant; +using namespace ::ant; static void deserialize_head_phene(phene::head& phene, const json& phene_element, resource_manager* resource_manager) { diff --git a/src/game/ant/gene/loader/larva-loader.cpp b/src/game/ant/gene/loader/larva-loader.cpp index 996ac32..0df4792 100644 --- a/src/game/ant/gene/loader/larva-loader.cpp +++ b/src/game/ant/gene/loader/larva-loader.cpp @@ -18,14 +18,14 @@ */ #include "game/ant/gene/loader/gene-loader.hpp" -#include "resources/resource-loader.hpp" -#include "resources/resource-manager.hpp" -#include "resources/json.hpp" +#include +#include +#include #include "game/ant/gene/larva.hpp" -#include "render/model.hpp" +#include #include -using namespace game::ant; +using namespace ::ant; static void deserialize_larva_phene(phene::larva& phene, const json& phene_element, resource_manager* resource_manager) { diff --git a/src/game/ant/gene/loader/legs-loader.cpp b/src/game/ant/gene/loader/legs-loader.cpp index 4637cde..5f46985 100644 --- a/src/game/ant/gene/loader/legs-loader.cpp +++ b/src/game/ant/gene/loader/legs-loader.cpp @@ -18,14 +18,14 @@ */ #include "game/ant/gene/loader/gene-loader.hpp" -#include "resources/resource-loader.hpp" -#include "resources/resource-manager.hpp" -#include "resources/json.hpp" +#include +#include +#include #include "game/ant/gene/legs.hpp" -#include "render/model.hpp" +#include #include -using namespace game::ant; +using namespace ::ant; static void deserialize_legs_phene(phene::legs& phene, const json& phene_element, resource_manager* resource_manager) { diff --git a/src/game/ant/gene/loader/mandibles-loader.cpp b/src/game/ant/gene/loader/mandibles-loader.cpp index e5fd28e..c666cc3 100644 --- a/src/game/ant/gene/loader/mandibles-loader.cpp +++ b/src/game/ant/gene/loader/mandibles-loader.cpp @@ -18,14 +18,14 @@ */ #include "game/ant/gene/loader/gene-loader.hpp" -#include "resources/resource-loader.hpp" -#include "resources/resource-manager.hpp" -#include "resources/json.hpp" +#include +#include +#include #include "game/ant/gene/mandibles.hpp" -#include "render/model.hpp" +#include #include -using namespace game::ant; +using namespace ::ant; static void deserialize_mandibles_phene(phene::mandibles& phene, const json& phene_element, resource_manager* resource_manager) { diff --git a/src/game/ant/gene/loader/mesosoma-loader.cpp b/src/game/ant/gene/loader/mesosoma-loader.cpp index a9dd4d3..7689186 100644 --- a/src/game/ant/gene/loader/mesosoma-loader.cpp +++ b/src/game/ant/gene/loader/mesosoma-loader.cpp @@ -18,14 +18,14 @@ */ #include "game/ant/gene/loader/gene-loader.hpp" -#include "resources/resource-loader.hpp" -#include "resources/resource-manager.hpp" -#include "resources/json.hpp" +#include +#include +#include #include "game/ant/gene/mesosoma.hpp" -#include "render/model.hpp" +#include #include -using namespace game::ant; +using namespace ::ant; static void deserialize_mesosoma_phene(phene::mesosoma& phene, const json& phene_element, resource_manager* resource_manager) { diff --git a/src/game/ant/gene/loader/nest-site-loader.cpp b/src/game/ant/gene/loader/nest-site-loader.cpp index 21edcc4..86adce2 100644 --- a/src/game/ant/gene/loader/nest-site-loader.cpp +++ b/src/game/ant/gene/loader/nest-site-loader.cpp @@ -18,15 +18,15 @@ */ #include "game/ant/gene/loader/gene-loader.hpp" -#include "resources/resource-loader.hpp" -#include "resources/resource-manager.hpp" -#include "resources/json.hpp" +#include +#include +#include #include "game/ant/gene/nest-site.hpp" -#include "math/angles.hpp" -#include "math/numbers.hpp" +#include +#include #include -using namespace game::ant; +using namespace ::ant; static void deserialize_nest_site_phene(phene::nest_site& phene, const json& phene_element, resource_manager* resource_manager) { diff --git a/src/game/ant/gene/loader/ocelli-loader.cpp b/src/game/ant/gene/loader/ocelli-loader.cpp index 147e9f6..1a90780 100644 --- a/src/game/ant/gene/loader/ocelli-loader.cpp +++ b/src/game/ant/gene/loader/ocelli-loader.cpp @@ -18,14 +18,14 @@ */ #include "game/ant/gene/loader/gene-loader.hpp" -#include "resources/resource-loader.hpp" -#include "resources/resource-manager.hpp" -#include "resources/json.hpp" +#include +#include +#include #include "game/ant/gene/ocelli.hpp" -#include "render/model.hpp" +#include #include -using namespace game::ant; +using namespace ::ant; static void deserialize_ocelli_phene(phene::ocelli& phene, const json& phene_element, resource_manager* resource_manager) { diff --git a/src/game/ant/gene/loader/pigmentation-loader.cpp b/src/game/ant/gene/loader/pigmentation-loader.cpp index fcc6d44..682a956 100644 --- a/src/game/ant/gene/loader/pigmentation-loader.cpp +++ b/src/game/ant/gene/loader/pigmentation-loader.cpp @@ -18,14 +18,14 @@ */ #include "game/ant/gene/loader/gene-loader.hpp" -#include "resources/resource-loader.hpp" -#include "resources/resource-manager.hpp" -#include "resources/json.hpp" +#include +#include +#include #include "game/ant/gene/pigmentation.hpp" -#include "render/material.hpp" +#include #include -using namespace game::ant; +using namespace ::ant; static void deserialize_pigmentation_phene(phene::pigmentation& phene, const json& phene_element, resource_manager* resource_manager) { diff --git a/src/game/ant/gene/loader/pilosity-loader.cpp b/src/game/ant/gene/loader/pilosity-loader.cpp index 9eb343b..ef8dc47 100644 --- a/src/game/ant/gene/loader/pilosity-loader.cpp +++ b/src/game/ant/gene/loader/pilosity-loader.cpp @@ -18,13 +18,13 @@ */ #include "game/ant/gene/loader/gene-loader.hpp" -#include "resources/resource-loader.hpp" -#include "resources/resource-manager.hpp" -#include "resources/json.hpp" +#include +#include +#include #include "game/ant/gene/pilosity.hpp" #include -using namespace game::ant; +using namespace ::ant; static void deserialize_pilosity_phene(phene::pilosity& phene, const json& phene_element, resource_manager* resource_manager) { diff --git a/src/game/ant/gene/loader/sculpturing-loader.cpp b/src/game/ant/gene/loader/sculpturing-loader.cpp index 272a841..29c07b8 100644 --- a/src/game/ant/gene/loader/sculpturing-loader.cpp +++ b/src/game/ant/gene/loader/sculpturing-loader.cpp @@ -18,14 +18,14 @@ */ #include "game/ant/gene/loader/gene-loader.hpp" -#include "resources/resource-loader.hpp" -#include "resources/resource-manager.hpp" -#include "resources/json.hpp" +#include +#include +#include #include "game/ant/gene/sculpturing.hpp" -#include "gl/texture-2d.hpp" +#include #include -using namespace game::ant; +using namespace ::ant; static void deserialize_sculpturing_phene(phene::sculpturing& phene, const json& phene_element, resource_manager* resource_manager) { diff --git a/src/game/ant/gene/loader/sting-loader.cpp b/src/game/ant/gene/loader/sting-loader.cpp index 7a40a41..d2838ec 100644 --- a/src/game/ant/gene/loader/sting-loader.cpp +++ b/src/game/ant/gene/loader/sting-loader.cpp @@ -18,14 +18,14 @@ */ #include "game/ant/gene/loader/gene-loader.hpp" -#include "resources/resource-loader.hpp" -#include "resources/resource-manager.hpp" -#include "resources/json.hpp" +#include +#include +#include #include "game/ant/gene/sting.hpp" -#include "render/model.hpp" +#include #include -using namespace game::ant; +using namespace ::ant; static void deserialize_sting_phene(phene::sting& phene, const json& phene_element, resource_manager* resource_manager) { diff --git a/src/game/ant/gene/loader/waist-loader.cpp b/src/game/ant/gene/loader/waist-loader.cpp index e1bd683..781c56f 100644 --- a/src/game/ant/gene/loader/waist-loader.cpp +++ b/src/game/ant/gene/loader/waist-loader.cpp @@ -18,14 +18,14 @@ */ #include "game/ant/gene/loader/gene-loader.hpp" -#include "resources/resource-loader.hpp" -#include "resources/resource-manager.hpp" -#include "resources/json.hpp" +#include +#include +#include #include "game/ant/gene/waist.hpp" -#include "render/model.hpp" +#include #include -using namespace game::ant; +using namespace ::ant; static void deserialize_waist_phene(phene::waist& phene, const json& phene_element, resource_manager* resource_manager) { diff --git a/src/game/ant/gene/loader/wings-loader.cpp b/src/game/ant/gene/loader/wings-loader.cpp index 26a85ac..212b94b 100644 --- a/src/game/ant/gene/loader/wings-loader.cpp +++ b/src/game/ant/gene/loader/wings-loader.cpp @@ -18,14 +18,14 @@ */ #include "game/ant/gene/loader/gene-loader.hpp" -#include "resources/resource-loader.hpp" -#include "resources/resource-manager.hpp" -#include "resources/json.hpp" +#include +#include +#include #include "game/ant/gene/wings.hpp" -#include "render/model.hpp" +#include #include -using namespace game::ant; +using namespace ::ant; static void deserialize_wings_phene(phene::wings& phene, const json& phene_element, resource_manager* resource_manager) { diff --git a/src/game/ant/gene/mandibles.hpp b/src/game/ant/gene/mandibles.hpp index 054fcf6..f398135 100644 --- a/src/game/ant/gene/mandibles.hpp +++ b/src/game/ant/gene/mandibles.hpp @@ -23,7 +23,6 @@ #include "game/ant/phene/mandibles.hpp" #include "game/ant/gene/polyphenic-gene.hpp" -namespace game { namespace ant { namespace gene { @@ -32,6 +31,5 @@ typedef polyphenic_gene mandibles; } // namespace gene } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_GENE_MANDIBLES_HPP diff --git a/src/game/ant/gene/mesosoma.hpp b/src/game/ant/gene/mesosoma.hpp index 02763ff..808910a 100644 --- a/src/game/ant/gene/mesosoma.hpp +++ b/src/game/ant/gene/mesosoma.hpp @@ -23,7 +23,6 @@ #include "game/ant/phene/mesosoma.hpp" #include "game/ant/gene/polyphenic-gene.hpp" -namespace game { namespace ant { namespace gene { @@ -32,6 +31,5 @@ typedef polyphenic_gene mesosoma; } // namespace gene } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_GENE_MESOSOMA_HPP diff --git a/src/game/ant/gene/monophenic-gene.hpp b/src/game/ant/gene/monophenic-gene.hpp index 5884c79..22d1556 100644 --- a/src/game/ant/gene/monophenic-gene.hpp +++ b/src/game/ant/gene/monophenic-gene.hpp @@ -22,7 +22,6 @@ #include -namespace game { namespace ant { namespace gene { @@ -43,6 +42,5 @@ struct monophenic_gene } // namespace gene } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_GENE_MONOPHENIC_GENE_HPP diff --git a/src/game/ant/gene/nest-site.hpp b/src/game/ant/gene/nest-site.hpp index 951a3e5..4d1761b 100644 --- a/src/game/ant/gene/nest-site.hpp +++ b/src/game/ant/gene/nest-site.hpp @@ -23,7 +23,6 @@ #include "game/ant/phene/nest-site.hpp" #include "game/ant/gene/monophenic-gene.hpp" -namespace game { namespace ant { namespace gene { @@ -32,6 +31,5 @@ typedef monophenic_gene nest_site; } // namespace gene } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_GENE_NEST_SITE_HPP diff --git a/src/game/ant/gene/ocelli.hpp b/src/game/ant/gene/ocelli.hpp index 40aa95a..9c07ffd 100644 --- a/src/game/ant/gene/ocelli.hpp +++ b/src/game/ant/gene/ocelli.hpp @@ -23,7 +23,6 @@ #include "game/ant/phene/ocelli.hpp" #include "game/ant/gene/polyphenic-gene.hpp" -namespace game { namespace ant { namespace gene { @@ -32,6 +31,5 @@ typedef polyphenic_gene ocelli; } // namespace gene } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_GENE_OCELLI_HPP diff --git a/src/game/ant/gene/pigmentation.hpp b/src/game/ant/gene/pigmentation.hpp index 9b195a9..3fd9f97 100644 --- a/src/game/ant/gene/pigmentation.hpp +++ b/src/game/ant/gene/pigmentation.hpp @@ -23,7 +23,6 @@ #include "game/ant/phene/pigmentation.hpp" #include "game/ant/gene/polyphenic-gene.hpp" -namespace game { namespace ant { namespace gene { @@ -32,6 +31,5 @@ typedef polyphenic_gene pigmentation; } // namespace gene } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_GENE_PIGMENTATION_HPP diff --git a/src/game/ant/gene/pilosity.hpp b/src/game/ant/gene/pilosity.hpp index afb561c..e6c3252 100644 --- a/src/game/ant/gene/pilosity.hpp +++ b/src/game/ant/gene/pilosity.hpp @@ -23,7 +23,6 @@ #include "game/ant/phene/pilosity.hpp" #include "game/ant/gene/polyphenic-gene.hpp" -namespace game { namespace ant { namespace gene { @@ -32,6 +31,5 @@ typedef polyphenic_gene pilosity; } // namespace gene } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_GENE_PILOSITY_HPP diff --git a/src/game/ant/gene/polyphenic-gene.hpp b/src/game/ant/gene/polyphenic-gene.hpp index d759580..a5e12d2 100644 --- a/src/game/ant/gene/polyphenic-gene.hpp +++ b/src/game/ant/gene/polyphenic-gene.hpp @@ -24,7 +24,6 @@ #include #include -namespace game { namespace ant { namespace gene { @@ -47,6 +46,5 @@ struct polyphenic_gene } // namespace gene } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_GENE_POLYPHENIC_GENE_HPP diff --git a/src/game/ant/gene/sculpturing.hpp b/src/game/ant/gene/sculpturing.hpp index e19bc15..1455fb1 100644 --- a/src/game/ant/gene/sculpturing.hpp +++ b/src/game/ant/gene/sculpturing.hpp @@ -23,7 +23,6 @@ #include "game/ant/phene/sculpturing.hpp" #include "game/ant/gene/polyphenic-gene.hpp" -namespace game { namespace ant { namespace gene { @@ -32,6 +31,5 @@ typedef polyphenic_gene sculpturing; } // namespace gene } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_GENE_SCULPTURING_HPP diff --git a/src/game/ant/gene/sting.hpp b/src/game/ant/gene/sting.hpp index bf9df58..f289337 100644 --- a/src/game/ant/gene/sting.hpp +++ b/src/game/ant/gene/sting.hpp @@ -23,7 +23,6 @@ #include "game/ant/phene/sting.hpp" #include "game/ant/gene/polyphenic-gene.hpp" -namespace game { namespace ant { namespace gene { @@ -32,6 +31,5 @@ typedef polyphenic_gene sting; } // namespace gene } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_GENE_STING_HPP diff --git a/src/game/ant/gene/waist.hpp b/src/game/ant/gene/waist.hpp index 3b04c34..e63be05 100644 --- a/src/game/ant/gene/waist.hpp +++ b/src/game/ant/gene/waist.hpp @@ -23,7 +23,6 @@ #include "game/ant/phene/waist.hpp" #include "game/ant/gene/polyphenic-gene.hpp" -namespace game { namespace ant { namespace gene { @@ -32,6 +31,5 @@ typedef polyphenic_gene waist; } // namespace gene } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_GENE_WAIST_HPP diff --git a/src/game/ant/gene/wings.hpp b/src/game/ant/gene/wings.hpp index 4f87c17..910bdfb 100644 --- a/src/game/ant/gene/wings.hpp +++ b/src/game/ant/gene/wings.hpp @@ -23,7 +23,6 @@ #include "game/ant/phene/wings.hpp" #include "game/ant/gene/polyphenic-gene.hpp" -namespace game { namespace ant { namespace gene { @@ -32,6 +31,5 @@ typedef polyphenic_gene wings; } // namespace gene } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_GENE_WINGS_HPP diff --git a/src/game/ant/genome.cpp b/src/game/ant/genome.cpp index 1dd421a..962a3bb 100644 --- a/src/game/ant/genome.cpp +++ b/src/game/ant/genome.cpp @@ -19,7 +19,6 @@ #include "game/ant/genome.hpp" -namespace game { namespace ant { genome::genome(): @@ -48,4 +47,3 @@ genome::genome(): {} } // namespace ant -} // namespace game diff --git a/src/game/ant/genome.hpp b/src/game/ant/genome.hpp index f1d4224..6733e1f 100644 --- a/src/game/ant/genome.hpp +++ b/src/game/ant/genome.hpp @@ -43,7 +43,6 @@ #include "game/ant/gene/waist.hpp" #include "game/ant/gene/wings.hpp" -namespace game { namespace ant { /** @@ -79,6 +78,5 @@ struct genome }; } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_GENOME_HPP diff --git a/src/game/ant/morphogenesis.cpp b/src/game/ant/morphogenesis.cpp index a1f9e8d..3e797b4 100644 --- a/src/game/ant/morphogenesis.cpp +++ b/src/game/ant/morphogenesis.cpp @@ -1,977 +1,975 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "game/ant/morphogenesis.hpp" -#include "render/material.hpp" -#include "render/vertex-attribute.hpp" -#include "math/transform-operators.hpp" -#include "math/quaternion.hpp" -#include - -namespace game { -namespace ant { - -static render::material* build_exoskeleton_material -( - const phene::pigmentation& pigmentation, - const phene::sculpturing& sculpturing -); -static void reskin_vertices -( - std::uint8_t* vertex_data, - std::size_t index_count, - const gl::vertex_attribute& position_attribute, - const gl::vertex_attribute& normal_attribute, - const gl::vertex_attribute& tangent_attribute, - const gl::vertex_attribute& bone_index_attribute, - const std::unordered_set& old_bone_indices, - std::uint8_t new_bone_index, - const math::transform& transform -); -static geom::aabb calculate_bounds(std::uint8_t* vertex_data, std::size_t index_count, const gl::vertex_attribute& position_attribute); -static render::model* build_model -( - render::material* material, - const render::model* antennae, - const render::model* eyes, - const render::model* forewings, - const render::model* gaster, - const render::model* head, - const render::model* hindwings, - const render::model* legs, - const render::model* mandibles, - const render::model* mesosoma, - const render::model* lateral_ocelli, - const render::model* median_ocellus, - const render::model* sting, - const render::model* waist -); - -render::material* build_exoskeleton_material -( - const phene::pigmentation& pigmentation, - const phene::sculpturing& sculpturing -) -{ - // Allocate copy of pigmentation material - render::material* exoskeleton_material = new render::material(*pigmentation.material); - - // Adjust roughness parameter - if (render::material_property_base* property = exoskeleton_material->get_property("roughness"); property != nullptr) - { - static_cast*>(property)->set_value(sculpturing.roughness); - } - else - { - exoskeleton_material->add_property("roughness")->set_value(sculpturing.roughness); - } - - // Adjust normal map parameter - if (render::material_property_base* property = exoskeleton_material->get_property("normal_map"); property != nullptr) - { - static_cast*>(property)->set_value(sculpturing.normal_map); - } - else - { - exoskeleton_material->add_property("normal_map")->set_value(sculpturing.normal_map); - } - - return exoskeleton_material; -} - -render::model* morphogenesis(const phenome& phenome) -{ - // Build exoskeleton material - render::material* exoskeleton_material = build_exoskeleton_material(*phenome.pigmentation, *phenome.sculpturing); - - // Determine presence of optional parts - bool eyes_present = phenome.eyes->present; - bool lateral_ocelli_present = phenome.ocelli->lateral_ocelli_present; - bool median_ocellus_present = phenome.ocelli->median_ocellus_present; - bool petiole_present = phenome.waist->petiole_present; - bool postpetiole_present = phenome.waist->postpetiole_present; - bool sting_present = phenome.sting->present; - bool wings_present = phenome.wings->present; - - // Get body part models - render::model* antennae_model = phenome.antennae->model; - render::model* eyes_model = phenome.eyes->model; - render::model* forewings_model = phenome.wings->forewings_model; - render::model* gaster_model = phenome.gaster->model; - render::model* head_model = phenome.head->model; - render::model* hindwings_model = phenome.wings->hindwings_model; - render::model* lateral_ocelli_model = phenome.ocelli->lateral_ocelli_model; - render::model* legs_model = phenome.legs->model; - render::model* mandibles_model = phenome.mandibles->model; - render::model* median_ocellus_model = phenome.ocelli->median_ocellus_model; - render::model* mesosoma_model = phenome.mesosoma->model; - render::model* sting_model = phenome.sting->model; - render::model* waist_model = phenome.waist->model; - - // Get body part vertex buffers - const gl::vertex_buffer* antennae_vbo = antennae_model->get_vertex_buffer(); - const gl::vertex_buffer* eyes_vbo = (eyes_present) ? eyes_model->get_vertex_buffer() : nullptr; - const gl::vertex_buffer* forewings_vbo = (wings_present) ? forewings_model->get_vertex_buffer() : nullptr; - const gl::vertex_buffer* gaster_vbo = gaster_model->get_vertex_buffer(); - const gl::vertex_buffer* head_vbo = head_model->get_vertex_buffer(); - const gl::vertex_buffer* hindwings_vbo = (wings_present) ? hindwings_model->get_vertex_buffer() : nullptr; - const gl::vertex_buffer* lateral_ocelli_vbo = (lateral_ocelli_model) ? lateral_ocelli_model->get_vertex_buffer() : nullptr; - const gl::vertex_buffer* legs_vbo = legs_model->get_vertex_buffer(); - const gl::vertex_buffer* mandibles_vbo = mandibles_model->get_vertex_buffer(); - const gl::vertex_buffer* median_ocellus_vbo = (median_ocellus_model) ? median_ocellus_model->get_vertex_buffer() : nullptr; - const gl::vertex_buffer* mesosoma_vbo = mesosoma_model->get_vertex_buffer(); - const gl::vertex_buffer* sting_vbo = (sting_present) ? sting_model->get_vertex_buffer() : nullptr; - const gl::vertex_buffer* waist_vbo = waist_model->get_vertex_buffer(); - - // Determine combined size of vertex buffers and save offsets - std::size_t vertex_buffer_size = 0; - std::size_t mesosoma_vbo_offset = vertex_buffer_size; - vertex_buffer_size += mesosoma_vbo->get_size(); - std::size_t legs_vbo_offset = vertex_buffer_size; - vertex_buffer_size += legs_vbo->get_size(); - std::size_t head_vbo_offset = vertex_buffer_size; - vertex_buffer_size += head_vbo->get_size(); - std::size_t mandibles_vbo_offset = vertex_buffer_size; - vertex_buffer_size += mandibles_vbo->get_size(); - std::size_t antennae_vbo_offset = vertex_buffer_size; - vertex_buffer_size += antennae_vbo->get_size(); - std::size_t waist_vbo_offset = vertex_buffer_size; - vertex_buffer_size += waist_vbo->get_size(); - std::size_t gaster_vbo_offset = vertex_buffer_size; - vertex_buffer_size += gaster_vbo->get_size(); - std::size_t sting_vbo_offset = vertex_buffer_size; - if (sting_present) - vertex_buffer_size += sting_vbo->get_size(); - std::size_t eyes_vbo_offset = vertex_buffer_size; - if (eyes_present) - vertex_buffer_size += eyes_vbo->get_size(); - std::size_t lateral_ocelli_vbo_offset = vertex_buffer_size; - if (lateral_ocelli_present) - vertex_buffer_size += lateral_ocelli_vbo->get_size(); - std::size_t median_ocellus_vbo_offset = vertex_buffer_size; - if (median_ocellus_present) - vertex_buffer_size += median_ocellus_vbo->get_size(); - std::size_t forewings_vbo_offset = vertex_buffer_size; - if (wings_present) - vertex_buffer_size += forewings_vbo->get_size(); - std::size_t hindwings_vbo_offset = vertex_buffer_size; - if (wings_present) - vertex_buffer_size += hindwings_vbo->get_size(); - - // Allocate combined vertex buffer data - std::uint8_t* vertex_buffer_data = new std::uint8_t[vertex_buffer_size]; - - // Read body part vertex buffer data into combined vertex buffer data - mesosoma_vbo->read(0, mesosoma_vbo->get_size(), vertex_buffer_data + mesosoma_vbo_offset); - legs_vbo->read(0, legs_vbo->get_size(), vertex_buffer_data + legs_vbo_offset); - head_vbo->read(0, head_vbo->get_size(), vertex_buffer_data + head_vbo_offset); - mandibles_vbo->read(0, mandibles_vbo->get_size(), vertex_buffer_data + mandibles_vbo_offset); - antennae_vbo->read(0, antennae_vbo->get_size(), vertex_buffer_data + antennae_vbo_offset); - waist_vbo->read(0, waist_vbo->get_size(), vertex_buffer_data + waist_vbo_offset); - gaster_vbo->read(0, gaster_vbo->get_size(), vertex_buffer_data + gaster_vbo_offset); - if (sting_present) - sting_vbo->read(0, sting_vbo->get_size(), vertex_buffer_data + sting_vbo_offset); - if (eyes_present) - eyes_vbo->read(0, eyes_vbo->get_size(), vertex_buffer_data + eyes_vbo_offset); - if (lateral_ocelli_present) - lateral_ocelli_vbo->read(0, lateral_ocelli_vbo->get_size(), vertex_buffer_data + lateral_ocelli_vbo_offset); - if (median_ocellus_present) - median_ocellus_vbo->read(0, median_ocellus_vbo->get_size(), vertex_buffer_data + median_ocellus_vbo_offset); - if (wings_present) - { - forewings_vbo->read(0, forewings_vbo->get_size(), vertex_buffer_data + forewings_vbo_offset); - hindwings_vbo->read(0, hindwings_vbo->get_size(), vertex_buffer_data + hindwings_vbo_offset); - } - - // Allocate model - render::model* model = new render::model(); - - // Setup model VAO - gl::vertex_array* model_vao = model->get_vertex_array(); - for (auto [location, attribute]: mesosoma_model->get_vertex_array()->get_attributes()) - { - attribute.buffer = model->get_vertex_buffer(); - model_vao->bind(location, attribute); - } - - // Get vertex attributes - const gl::vertex_attribute* position_attribute = nullptr; - const gl::vertex_attribute* normal_attribute = nullptr; - const gl::vertex_attribute* tangent_attribute = nullptr; - const gl::vertex_attribute* bone_index_attribute = nullptr; - const auto& vertex_attribute_map = model_vao->get_attributes(); - if (auto it = vertex_attribute_map.find(render::vertex_attribute::position); it != vertex_attribute_map.end()) - position_attribute = &it->second; - if (auto it = vertex_attribute_map.find(render::vertex_attribute::normal); it != vertex_attribute_map.end()) - normal_attribute = &it->second; - if (auto it = vertex_attribute_map.find(render::vertex_attribute::tangent); it != vertex_attribute_map.end()) - tangent_attribute = &it->second; - if (auto it = vertex_attribute_map.find(render::vertex_attribute::bone_index); it != vertex_attribute_map.end()) - bone_index_attribute = &it->second; - - // Get body part skeletons - const ::skeleton& mesosoma_skeleton = mesosoma_model->get_skeleton(); - const ::skeleton& legs_skeleton = legs_model->get_skeleton(); - const ::skeleton& head_skeleton = head_model->get_skeleton(); - const ::skeleton& mandibles_skeleton = mandibles_model->get_skeleton(); - const ::skeleton& antennae_skeleton = antennae_model->get_skeleton(); - const ::skeleton& waist_skeleton = waist_model->get_skeleton(); - const ::skeleton& gaster_skeleton = gaster_model->get_skeleton(); - const ::skeleton* sting_skeleton = (sting_present) ? &sting_model->get_skeleton() : nullptr; - const ::skeleton* eyes_skeleton = (eyes_present) ? &eyes_model->get_skeleton() : nullptr; - const ::skeleton* lateral_ocelli_skeleton = (lateral_ocelli_present) ? &lateral_ocelli_model->get_skeleton() : nullptr; - const ::skeleton* median_ocellus_skeleton = (median_ocellus_present) ? &median_ocellus_model->get_skeleton() : nullptr; - - // Allocate skeleton bones - ::skeleton& skeleton = model->get_skeleton(); - std::size_t bone_count = 33; - if (petiole_present) - bone_count += 1; - if (postpetiole_present) - bone_count += 1; - if (sting_present) - bone_count += 1; - if (wings_present) - bone_count += 4; - - // Assign bone indices - std::uint8_t bone_index = 0; - std::uint8_t mesosoma_bone_index = bone_index++; - std::uint8_t procoxa_l_bone_index = bone_index++; - std::uint8_t procoxa_r_bone_index = bone_index++; - std::uint8_t profemur_l_bone_index = bone_index++; - std::uint8_t profemur_r_bone_index = bone_index++; - std::uint8_t protibia_l_bone_index = bone_index++; - std::uint8_t protibia_r_bone_index = bone_index++; - std::uint8_t protarsus_l_bone_index = bone_index++; - std::uint8_t protarsus_r_bone_index = bone_index++; - std::uint8_t mesocoxa_l_bone_index = bone_index++; - std::uint8_t mesocoxa_r_bone_index = bone_index++; - std::uint8_t mesofemur_l_bone_index = bone_index++; - std::uint8_t mesofemur_r_bone_index = bone_index++; - std::uint8_t mesotibia_l_bone_index = bone_index++; - std::uint8_t mesotibia_r_bone_index = bone_index++; - std::uint8_t mesotarsus_l_bone_index = bone_index++; - std::uint8_t mesotarsus_r_bone_index = bone_index++; - std::uint8_t metacoxa_l_bone_index = bone_index++; - std::uint8_t metacoxa_r_bone_index = bone_index++; - std::uint8_t metafemur_l_bone_index = bone_index++; - std::uint8_t metafemur_r_bone_index = bone_index++; - std::uint8_t metatibia_l_bone_index = bone_index++; - std::uint8_t metatibia_r_bone_index = bone_index++; - std::uint8_t metatarsus_l_bone_index = bone_index++; - std::uint8_t metatarsus_r_bone_index = bone_index++; - std::uint8_t head_bone_index = bone_index++; - std::uint8_t mandible_l_bone_index = bone_index++; - std::uint8_t mandible_r_bone_index = bone_index++; - std::uint8_t antennomere1_l_bone_index = bone_index++; - std::uint8_t antennomere1_r_bone_index = bone_index++; - std::uint8_t antennomere2_l_bone_index = bone_index++; - std::uint8_t antennomere2_r_bone_index = bone_index++; - std::uint8_t petiole_bone_index =(petiole_present) ? bone_index++ : static_cast(bone_count); - std::uint8_t postpetiole_bone_index = (postpetiole_present) ? bone_index++ : static_cast(bone_count); - std::uint8_t gaster_bone_index = bone_index++; - std::uint8_t sting_bone_index = (sting_present) ? bone_index++ : static_cast(bone_count); - - // Construct bone identifiers - ::bone mesosoma_bone = make_bone(mesosoma_bone_index); - ::bone procoxa_l_bone = make_bone(procoxa_l_bone_index, mesosoma_bone_index); - ::bone procoxa_r_bone = make_bone(procoxa_r_bone_index, mesosoma_bone_index); - ::bone profemur_l_bone = make_bone(profemur_l_bone_index, procoxa_l_bone_index); - ::bone profemur_r_bone = make_bone(profemur_r_bone_index, procoxa_r_bone_index); - ::bone protibia_l_bone = make_bone(protibia_l_bone_index, profemur_l_bone_index); - ::bone protibia_r_bone = make_bone(protibia_r_bone_index, profemur_r_bone_index); - ::bone protarsus_l_bone = make_bone(protarsus_l_bone_index, protibia_l_bone_index); - ::bone protarsus_r_bone = make_bone(protarsus_r_bone_index, protibia_r_bone_index); - ::bone mesocoxa_l_bone = make_bone(mesocoxa_l_bone_index, mesosoma_bone_index); - ::bone mesocoxa_r_bone = make_bone(mesocoxa_r_bone_index, mesosoma_bone_index); - ::bone mesofemur_l_bone = make_bone(mesofemur_l_bone_index, mesocoxa_l_bone_index); - ::bone mesofemur_r_bone = make_bone(mesofemur_r_bone_index, mesocoxa_r_bone_index); - ::bone mesotibia_l_bone = make_bone(mesotibia_l_bone_index, mesofemur_l_bone_index); - ::bone mesotibia_r_bone = make_bone(mesotibia_r_bone_index, mesofemur_r_bone_index); - ::bone mesotarsus_l_bone = make_bone(mesotarsus_l_bone_index, mesotibia_l_bone_index); - ::bone mesotarsus_r_bone = make_bone(mesotarsus_r_bone_index, mesotibia_r_bone_index); - ::bone metacoxa_l_bone = make_bone(metacoxa_l_bone_index, mesosoma_bone_index); - ::bone metacoxa_r_bone = make_bone(metacoxa_r_bone_index, mesosoma_bone_index); - ::bone metafemur_l_bone = make_bone(metafemur_l_bone_index, metacoxa_l_bone_index); - ::bone metafemur_r_bone = make_bone(metafemur_r_bone_index, metacoxa_r_bone_index); - ::bone metatibia_l_bone = make_bone(metatibia_l_bone_index, metafemur_l_bone_index); - ::bone metatibia_r_bone = make_bone(metatibia_r_bone_index, metafemur_r_bone_index); - ::bone metatarsus_l_bone = make_bone(metatarsus_l_bone_index, metatibia_l_bone_index); - ::bone metatarsus_r_bone = make_bone(metatarsus_r_bone_index, metatibia_r_bone_index); - ::bone head_bone = make_bone(head_bone_index, mesosoma_bone_index); - ::bone mandible_l_bone = make_bone(mandible_l_bone_index, head_bone_index); - ::bone mandible_r_bone = make_bone(mandible_r_bone_index, head_bone_index); - ::bone antennomere1_l_bone = make_bone(antennomere1_l_bone_index, head_bone_index); - ::bone antennomere1_r_bone = make_bone(antennomere1_r_bone_index, head_bone_index); - ::bone antennomere2_l_bone = make_bone(antennomere2_l_bone_index, antennomere1_l_bone_index); - ::bone antennomere2_r_bone = make_bone(antennomere2_r_bone_index, antennomere1_r_bone_index); - ::bone petiole_bone = make_bone(petiole_bone_index, mesosoma_bone_index); - ::bone postpetiole_bone = make_bone(postpetiole_bone_index, petiole_bone_index); - ::bone gaster_bone = make_bone(gaster_bone_index, (postpetiole_present) ? postpetiole_bone_index : petiole_bone_index); - ::bone sting_bone = make_bone(sting_bone_index, gaster_bone_index); - - // Map bone names to bones - skeleton.bone_map["mesosoma"] = mesosoma_bone; - skeleton.bone_map["procoxa_l"] = procoxa_l_bone; - skeleton.bone_map["procoxa_r"] = procoxa_r_bone; - skeleton.bone_map["profemur_l"] = profemur_l_bone; - skeleton.bone_map["profemur_r"] = profemur_r_bone; - skeleton.bone_map["protibia_l"] = protibia_l_bone; - skeleton.bone_map["protibia_r"] = protibia_r_bone; - skeleton.bone_map["protarsus_l"] = protarsus_l_bone; - skeleton.bone_map["protarsus_r"] = protarsus_r_bone; - skeleton.bone_map["mesocoxa_l"] = mesocoxa_l_bone; - skeleton.bone_map["mesocoxa_r"] = mesocoxa_r_bone; - skeleton.bone_map["mesofemur_l"] = mesofemur_l_bone; - skeleton.bone_map["mesofemur_r"] = mesofemur_r_bone; - skeleton.bone_map["mesotibia_l"] = mesotibia_l_bone; - skeleton.bone_map["mesotibia_r"] = mesotibia_r_bone; - skeleton.bone_map["mesotarsus_l"] = mesotarsus_l_bone; - skeleton.bone_map["mesotarsus_r"] = mesotarsus_r_bone; - skeleton.bone_map["metacoxa_l"] = metacoxa_l_bone; - skeleton.bone_map["metacoxa_r"] = metacoxa_r_bone; - skeleton.bone_map["metafemur_l"] = metafemur_l_bone; - skeleton.bone_map["metafemur_r"] = metafemur_r_bone; - skeleton.bone_map["metatibia_l"] = metatibia_l_bone; - skeleton.bone_map["metatibia_r"] = metatibia_r_bone; - skeleton.bone_map["metatarsus_l"] = metatarsus_l_bone; - skeleton.bone_map["metatarsus_r"] = metatarsus_r_bone; - skeleton.bone_map["head"] = head_bone; - skeleton.bone_map["mandible_l"] = mandible_l_bone; - skeleton.bone_map["mandible_r"] = mandible_r_bone; - skeleton.bone_map["antennomere1_l"] = antennomere1_l_bone; - skeleton.bone_map["antennomere1_r"] = antennomere1_r_bone; - skeleton.bone_map["antennomere2_l"] = antennomere2_l_bone; - skeleton.bone_map["antennomere2_r"] = antennomere2_r_bone; - if (petiole_present) - skeleton.bone_map["petiole"] = petiole_bone; - if (postpetiole_present) - skeleton.bone_map["postpetiole"] = postpetiole_bone; - skeleton.bone_map["gaster"] = gaster_bone; - if (sting_present) - skeleton.bone_map["sting"] = sting_bone; - - // Get reference to skeleton bind pose - pose& bind_pose = skeleton.bind_pose; - - // Skeleton mesosoma pose - if (auto it = mesosoma_skeleton.bone_map.find("mesosoma"); it != mesosoma_skeleton.bone_map.end()) - bind_pose[mesosoma_bone] = mesosoma_skeleton.bind_pose.at(it->second); - - // Skeleton forelegs pose - if (auto it = legs_skeleton.bone_map.find("procoxa_l"); it != legs_skeleton.bone_map.end()) - bind_pose[procoxa_l_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("procoxa_r"); it != legs_skeleton.bone_map.end()) - bind_pose[procoxa_r_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("profemur_l"); it != legs_skeleton.bone_map.end()) - bind_pose[profemur_l_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("profemur_r"); it != legs_skeleton.bone_map.end()) - bind_pose[profemur_r_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("protibia_l"); it != legs_skeleton.bone_map.end()) - bind_pose[protibia_l_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("protibia_r"); it != legs_skeleton.bone_map.end()) - bind_pose[protibia_r_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("protarsus1_l"); it != legs_skeleton.bone_map.end()) - bind_pose[protarsus_l_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("protarsus1_r"); it != legs_skeleton.bone_map.end()) - bind_pose[protarsus_r_bone] = legs_skeleton.bind_pose.at(it->second); - - // Skeleton midlegs pose - if (auto it = legs_skeleton.bone_map.find("mesocoxa_l"); it != legs_skeleton.bone_map.end()) - bind_pose[mesocoxa_l_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("mesocoxa_r"); it != legs_skeleton.bone_map.end()) - bind_pose[mesocoxa_r_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("mesofemur_l"); it != legs_skeleton.bone_map.end()) - bind_pose[mesofemur_l_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("mesofemur_r"); it != legs_skeleton.bone_map.end()) - bind_pose[mesofemur_r_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("mesotibia_l"); it != legs_skeleton.bone_map.end()) - bind_pose[mesotibia_l_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("mesotibia_r"); it != legs_skeleton.bone_map.end()) - bind_pose[mesotibia_r_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("mesotarsus1_l"); it != legs_skeleton.bone_map.end()) - bind_pose[mesotarsus_l_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("mesotarsus1_r"); it != legs_skeleton.bone_map.end()) - bind_pose[mesotarsus_r_bone] = legs_skeleton.bind_pose.at(it->second); - - // Skeleton hindlegs pose - if (auto it = legs_skeleton.bone_map.find("metacoxa_l"); it != legs_skeleton.bone_map.end()) - bind_pose[metacoxa_l_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("metacoxa_r"); it != legs_skeleton.bone_map.end()) - bind_pose[metacoxa_r_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("metafemur_l"); it != legs_skeleton.bone_map.end()) - bind_pose[metafemur_l_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("metafemur_r"); it != legs_skeleton.bone_map.end()) - bind_pose[metafemur_r_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("metatibia_l"); it != legs_skeleton.bone_map.end()) - bind_pose[metatibia_l_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("metatibia_r"); it != legs_skeleton.bone_map.end()) - bind_pose[metatibia_r_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("metatarsus1_l"); it != legs_skeleton.bone_map.end()) - bind_pose[metatarsus_l_bone] = legs_skeleton.bind_pose.at(it->second); - if (auto it = legs_skeleton.bone_map.find("metatarsus1_r"); it != legs_skeleton.bone_map.end()) - bind_pose[metatarsus_r_bone] = legs_skeleton.bind_pose.at(it->second); - - // Skeleton head pose - bind_pose[head_bone] = mesosoma_skeleton.bind_pose.at(mesosoma_skeleton.bone_map.at("head")) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("head")); - - // Skeleton mandibles pose - bind_pose[mandible_l_bone] = head_skeleton.bind_pose.at(head_skeleton.bone_map.at("mandible_l")) * mandibles_skeleton.bind_pose.at(mandibles_skeleton.bone_map.at("mandible_l")); - bind_pose[mandible_r_bone] = head_skeleton.bind_pose.at(head_skeleton.bone_map.at("mandible_r")) * mandibles_skeleton.bind_pose.at(mandibles_skeleton.bone_map.at("mandible_r")); - - // Skeleton antennae pose - bind_pose[antennomere1_l_bone] = head_skeleton.bind_pose.at(head_skeleton.bone_map.at("antenna_l")) * antennae_skeleton.bind_pose.at(antennae_skeleton.bone_map.at("antennomere1_l")); - bind_pose[antennomere1_r_bone] = head_skeleton.bind_pose.at(head_skeleton.bone_map.at("antenna_r")) * antennae_skeleton.bind_pose.at(antennae_skeleton.bone_map.at("antennomere1_r")); - bind_pose[antennomere2_l_bone] = antennae_skeleton.bind_pose.at(antennae_skeleton.bone_map.at("antennomere2_l")); - bind_pose[antennomere2_r_bone] = antennae_skeleton.bind_pose.at(antennae_skeleton.bone_map.at("antennomere2_r")); - - // Skeleton waist pose - if (petiole_present) - { - bind_pose[petiole_bone] = mesosoma_skeleton.bind_pose.at(mesosoma_skeleton.bone_map.at("petiole")) * waist_skeleton.bind_pose.at(waist_skeleton.bone_map.at("petiole")); - } - if (postpetiole_present) - { - bind_pose[postpetiole_bone] = waist_skeleton.bind_pose.at(waist_skeleton.bone_map.at("postpetiole")); - } - - // Skeleton gaster pose - if (postpetiole_present) - { - bind_pose[gaster_bone] = waist_skeleton.bind_pose.at(waist_skeleton.bone_map.at("postpetiole")) * gaster_skeleton.bind_pose.at(gaster_skeleton.bone_map.at("gaster")); - } - else if (petiole_present) - { - bind_pose[gaster_bone] = waist_skeleton.bind_pose.at(waist_skeleton.bone_map.at("petiole")) * gaster_skeleton.bind_pose.at(gaster_skeleton.bone_map.at("gaster")); - } - else - { - bind_pose[gaster_bone] = mesosoma_skeleton.bind_pose.at(mesosoma_skeleton.bone_map.at("petiole")) * gaster_skeleton.bind_pose.at(gaster_skeleton.bone_map.at("gaster")); - } - - // Skeleton sting pose - if (sting_present) - { - bind_pose[sting_bone] = gaster_skeleton.bind_pose.at(gaster_skeleton.bone_map.at("sting")) * sting_skeleton->bind_pose.at(sting_skeleton->bone_map.at("sting")); - } - - // Calculate the skeleton-space bind pose - pose bind_pose_ss; - ::concatenate(bind_pose, bind_pose_ss); - - // Calculate inverse skeleton-space bind pose - ::inverse(bind_pose_ss, skeleton.inverse_bind_pose); - - // Get number of vertex indices for each body part - std::size_t mesosoma_index_count = (*mesosoma_model->get_groups())[0]->get_index_count(); - std::size_t legs_index_count = (*legs_model->get_groups())[0]->get_index_count(); - std::size_t head_index_count = (*head_model->get_groups())[0]->get_index_count(); - std::size_t mandibles_index_count = (*mandibles_model->get_groups())[0]->get_index_count(); - std::size_t antennae_index_count = (*antennae_model->get_groups())[0]->get_index_count(); - std::size_t waist_index_count = (*waist_model->get_groups())[0]->get_index_count(); - std::size_t gaster_index_count = (*gaster_model->get_groups())[0]->get_index_count(); - std::size_t sting_index_count = (sting_present) ? (*sting_model->get_groups())[0]->get_index_count() : 0; - std::size_t eyes_index_count = (eyes_present) ? (*eyes_model->get_groups())[0]->get_index_count() : 0; - std::size_t lateral_ocelli_index_count = (lateral_ocelli_present) ? (*lateral_ocelli_model->get_groups())[0]->get_index_count() : 0; - std::size_t median_ocellus_index_count = (median_ocellus_present) ? (*median_ocellus_model->get_groups())[0]->get_index_count() : 0; - std::size_t forewings_index_count = (wings_present) ? (*forewings_model->get_groups())[0]->get_index_count() : 0; - std::size_t hindwings_index_count = (wings_present) ? (*hindwings_model->get_groups())[0]->get_index_count() : 0; - std::size_t exoskeleton_index_count = - mesosoma_index_count - + legs_index_count - + head_index_count - + mandibles_index_count - + antennae_index_count - + waist_index_count - + gaster_index_count - + sting_index_count; - - // Calculate transform from legs space to body space - const math::transform& legs_to_body = math::transform::identity; - - // Reskin leg bones - std::unordered_set old_procoxa_l_indices; - if (auto it = legs_skeleton.bone_map.find("procoxa_l"); it != legs_skeleton.bone_map.end()) - old_procoxa_l_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_procoxa_l_indices, procoxa_l_bone_index, legs_to_body); - std::unordered_set old_profemur_l_indices; - if (auto it = legs_skeleton.bone_map.find("profemur_l"); it != legs_skeleton.bone_map.end()) - old_profemur_l_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_profemur_l_indices, profemur_l_bone_index, legs_to_body); - std::unordered_set old_protibia_l_indices; - if (auto it = legs_skeleton.bone_map.find("protibia_l"); it != legs_skeleton.bone_map.end()) - old_protibia_l_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_protibia_l_indices, protibia_l_bone_index, legs_to_body); - std::unordered_set old_protarsus_l_indices; - if (auto it = legs_skeleton.bone_map.find("protarsus1_l"); it != legs_skeleton.bone_map.end()) - old_protarsus_l_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("protarsus2_l"); it != legs_skeleton.bone_map.end()) - old_protarsus_l_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("protarsus3_l"); it != legs_skeleton.bone_map.end()) - old_protarsus_l_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("protarsus4_l"); it != legs_skeleton.bone_map.end()) - old_protarsus_l_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("protarsus5_l"); it != legs_skeleton.bone_map.end()) - old_protarsus_l_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_protarsus_l_indices, protarsus_l_bone_index, legs_to_body); - - std::unordered_set old_procoxa_r_indices; - if (auto it = legs_skeleton.bone_map.find("procoxa_r"); it != legs_skeleton.bone_map.end()) - old_procoxa_r_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_procoxa_r_indices, procoxa_r_bone_index, legs_to_body); - std::unordered_set old_profemur_r_indices; - if (auto it = legs_skeleton.bone_map.find("profemur_r"); it != legs_skeleton.bone_map.end()) - old_profemur_r_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_profemur_r_indices, profemur_r_bone_index, legs_to_body); - std::unordered_set old_protibia_r_indices; - if (auto it = legs_skeleton.bone_map.find("protibia_r"); it != legs_skeleton.bone_map.end()) - old_protibia_r_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_protibia_r_indices, protibia_r_bone_index, legs_to_body); - std::unordered_set old_protarsus_r_indices; - if (auto it = legs_skeleton.bone_map.find("protarsus1_r"); it != legs_skeleton.bone_map.end()) - old_protarsus_r_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("protarsus2_r"); it != legs_skeleton.bone_map.end()) - old_protarsus_r_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("protarsus3_r"); it != legs_skeleton.bone_map.end()) - old_protarsus_r_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("protarsus4_r"); it != legs_skeleton.bone_map.end()) - old_protarsus_r_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("protarsus5_r"); it != legs_skeleton.bone_map.end()) - old_protarsus_r_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_protarsus_r_indices, protarsus_r_bone_index, legs_to_body); - - std::unordered_set old_mesocoxa_l_indices; - if (auto it = legs_skeleton.bone_map.find("mesocoxa_l"); it != legs_skeleton.bone_map.end()) - old_mesocoxa_l_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_mesocoxa_l_indices, mesocoxa_l_bone_index, legs_to_body); - std::unordered_set old_mesofemur_l_indices; - if (auto it = legs_skeleton.bone_map.find("mesofemur_l"); it != legs_skeleton.bone_map.end()) - old_mesofemur_l_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_mesofemur_l_indices, mesofemur_l_bone_index, legs_to_body); - std::unordered_set old_mesotibia_l_indices; - if (auto it = legs_skeleton.bone_map.find("mesotibia_l"); it != legs_skeleton.bone_map.end()) - old_mesotibia_l_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_mesotibia_l_indices, mesotibia_l_bone_index, legs_to_body); - std::unordered_set old_mesotarsus_l_indices; - if (auto it = legs_skeleton.bone_map.find("mesotarsus1_l"); it != legs_skeleton.bone_map.end()) - old_mesotarsus_l_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("mesotarsus2_l"); it != legs_skeleton.bone_map.end()) - old_mesotarsus_l_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("mesotarsus3_l"); it != legs_skeleton.bone_map.end()) - old_mesotarsus_l_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("mesotarsus4_l"); it != legs_skeleton.bone_map.end()) - old_mesotarsus_l_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("mesotarsus5_l"); it != legs_skeleton.bone_map.end()) - old_mesotarsus_l_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_mesotarsus_l_indices, mesotarsus_l_bone_index, legs_to_body); - - std::unordered_set old_mesocoxa_r_indices; - if (auto it = legs_skeleton.bone_map.find("mesocoxa_r"); it != legs_skeleton.bone_map.end()) - old_mesocoxa_r_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_mesocoxa_r_indices, mesocoxa_r_bone_index, legs_to_body); - std::unordered_set old_mesofemur_r_indices; - if (auto it = legs_skeleton.bone_map.find("mesofemur_r"); it != legs_skeleton.bone_map.end()) - old_mesofemur_r_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_mesofemur_r_indices, mesofemur_r_bone_index, legs_to_body); - std::unordered_set old_mesotibia_r_indices; - if (auto it = legs_skeleton.bone_map.find("mesotibia_r"); it != legs_skeleton.bone_map.end()) - old_mesotibia_r_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_mesotibia_r_indices, mesotibia_r_bone_index, legs_to_body); - std::unordered_set old_mesotarsus_r_indices; - if (auto it = legs_skeleton.bone_map.find("mesotarsus1_r"); it != legs_skeleton.bone_map.end()) - old_mesotarsus_r_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("mesotarsus2_r"); it != legs_skeleton.bone_map.end()) - old_mesotarsus_r_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("mesotarsus3_r"); it != legs_skeleton.bone_map.end()) - old_mesotarsus_r_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("mesotarsus4_r"); it != legs_skeleton.bone_map.end()) - old_mesotarsus_r_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("mesotarsus5_r"); it != legs_skeleton.bone_map.end()) - old_mesotarsus_r_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_mesotarsus_r_indices, mesotarsus_r_bone_index, legs_to_body); - - std::unordered_set old_metacoxa_l_indices; - if (auto it = legs_skeleton.bone_map.find("metacoxa_l"); it != legs_skeleton.bone_map.end()) - old_metacoxa_l_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_metacoxa_l_indices, metacoxa_l_bone_index, legs_to_body); - std::unordered_set old_metafemur_l_indices; - if (auto it = legs_skeleton.bone_map.find("metafemur_l"); it != legs_skeleton.bone_map.end()) - old_metafemur_l_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_metafemur_l_indices, metafemur_l_bone_index, legs_to_body); - std::unordered_set old_metatibia_l_indices; - if (auto it = legs_skeleton.bone_map.find("metatibia_l"); it != legs_skeleton.bone_map.end()) - old_metatibia_l_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_metatibia_l_indices, metatibia_l_bone_index, legs_to_body); - std::unordered_set old_metatarsus_l_indices; - if (auto it = legs_skeleton.bone_map.find("metatarsus1_l"); it != legs_skeleton.bone_map.end()) - old_metatarsus_l_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("metatarsus2_l"); it != legs_skeleton.bone_map.end()) - old_metatarsus_l_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("metatarsus3_l"); it != legs_skeleton.bone_map.end()) - old_metatarsus_l_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("metatarsus4_l"); it != legs_skeleton.bone_map.end()) - old_metatarsus_l_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("metatarsus5_l"); it != legs_skeleton.bone_map.end()) - old_metatarsus_l_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_metatarsus_l_indices, metatarsus_l_bone_index, legs_to_body); - - std::unordered_set old_metacoxa_r_indices; - if (auto it = legs_skeleton.bone_map.find("metacoxa_r"); it != legs_skeleton.bone_map.end()) - old_metacoxa_r_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_metacoxa_r_indices, metacoxa_r_bone_index, legs_to_body); - std::unordered_set old_metafemur_r_indices; - if (auto it = legs_skeleton.bone_map.find("metafemur_r"); it != legs_skeleton.bone_map.end()) - old_metafemur_r_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_metafemur_r_indices, metafemur_r_bone_index, legs_to_body); - std::unordered_set old_metatibia_r_indices; - if (auto it = legs_skeleton.bone_map.find("metatibia_r"); it != legs_skeleton.bone_map.end()) - old_metatibia_r_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_metatibia_r_indices, metatibia_r_bone_index, legs_to_body); - std::unordered_set old_metatarsus_r_indices; - if (auto it = legs_skeleton.bone_map.find("metatarsus1_r"); it != legs_skeleton.bone_map.end()) - old_metatarsus_r_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("metatarsus2_r"); it != legs_skeleton.bone_map.end()) - old_metatarsus_r_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("metatarsus3_r"); it != legs_skeleton.bone_map.end()) - old_metatarsus_r_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("metatarsus4_r"); it != legs_skeleton.bone_map.end()) - old_metatarsus_r_indices.emplace(it->second); - if (auto it = legs_skeleton.bone_map.find("metatarsus5_r"); it != legs_skeleton.bone_map.end()) - old_metatarsus_r_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_metatarsus_r_indices, metatarsus_r_bone_index, legs_to_body); - - // Calculate transform from head space to body space - math::transform head_to_body = bind_pose_ss.at(mesosoma_bone) * mesosoma_skeleton.bind_pose.at(mesosoma_skeleton.bone_map.at("head")); - - // Reskin head bone - std::unordered_set old_head_bone_indices; - if (auto it = head_skeleton.bone_map.find("head"); it != head_skeleton.bone_map.end()) - old_head_bone_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + head_vbo_offset, head_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_head_bone_indices, head_bone_index, head_to_body); - - // Calculate transforms from mandibles space to body space - math::transform mandible_l_to_body = bind_pose_ss.at(head_bone) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("mandible_l")); - math::transform mandible_r_to_body = bind_pose_ss.at(head_bone) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("mandible_r")); - - // Reskin mandible bones - std::unordered_set old_head_mandible_l_bone_indices; - if (auto it = mandibles_skeleton.bone_map.find("mandible_l"); it != mandibles_skeleton.bone_map.end()) - old_head_mandible_l_bone_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + mandibles_vbo_offset, mandibles_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_head_mandible_l_bone_indices, mandible_l_bone_index, mandible_l_to_body); - std::unordered_set old_head_mandible_r_bone_indices; - if (auto it = mandibles_skeleton.bone_map.find("mandible_r"); it != mandibles_skeleton.bone_map.end()) - old_head_mandible_r_bone_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + mandibles_vbo_offset, mandibles_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_head_mandible_r_bone_indices, mandible_r_bone_index, mandible_r_to_body); - - // Calculate transforms from antennae space to body space - math::transform antenna_l_to_body = bind_pose_ss.at(head_bone) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("antenna_l")); - math::transform antenna_r_to_body = bind_pose_ss.at(head_bone) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("antenna_r")); - - // Reskin antennomere1 bones - std::unordered_set old_antennomere1_l_indices; - if (auto it = antennae_skeleton.bone_map.find("antennomere1_l"); it != antennae_skeleton.bone_map.end()) - old_antennomere1_l_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + antennae_vbo_offset, antennae_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_antennomere1_l_indices, antennomere1_l_bone_index, antenna_l_to_body); - std::unordered_set old_antennomere1_r_indices; - if (auto it = antennae_skeleton.bone_map.find("antennomere1_r"); it != antennae_skeleton.bone_map.end()) - old_antennomere1_r_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + antennae_vbo_offset, antennae_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_antennomere1_r_indices, antennomere1_r_bone_index, antenna_r_to_body); - - // Reskin antennomere2+ bones - const std::vector antennomere_bone_names = - { - "antennomere2", - "antennomere3", - "antennomere4", - "antennomere5", - "antennomere6", - "antennomere7", - "antennomere8", - "antennomere9", - "antennomere10", - "antennomere11", - "antennomere12", - "antennomere13" - }; - std::unordered_set old_antennomere_l_indices; - for (const std::string& bone_name: antennomere_bone_names) - if (auto it = antennae_skeleton.bone_map.find(bone_name + "_l"); it != antennae_skeleton.bone_map.end()) - old_antennomere_l_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + antennae_vbo_offset, antennae_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_antennomere_l_indices, antennomere2_l_bone_index, antenna_l_to_body); - std::unordered_set old_antennomere_r_indices; - for (const std::string& bone_name: antennomere_bone_names) - if (auto it = antennae_skeleton.bone_map.find(bone_name + "_r"); it != antennae_skeleton.bone_map.end()) - old_antennomere_r_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + antennae_vbo_offset, antennae_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_antennomere_r_indices, antennomere2_r_bone_index, antenna_r_to_body); - - // Calculate transform from waist space to body space - math::transform waist_to_body = bind_pose_ss.at(mesosoma_bone) * mesosoma_skeleton.bind_pose.at(mesosoma_skeleton.bone_map.at("petiole")); - - // Reskin waist bones - std::unordered_set old_petiole_bone_indices; - if (auto it = waist_skeleton.bone_map.find("petiole"); it != waist_skeleton.bone_map.end()) - old_petiole_bone_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + waist_vbo_offset, waist_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_petiole_bone_indices, petiole_bone_index, waist_to_body); - if (postpetiole_present) - { - std::unordered_set old_postpetiole_bone_indices; - if (auto it = waist_skeleton.bone_map.find("postpetiole"); it != waist_skeleton.bone_map.end()) - old_postpetiole_bone_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + waist_vbo_offset, waist_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_postpetiole_bone_indices, postpetiole_bone_index, waist_to_body); - } - - // Calculate transform from gaster space to body space - math::transform gaster_to_body = bind_pose_ss.at(bone_parent_index(gaster_bone)) * waist_skeleton.bind_pose.at(waist_skeleton.bone_map.at("gaster")); - - // Reskin gaster bones - std::unordered_set old_gaster_bone_indices; - if (auto it = gaster_skeleton.bone_map.find("gaster"); it != gaster_skeleton.bone_map.end()) - old_gaster_bone_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + gaster_vbo_offset, gaster_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_gaster_bone_indices, gaster_bone_index, gaster_to_body); - - if (sting_present) - { - // Calculate transform from sting space to body space - math::transform sting_to_body = gaster_to_body * gaster_skeleton.bind_pose.at(gaster_skeleton.bone_map.at("sting")); - - // Reskin sting bones - std::unordered_set old_sting_bone_indices; - if (auto it = sting_skeleton->bone_map.find("sting"); it != sting_skeleton->bone_map.end()) - old_sting_bone_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + sting_vbo_offset, sting_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_sting_bone_indices, sting_bone_index, sting_to_body); - } - - if (eyes_present) - { - // Calculate transforms from eyes space to body space - math::transform eye_l_to_body = bind_pose_ss.at(head_bone) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("eye_l")); - math::transform eye_r_to_body = bind_pose_ss.at(head_bone) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("eye_r")); - - // Reskin eye bones - std::unordered_set old_eye_l_bone_indices; - if (auto it = eyes_skeleton->bone_map.find("eye_l"); it != eyes_skeleton->bone_map.end()) - old_eye_l_bone_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + eyes_vbo_offset, eyes_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_eye_l_bone_indices, head_bone_index, eye_l_to_body); - std::unordered_set old_eye_r_bone_indices; - if (auto it = eyes_skeleton->bone_map.find("eye_r"); it != eyes_skeleton->bone_map.end()) - old_eye_r_bone_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + eyes_vbo_offset, eyes_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_eye_r_bone_indices, head_bone_index, eye_r_to_body); - } - - if (lateral_ocelli_present) - { - // Calculate transforms from lateral ocelli space to body space - math::transform ocellus_l_to_body = bind_pose_ss.at(head_bone) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("ocellus_l")); - math::transform ocellus_r_to_body = bind_pose_ss.at(head_bone) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("ocellus_r")); - - // Reskin lateral ocelli bones - std::unordered_set old_ocellus_l_bone_indices; - if (auto it = lateral_ocelli_skeleton->bone_map.find("ocellus_l"); it != lateral_ocelli_skeleton->bone_map.end()) - old_ocellus_l_bone_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + lateral_ocelli_vbo_offset, lateral_ocelli_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_ocellus_l_bone_indices, head_bone_index, ocellus_l_to_body); - std::unordered_set old_ocellus_r_bone_indices; - if (auto it = lateral_ocelli_skeleton->bone_map.find("ocellus_r"); it != lateral_ocelli_skeleton->bone_map.end()) - old_ocellus_r_bone_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + lateral_ocelli_vbo_offset, lateral_ocelli_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_ocellus_r_bone_indices, head_bone_index, ocellus_r_to_body); - } - - if (median_ocellus_present) - { - // Calculate transforms from lateral ocelli space to body space - math::transform ocellus_m_to_body = bind_pose_ss.at(head_bone) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("ocellus_m")); - - // Reskin lateral ocelli bones - std::unordered_set old_ocellus_m_bone_indices; - if (auto it = median_ocellus_skeleton->bone_map.find("ocellus_m"); it != median_ocellus_skeleton->bone_map.end()) - old_ocellus_m_bone_indices.emplace(it->second); - reskin_vertices(vertex_buffer_data + median_ocellus_vbo_offset, median_ocellus_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_ocellus_m_bone_indices, head_bone_index, ocellus_m_to_body); - } - - // Upload vertex buffer data to model VBO - model->get_vertex_buffer()->repurpose(gl::buffer_usage::static_draw, vertex_buffer_size, vertex_buffer_data); - - // Construct exoskeleton model group - render::model_group* exoskeleton_group = model->add_group("exoskeleton"); - exoskeleton_group->set_material(exoskeleton_material); - exoskeleton_group->set_drawing_mode(gl::drawing_mode::triangles); - exoskeleton_group->set_start_index(0); - exoskeleton_group->set_index_count(exoskeleton_index_count); - - std::size_t index_offset = exoskeleton_index_count; - if (eyes_present) - { - // Construct eyes model group - render::model_group* eyes_group = model->add_group("eyes"); - eyes_group->set_material((*eyes_model->get_groups())[0]->get_material()); - eyes_group->set_drawing_mode(gl::drawing_mode::triangles); - eyes_group->set_start_index(index_offset); - eyes_group->set_index_count(eyes_index_count); - index_offset += eyes_index_count; - } - - if (lateral_ocelli_present || median_ocellus_present) - { - // Construct ocelli model group - render::model_group* ocelli_group = model->add_group("ocelli"); - ocelli_group->set_drawing_mode(gl::drawing_mode::triangles); - ocelli_group->set_start_index(index_offset); - - std::size_t index_count = 0; - if (lateral_ocelli_present) - { - index_count += lateral_ocelli_index_count; - index_offset += lateral_ocelli_index_count; - ocelli_group->set_material((*lateral_ocelli_model->get_groups())[0]->get_material()); - } - if (median_ocellus_present) - { - index_count += median_ocellus_index_count; - index_offset += median_ocellus_index_count; - if (!lateral_ocelli_present) - ocelli_group->set_material((*median_ocellus_model->get_groups())[0]->get_material()); - } - - ocelli_group->set_index_count(index_count); - } - - if (wings_present) - { - // Construct forewings model group - render::model_group* forewings_group = model->add_group("forewings"); - forewings_group->set_material((*forewings_model->get_groups())[0]->get_material()); - forewings_group->set_drawing_mode(gl::drawing_mode::triangles); - forewings_group->set_start_index(index_offset); - forewings_group->set_index_count(forewings_index_count); - index_offset += forewings_index_count; - - // Construct hindwings model group - render::model_group* hindwings_group = model->add_group("hindwings"); - hindwings_group->set_material((*hindwings_model->get_groups())[0]->get_material()); - hindwings_group->set_drawing_mode(gl::drawing_mode::triangles); - hindwings_group->set_start_index(index_offset); - hindwings_group->set_index_count(hindwings_index_count); - index_offset += hindwings_index_count; - } - - // Calculate model bounding box - geom::aabb bounds = calculate_bounds(vertex_buffer_data, index_offset, *position_attribute); - model->set_bounds(bounds); - - // Free vertex buffer data - delete[] vertex_buffer_data; - - return model; -} - -void reskin_vertices -( - std::uint8_t* vertex_data, - std::size_t index_count, - const gl::vertex_attribute& position_attribute, - const gl::vertex_attribute& normal_attribute, - const gl::vertex_attribute& tangent_attribute, - const gl::vertex_attribute& bone_index_attribute, - const std::unordered_set& old_bone_indices, - std::uint8_t new_bone_index, - const math::transform& transform -) -{ - std::uint8_t* position_data = vertex_data + position_attribute.offset; - std::uint8_t* normal_data = vertex_data + normal_attribute.offset; - std::uint8_t* tangent_data = vertex_data + tangent_attribute.offset; - std::uint8_t* bone_index_data = vertex_data + bone_index_attribute.offset; - - for (std::size_t i = 0; i < index_count; ++i) - { - // Get bone index of current vertex - float* bone_index = reinterpret_cast(bone_index_data + bone_index_attribute.stride * i); - - // Skip irrelevant bones - if (!old_bone_indices.count(static_cast(*bone_index + 0.5f))) - continue; - - // Get vertex position - float* x = reinterpret_cast(position_data + position_attribute.stride * i); - float* y = x + 1; - float* z = y + 1; - - // Get vertex normal - float* nx = reinterpret_cast(normal_data + normal_attribute.stride * i); - float* ny = nx + 1; - float* nz = ny + 1; - - // Get vertex tangent - float* tx = reinterpret_cast(tangent_data + tangent_attribute.stride * i); - float* ty = tx + 1; - float* tz = ty + 1; - //float* bts = tz + 1; - - // Transform vertex attributes - float3 position = transform * float3{*x, *y, *z}; - float3 normal = math::normalize(transform.rotation * float3{*nx, *ny, *nz}); - float3 tangent = transform.rotation * float3{*tx, *ty, *tz}; - - // Update vertex data - *x = position.x(); - *y = position.y(); - *z = position.z(); - *nx = normal.x(); - *ny = normal.y(); - *nz = normal.z(); - *tx = tangent.x(); - *ty = tangent.y(); - *tz = tangent.z(); - //*bts = ... - *bone_index = static_cast(new_bone_index); - } -} - -geom::aabb calculate_bounds(std::uint8_t* vertex_data, std::size_t index_count, const gl::vertex_attribute& position_attribute) -{ - std::uint8_t* position_data = vertex_data + position_attribute.offset; - - geom::aabb bounds; - bounds.min_point.x() = std::numeric_limits::infinity(); - bounds.min_point.y() = std::numeric_limits::infinity(); - bounds.min_point.z() = std::numeric_limits::infinity(); - bounds.max_point.x() = -std::numeric_limits::infinity(); - bounds.max_point.y() = -std::numeric_limits::infinity(); - bounds.max_point.z() = -std::numeric_limits::infinity(); - - for (std::size_t i = 0; i < index_count; ++i) - { - // Get vertex position - float* x = reinterpret_cast(position_data + position_attribute.stride * i); - float* y = x + 1; - float* z = y + 1; - - bounds.min_point.x() = std::min(*x, bounds.min_point.x()); - bounds.min_point.y() = std::min(*y, bounds.min_point.y()); - bounds.min_point.z() = std::min(*z, bounds.min_point.z()); - bounds.max_point.x() = std::max(*x, bounds.max_point.x()); - bounds.max_point.y() = std::max(*y, bounds.max_point.y()); - bounds.max_point.z() = std::max(*z, bounds.max_point.z()); - } - - return bounds; -} - -} // namespace ant -} // namespace game +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include "game/ant/morphogenesis.hpp" +#include +#include +#include +#include +#include + +namespace ant { + +static render::material* build_exoskeleton_material +( + const phene::pigmentation& pigmentation, + const phene::sculpturing& sculpturing +); +static void reskin_vertices +( + std::uint8_t* vertex_data, + std::size_t index_count, + const gl::vertex_attribute& position_attribute, + const gl::vertex_attribute& normal_attribute, + const gl::vertex_attribute& tangent_attribute, + const gl::vertex_attribute& bone_index_attribute, + const std::unordered_set& old_bone_indices, + std::uint8_t new_bone_index, + const math::transform& transform +); +static geom::aabb calculate_bounds(std::uint8_t* vertex_data, std::size_t index_count, const gl::vertex_attribute& position_attribute); +static render::model* build_model +( + render::material* material, + const render::model* antennae, + const render::model* eyes, + const render::model* forewings, + const render::model* gaster, + const render::model* head, + const render::model* hindwings, + const render::model* legs, + const render::model* mandibles, + const render::model* mesosoma, + const render::model* lateral_ocelli, + const render::model* median_ocellus, + const render::model* sting, + const render::model* waist +); + +render::material* build_exoskeleton_material +( + const phene::pigmentation& pigmentation, + const phene::sculpturing& sculpturing +) +{ + // Allocate copy of pigmentation material + render::material* exoskeleton_material = new render::material(*pigmentation.material); + + // Adjust roughness parameter + if (render::material_property_base* property = exoskeleton_material->get_property("roughness"); property != nullptr) + { + static_cast*>(property)->set_value(sculpturing.roughness); + } + else + { + exoskeleton_material->add_property("roughness")->set_value(sculpturing.roughness); + } + + // Adjust normal map parameter + if (render::material_property_base* property = exoskeleton_material->get_property("normal_map"); property != nullptr) + { + static_cast*>(property)->set_value(sculpturing.normal_map); + } + else + { + exoskeleton_material->add_property("normal_map")->set_value(sculpturing.normal_map); + } + + return exoskeleton_material; +} + +render::model* morphogenesis(const phenome& phenome) +{ + // Build exoskeleton material + render::material* exoskeleton_material = build_exoskeleton_material(*phenome.pigmentation, *phenome.sculpturing); + + // Determine presence of optional parts + bool eyes_present = phenome.eyes->present; + bool lateral_ocelli_present = phenome.ocelli->lateral_ocelli_present; + bool median_ocellus_present = phenome.ocelli->median_ocellus_present; + bool petiole_present = phenome.waist->petiole_present; + bool postpetiole_present = phenome.waist->postpetiole_present; + bool sting_present = phenome.sting->present; + bool wings_present = phenome.wings->present; + + // Get body part models + render::model* antennae_model = phenome.antennae->model; + render::model* eyes_model = phenome.eyes->model; + render::model* forewings_model = phenome.wings->forewings_model; + render::model* gaster_model = phenome.gaster->model; + render::model* head_model = phenome.head->model; + render::model* hindwings_model = phenome.wings->hindwings_model; + render::model* lateral_ocelli_model = phenome.ocelli->lateral_ocelli_model; + render::model* legs_model = phenome.legs->model; + render::model* mandibles_model = phenome.mandibles->model; + render::model* median_ocellus_model = phenome.ocelli->median_ocellus_model; + render::model* mesosoma_model = phenome.mesosoma->model; + render::model* sting_model = phenome.sting->model; + render::model* waist_model = phenome.waist->model; + + // Get body part vertex buffers + const gl::vertex_buffer* antennae_vbo = antennae_model->get_vertex_buffer(); + const gl::vertex_buffer* eyes_vbo = (eyes_present) ? eyes_model->get_vertex_buffer() : nullptr; + const gl::vertex_buffer* forewings_vbo = (wings_present) ? forewings_model->get_vertex_buffer() : nullptr; + const gl::vertex_buffer* gaster_vbo = gaster_model->get_vertex_buffer(); + const gl::vertex_buffer* head_vbo = head_model->get_vertex_buffer(); + const gl::vertex_buffer* hindwings_vbo = (wings_present) ? hindwings_model->get_vertex_buffer() : nullptr; + const gl::vertex_buffer* lateral_ocelli_vbo = (lateral_ocelli_model) ? lateral_ocelli_model->get_vertex_buffer() : nullptr; + const gl::vertex_buffer* legs_vbo = legs_model->get_vertex_buffer(); + const gl::vertex_buffer* mandibles_vbo = mandibles_model->get_vertex_buffer(); + const gl::vertex_buffer* median_ocellus_vbo = (median_ocellus_model) ? median_ocellus_model->get_vertex_buffer() : nullptr; + const gl::vertex_buffer* mesosoma_vbo = mesosoma_model->get_vertex_buffer(); + const gl::vertex_buffer* sting_vbo = (sting_present) ? sting_model->get_vertex_buffer() : nullptr; + const gl::vertex_buffer* waist_vbo = waist_model->get_vertex_buffer(); + + // Determine combined size of vertex buffers and save offsets + std::size_t vertex_buffer_size = 0; + std::size_t mesosoma_vbo_offset = vertex_buffer_size; + vertex_buffer_size += mesosoma_vbo->get_size(); + std::size_t legs_vbo_offset = vertex_buffer_size; + vertex_buffer_size += legs_vbo->get_size(); + std::size_t head_vbo_offset = vertex_buffer_size; + vertex_buffer_size += head_vbo->get_size(); + std::size_t mandibles_vbo_offset = vertex_buffer_size; + vertex_buffer_size += mandibles_vbo->get_size(); + std::size_t antennae_vbo_offset = vertex_buffer_size; + vertex_buffer_size += antennae_vbo->get_size(); + std::size_t waist_vbo_offset = vertex_buffer_size; + vertex_buffer_size += waist_vbo->get_size(); + std::size_t gaster_vbo_offset = vertex_buffer_size; + vertex_buffer_size += gaster_vbo->get_size(); + std::size_t sting_vbo_offset = vertex_buffer_size; + if (sting_present) + vertex_buffer_size += sting_vbo->get_size(); + std::size_t eyes_vbo_offset = vertex_buffer_size; + if (eyes_present) + vertex_buffer_size += eyes_vbo->get_size(); + std::size_t lateral_ocelli_vbo_offset = vertex_buffer_size; + if (lateral_ocelli_present) + vertex_buffer_size += lateral_ocelli_vbo->get_size(); + std::size_t median_ocellus_vbo_offset = vertex_buffer_size; + if (median_ocellus_present) + vertex_buffer_size += median_ocellus_vbo->get_size(); + std::size_t forewings_vbo_offset = vertex_buffer_size; + if (wings_present) + vertex_buffer_size += forewings_vbo->get_size(); + std::size_t hindwings_vbo_offset = vertex_buffer_size; + if (wings_present) + vertex_buffer_size += hindwings_vbo->get_size(); + + // Allocate combined vertex buffer data + std::uint8_t* vertex_buffer_data = new std::uint8_t[vertex_buffer_size]; + + // Read body part vertex buffer data into combined vertex buffer data + mesosoma_vbo->read(0, mesosoma_vbo->get_size(), vertex_buffer_data + mesosoma_vbo_offset); + legs_vbo->read(0, legs_vbo->get_size(), vertex_buffer_data + legs_vbo_offset); + head_vbo->read(0, head_vbo->get_size(), vertex_buffer_data + head_vbo_offset); + mandibles_vbo->read(0, mandibles_vbo->get_size(), vertex_buffer_data + mandibles_vbo_offset); + antennae_vbo->read(0, antennae_vbo->get_size(), vertex_buffer_data + antennae_vbo_offset); + waist_vbo->read(0, waist_vbo->get_size(), vertex_buffer_data + waist_vbo_offset); + gaster_vbo->read(0, gaster_vbo->get_size(), vertex_buffer_data + gaster_vbo_offset); + if (sting_present) + sting_vbo->read(0, sting_vbo->get_size(), vertex_buffer_data + sting_vbo_offset); + if (eyes_present) + eyes_vbo->read(0, eyes_vbo->get_size(), vertex_buffer_data + eyes_vbo_offset); + if (lateral_ocelli_present) + lateral_ocelli_vbo->read(0, lateral_ocelli_vbo->get_size(), vertex_buffer_data + lateral_ocelli_vbo_offset); + if (median_ocellus_present) + median_ocellus_vbo->read(0, median_ocellus_vbo->get_size(), vertex_buffer_data + median_ocellus_vbo_offset); + if (wings_present) + { + forewings_vbo->read(0, forewings_vbo->get_size(), vertex_buffer_data + forewings_vbo_offset); + hindwings_vbo->read(0, hindwings_vbo->get_size(), vertex_buffer_data + hindwings_vbo_offset); + } + + // Allocate model + render::model* model = new render::model(); + + // Setup model VAO + gl::vertex_array* model_vao = model->get_vertex_array(); + for (auto [location, attribute]: mesosoma_model->get_vertex_array()->get_attributes()) + { + attribute.buffer = model->get_vertex_buffer(); + model_vao->bind(location, attribute); + } + + // Get vertex attributes + const gl::vertex_attribute* position_attribute = nullptr; + const gl::vertex_attribute* normal_attribute = nullptr; + const gl::vertex_attribute* tangent_attribute = nullptr; + const gl::vertex_attribute* bone_index_attribute = nullptr; + const auto& vertex_attribute_map = model_vao->get_attributes(); + if (auto it = vertex_attribute_map.find(render::vertex_attribute::position); it != vertex_attribute_map.end()) + position_attribute = &it->second; + if (auto it = vertex_attribute_map.find(render::vertex_attribute::normal); it != vertex_attribute_map.end()) + normal_attribute = &it->second; + if (auto it = vertex_attribute_map.find(render::vertex_attribute::tangent); it != vertex_attribute_map.end()) + tangent_attribute = &it->second; + if (auto it = vertex_attribute_map.find(render::vertex_attribute::bone_index); it != vertex_attribute_map.end()) + bone_index_attribute = &it->second; + + // Get body part skeletons + const ::skeleton& mesosoma_skeleton = mesosoma_model->get_skeleton(); + const ::skeleton& legs_skeleton = legs_model->get_skeleton(); + const ::skeleton& head_skeleton = head_model->get_skeleton(); + const ::skeleton& mandibles_skeleton = mandibles_model->get_skeleton(); + const ::skeleton& antennae_skeleton = antennae_model->get_skeleton(); + const ::skeleton& waist_skeleton = waist_model->get_skeleton(); + const ::skeleton& gaster_skeleton = gaster_model->get_skeleton(); + const ::skeleton* sting_skeleton = (sting_present) ? &sting_model->get_skeleton() : nullptr; + const ::skeleton* eyes_skeleton = (eyes_present) ? &eyes_model->get_skeleton() : nullptr; + const ::skeleton* lateral_ocelli_skeleton = (lateral_ocelli_present) ? &lateral_ocelli_model->get_skeleton() : nullptr; + const ::skeleton* median_ocellus_skeleton = (median_ocellus_present) ? &median_ocellus_model->get_skeleton() : nullptr; + + // Allocate skeleton bones + ::skeleton& skeleton = model->get_skeleton(); + std::size_t bone_count = 33; + if (petiole_present) + bone_count += 1; + if (postpetiole_present) + bone_count += 1; + if (sting_present) + bone_count += 1; + if (wings_present) + bone_count += 4; + + // Assign bone indices + std::uint8_t bone_index = 0; + std::uint8_t mesosoma_bone_index = bone_index++; + std::uint8_t procoxa_l_bone_index = bone_index++; + std::uint8_t procoxa_r_bone_index = bone_index++; + std::uint8_t profemur_l_bone_index = bone_index++; + std::uint8_t profemur_r_bone_index = bone_index++; + std::uint8_t protibia_l_bone_index = bone_index++; + std::uint8_t protibia_r_bone_index = bone_index++; + std::uint8_t protarsus_l_bone_index = bone_index++; + std::uint8_t protarsus_r_bone_index = bone_index++; + std::uint8_t mesocoxa_l_bone_index = bone_index++; + std::uint8_t mesocoxa_r_bone_index = bone_index++; + std::uint8_t mesofemur_l_bone_index = bone_index++; + std::uint8_t mesofemur_r_bone_index = bone_index++; + std::uint8_t mesotibia_l_bone_index = bone_index++; + std::uint8_t mesotibia_r_bone_index = bone_index++; + std::uint8_t mesotarsus_l_bone_index = bone_index++; + std::uint8_t mesotarsus_r_bone_index = bone_index++; + std::uint8_t metacoxa_l_bone_index = bone_index++; + std::uint8_t metacoxa_r_bone_index = bone_index++; + std::uint8_t metafemur_l_bone_index = bone_index++; + std::uint8_t metafemur_r_bone_index = bone_index++; + std::uint8_t metatibia_l_bone_index = bone_index++; + std::uint8_t metatibia_r_bone_index = bone_index++; + std::uint8_t metatarsus_l_bone_index = bone_index++; + std::uint8_t metatarsus_r_bone_index = bone_index++; + std::uint8_t head_bone_index = bone_index++; + std::uint8_t mandible_l_bone_index = bone_index++; + std::uint8_t mandible_r_bone_index = bone_index++; + std::uint8_t antennomere1_l_bone_index = bone_index++; + std::uint8_t antennomere1_r_bone_index = bone_index++; + std::uint8_t antennomere2_l_bone_index = bone_index++; + std::uint8_t antennomere2_r_bone_index = bone_index++; + std::uint8_t petiole_bone_index =(petiole_present) ? bone_index++ : static_cast(bone_count); + std::uint8_t postpetiole_bone_index = (postpetiole_present) ? bone_index++ : static_cast(bone_count); + std::uint8_t gaster_bone_index = bone_index++; + std::uint8_t sting_bone_index = (sting_present) ? bone_index++ : static_cast(bone_count); + + // Construct bone identifiers + ::bone mesosoma_bone = make_bone(mesosoma_bone_index); + ::bone procoxa_l_bone = make_bone(procoxa_l_bone_index, mesosoma_bone_index); + ::bone procoxa_r_bone = make_bone(procoxa_r_bone_index, mesosoma_bone_index); + ::bone profemur_l_bone = make_bone(profemur_l_bone_index, procoxa_l_bone_index); + ::bone profemur_r_bone = make_bone(profemur_r_bone_index, procoxa_r_bone_index); + ::bone protibia_l_bone = make_bone(protibia_l_bone_index, profemur_l_bone_index); + ::bone protibia_r_bone = make_bone(protibia_r_bone_index, profemur_r_bone_index); + ::bone protarsus_l_bone = make_bone(protarsus_l_bone_index, protibia_l_bone_index); + ::bone protarsus_r_bone = make_bone(protarsus_r_bone_index, protibia_r_bone_index); + ::bone mesocoxa_l_bone = make_bone(mesocoxa_l_bone_index, mesosoma_bone_index); + ::bone mesocoxa_r_bone = make_bone(mesocoxa_r_bone_index, mesosoma_bone_index); + ::bone mesofemur_l_bone = make_bone(mesofemur_l_bone_index, mesocoxa_l_bone_index); + ::bone mesofemur_r_bone = make_bone(mesofemur_r_bone_index, mesocoxa_r_bone_index); + ::bone mesotibia_l_bone = make_bone(mesotibia_l_bone_index, mesofemur_l_bone_index); + ::bone mesotibia_r_bone = make_bone(mesotibia_r_bone_index, mesofemur_r_bone_index); + ::bone mesotarsus_l_bone = make_bone(mesotarsus_l_bone_index, mesotibia_l_bone_index); + ::bone mesotarsus_r_bone = make_bone(mesotarsus_r_bone_index, mesotibia_r_bone_index); + ::bone metacoxa_l_bone = make_bone(metacoxa_l_bone_index, mesosoma_bone_index); + ::bone metacoxa_r_bone = make_bone(metacoxa_r_bone_index, mesosoma_bone_index); + ::bone metafemur_l_bone = make_bone(metafemur_l_bone_index, metacoxa_l_bone_index); + ::bone metafemur_r_bone = make_bone(metafemur_r_bone_index, metacoxa_r_bone_index); + ::bone metatibia_l_bone = make_bone(metatibia_l_bone_index, metafemur_l_bone_index); + ::bone metatibia_r_bone = make_bone(metatibia_r_bone_index, metafemur_r_bone_index); + ::bone metatarsus_l_bone = make_bone(metatarsus_l_bone_index, metatibia_l_bone_index); + ::bone metatarsus_r_bone = make_bone(metatarsus_r_bone_index, metatibia_r_bone_index); + ::bone head_bone = make_bone(head_bone_index, mesosoma_bone_index); + ::bone mandible_l_bone = make_bone(mandible_l_bone_index, head_bone_index); + ::bone mandible_r_bone = make_bone(mandible_r_bone_index, head_bone_index); + ::bone antennomere1_l_bone = make_bone(antennomere1_l_bone_index, head_bone_index); + ::bone antennomere1_r_bone = make_bone(antennomere1_r_bone_index, head_bone_index); + ::bone antennomere2_l_bone = make_bone(antennomere2_l_bone_index, antennomere1_l_bone_index); + ::bone antennomere2_r_bone = make_bone(antennomere2_r_bone_index, antennomere1_r_bone_index); + ::bone petiole_bone = make_bone(petiole_bone_index, mesosoma_bone_index); + ::bone postpetiole_bone = make_bone(postpetiole_bone_index, petiole_bone_index); + ::bone gaster_bone = make_bone(gaster_bone_index, (postpetiole_present) ? postpetiole_bone_index : petiole_bone_index); + ::bone sting_bone = make_bone(sting_bone_index, gaster_bone_index); + + // Map bone names to bones + skeleton.bone_map["mesosoma"] = mesosoma_bone; + skeleton.bone_map["procoxa_l"] = procoxa_l_bone; + skeleton.bone_map["procoxa_r"] = procoxa_r_bone; + skeleton.bone_map["profemur_l"] = profemur_l_bone; + skeleton.bone_map["profemur_r"] = profemur_r_bone; + skeleton.bone_map["protibia_l"] = protibia_l_bone; + skeleton.bone_map["protibia_r"] = protibia_r_bone; + skeleton.bone_map["protarsus_l"] = protarsus_l_bone; + skeleton.bone_map["protarsus_r"] = protarsus_r_bone; + skeleton.bone_map["mesocoxa_l"] = mesocoxa_l_bone; + skeleton.bone_map["mesocoxa_r"] = mesocoxa_r_bone; + skeleton.bone_map["mesofemur_l"] = mesofemur_l_bone; + skeleton.bone_map["mesofemur_r"] = mesofemur_r_bone; + skeleton.bone_map["mesotibia_l"] = mesotibia_l_bone; + skeleton.bone_map["mesotibia_r"] = mesotibia_r_bone; + skeleton.bone_map["mesotarsus_l"] = mesotarsus_l_bone; + skeleton.bone_map["mesotarsus_r"] = mesotarsus_r_bone; + skeleton.bone_map["metacoxa_l"] = metacoxa_l_bone; + skeleton.bone_map["metacoxa_r"] = metacoxa_r_bone; + skeleton.bone_map["metafemur_l"] = metafemur_l_bone; + skeleton.bone_map["metafemur_r"] = metafemur_r_bone; + skeleton.bone_map["metatibia_l"] = metatibia_l_bone; + skeleton.bone_map["metatibia_r"] = metatibia_r_bone; + skeleton.bone_map["metatarsus_l"] = metatarsus_l_bone; + skeleton.bone_map["metatarsus_r"] = metatarsus_r_bone; + skeleton.bone_map["head"] = head_bone; + skeleton.bone_map["mandible_l"] = mandible_l_bone; + skeleton.bone_map["mandible_r"] = mandible_r_bone; + skeleton.bone_map["antennomere1_l"] = antennomere1_l_bone; + skeleton.bone_map["antennomere1_r"] = antennomere1_r_bone; + skeleton.bone_map["antennomere2_l"] = antennomere2_l_bone; + skeleton.bone_map["antennomere2_r"] = antennomere2_r_bone; + if (petiole_present) + skeleton.bone_map["petiole"] = petiole_bone; + if (postpetiole_present) + skeleton.bone_map["postpetiole"] = postpetiole_bone; + skeleton.bone_map["gaster"] = gaster_bone; + if (sting_present) + skeleton.bone_map["sting"] = sting_bone; + + // Get reference to skeleton bind pose + pose& bind_pose = skeleton.bind_pose; + + // Skeleton mesosoma pose + if (auto it = mesosoma_skeleton.bone_map.find("mesosoma"); it != mesosoma_skeleton.bone_map.end()) + bind_pose[mesosoma_bone] = mesosoma_skeleton.bind_pose.at(it->second); + + // Skeleton forelegs pose + if (auto it = legs_skeleton.bone_map.find("procoxa_l"); it != legs_skeleton.bone_map.end()) + bind_pose[procoxa_l_bone] = legs_skeleton.bind_pose.at(it->second); + if (auto it = legs_skeleton.bone_map.find("procoxa_r"); it != legs_skeleton.bone_map.end()) + bind_pose[procoxa_r_bone] = legs_skeleton.bind_pose.at(it->second); + if (auto it = legs_skeleton.bone_map.find("profemur_l"); it != legs_skeleton.bone_map.end()) + bind_pose[profemur_l_bone] = legs_skeleton.bind_pose.at(it->second); + if (auto it = legs_skeleton.bone_map.find("profemur_r"); it != legs_skeleton.bone_map.end()) + bind_pose[profemur_r_bone] = legs_skeleton.bind_pose.at(it->second); + if (auto it = legs_skeleton.bone_map.find("protibia_l"); it != legs_skeleton.bone_map.end()) + bind_pose[protibia_l_bone] = legs_skeleton.bind_pose.at(it->second); + if (auto it = legs_skeleton.bone_map.find("protibia_r"); it != legs_skeleton.bone_map.end()) + bind_pose[protibia_r_bone] = legs_skeleton.bind_pose.at(it->second); + if (auto it = legs_skeleton.bone_map.find("protarsus1_l"); it != legs_skeleton.bone_map.end()) + bind_pose[protarsus_l_bone] = legs_skeleton.bind_pose.at(it->second); + if (auto it = legs_skeleton.bone_map.find("protarsus1_r"); it != legs_skeleton.bone_map.end()) + bind_pose[protarsus_r_bone] = legs_skeleton.bind_pose.at(it->second); + + // Skeleton midlegs pose + if (auto it = legs_skeleton.bone_map.find("mesocoxa_l"); it != legs_skeleton.bone_map.end()) + bind_pose[mesocoxa_l_bone] = legs_skeleton.bind_pose.at(it->second); + if (auto it = legs_skeleton.bone_map.find("mesocoxa_r"); it != legs_skeleton.bone_map.end()) + bind_pose[mesocoxa_r_bone] = legs_skeleton.bind_pose.at(it->second); + if (auto it = legs_skeleton.bone_map.find("mesofemur_l"); it != legs_skeleton.bone_map.end()) + bind_pose[mesofemur_l_bone] = legs_skeleton.bind_pose.at(it->second); + if (auto it = legs_skeleton.bone_map.find("mesofemur_r"); it != legs_skeleton.bone_map.end()) + bind_pose[mesofemur_r_bone] = legs_skeleton.bind_pose.at(it->second); + if (auto it = legs_skeleton.bone_map.find("mesotibia_l"); it != legs_skeleton.bone_map.end()) + bind_pose[mesotibia_l_bone] = legs_skeleton.bind_pose.at(it->second); + if (auto it = legs_skeleton.bone_map.find("mesotibia_r"); it != legs_skeleton.bone_map.end()) + bind_pose[mesotibia_r_bone] = legs_skeleton.bind_pose.at(it->second); + if (auto it = legs_skeleton.bone_map.find("mesotarsus1_l"); it != legs_skeleton.bone_map.end()) + bind_pose[mesotarsus_l_bone] = legs_skeleton.bind_pose.at(it->second); + if (auto it = legs_skeleton.bone_map.find("mesotarsus1_r"); it != legs_skeleton.bone_map.end()) + bind_pose[mesotarsus_r_bone] = legs_skeleton.bind_pose.at(it->second); + + // Skeleton hindlegs pose + if (auto it = legs_skeleton.bone_map.find("metacoxa_l"); it != legs_skeleton.bone_map.end()) + bind_pose[metacoxa_l_bone] = legs_skeleton.bind_pose.at(it->second); + if (auto it = legs_skeleton.bone_map.find("metacoxa_r"); it != legs_skeleton.bone_map.end()) + bind_pose[metacoxa_r_bone] = legs_skeleton.bind_pose.at(it->second); + if (auto it = legs_skeleton.bone_map.find("metafemur_l"); it != legs_skeleton.bone_map.end()) + bind_pose[metafemur_l_bone] = legs_skeleton.bind_pose.at(it->second); + if (auto it = legs_skeleton.bone_map.find("metafemur_r"); it != legs_skeleton.bone_map.end()) + bind_pose[metafemur_r_bone] = legs_skeleton.bind_pose.at(it->second); + if (auto it = legs_skeleton.bone_map.find("metatibia_l"); it != legs_skeleton.bone_map.end()) + bind_pose[metatibia_l_bone] = legs_skeleton.bind_pose.at(it->second); + if (auto it = legs_skeleton.bone_map.find("metatibia_r"); it != legs_skeleton.bone_map.end()) + bind_pose[metatibia_r_bone] = legs_skeleton.bind_pose.at(it->second); + if (auto it = legs_skeleton.bone_map.find("metatarsus1_l"); it != legs_skeleton.bone_map.end()) + bind_pose[metatarsus_l_bone] = legs_skeleton.bind_pose.at(it->second); + if (auto it = legs_skeleton.bone_map.find("metatarsus1_r"); it != legs_skeleton.bone_map.end()) + bind_pose[metatarsus_r_bone] = legs_skeleton.bind_pose.at(it->second); + + // Skeleton head pose + bind_pose[head_bone] = mesosoma_skeleton.bind_pose.at(mesosoma_skeleton.bone_map.at("head")) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("head")); + + // Skeleton mandibles pose + bind_pose[mandible_l_bone] = head_skeleton.bind_pose.at(head_skeleton.bone_map.at("mandible_l")) * mandibles_skeleton.bind_pose.at(mandibles_skeleton.bone_map.at("mandible_l")); + bind_pose[mandible_r_bone] = head_skeleton.bind_pose.at(head_skeleton.bone_map.at("mandible_r")) * mandibles_skeleton.bind_pose.at(mandibles_skeleton.bone_map.at("mandible_r")); + + // Skeleton antennae pose + bind_pose[antennomere1_l_bone] = head_skeleton.bind_pose.at(head_skeleton.bone_map.at("antenna_l")) * antennae_skeleton.bind_pose.at(antennae_skeleton.bone_map.at("antennomere1_l")); + bind_pose[antennomere1_r_bone] = head_skeleton.bind_pose.at(head_skeleton.bone_map.at("antenna_r")) * antennae_skeleton.bind_pose.at(antennae_skeleton.bone_map.at("antennomere1_r")); + bind_pose[antennomere2_l_bone] = antennae_skeleton.bind_pose.at(antennae_skeleton.bone_map.at("antennomere2_l")); + bind_pose[antennomere2_r_bone] = antennae_skeleton.bind_pose.at(antennae_skeleton.bone_map.at("antennomere2_r")); + + // Skeleton waist pose + if (petiole_present) + { + bind_pose[petiole_bone] = mesosoma_skeleton.bind_pose.at(mesosoma_skeleton.bone_map.at("petiole")) * waist_skeleton.bind_pose.at(waist_skeleton.bone_map.at("petiole")); + } + if (postpetiole_present) + { + bind_pose[postpetiole_bone] = waist_skeleton.bind_pose.at(waist_skeleton.bone_map.at("postpetiole")); + } + + // Skeleton gaster pose + if (postpetiole_present) + { + bind_pose[gaster_bone] = waist_skeleton.bind_pose.at(waist_skeleton.bone_map.at("postpetiole")) * gaster_skeleton.bind_pose.at(gaster_skeleton.bone_map.at("gaster")); + } + else if (petiole_present) + { + bind_pose[gaster_bone] = waist_skeleton.bind_pose.at(waist_skeleton.bone_map.at("petiole")) * gaster_skeleton.bind_pose.at(gaster_skeleton.bone_map.at("gaster")); + } + else + { + bind_pose[gaster_bone] = mesosoma_skeleton.bind_pose.at(mesosoma_skeleton.bone_map.at("petiole")) * gaster_skeleton.bind_pose.at(gaster_skeleton.bone_map.at("gaster")); + } + + // Skeleton sting pose + if (sting_present) + { + bind_pose[sting_bone] = gaster_skeleton.bind_pose.at(gaster_skeleton.bone_map.at("sting")) * sting_skeleton->bind_pose.at(sting_skeleton->bone_map.at("sting")); + } + + // Calculate the skeleton-space bind pose + pose bind_pose_ss; + ::concatenate(bind_pose, bind_pose_ss); + + // Calculate inverse skeleton-space bind pose + ::inverse(bind_pose_ss, skeleton.inverse_bind_pose); + + // Get number of vertex indices for each body part + std::size_t mesosoma_index_count = (*mesosoma_model->get_groups())[0]->get_index_count(); + std::size_t legs_index_count = (*legs_model->get_groups())[0]->get_index_count(); + std::size_t head_index_count = (*head_model->get_groups())[0]->get_index_count(); + std::size_t mandibles_index_count = (*mandibles_model->get_groups())[0]->get_index_count(); + std::size_t antennae_index_count = (*antennae_model->get_groups())[0]->get_index_count(); + std::size_t waist_index_count = (*waist_model->get_groups())[0]->get_index_count(); + std::size_t gaster_index_count = (*gaster_model->get_groups())[0]->get_index_count(); + std::size_t sting_index_count = (sting_present) ? (*sting_model->get_groups())[0]->get_index_count() : 0; + std::size_t eyes_index_count = (eyes_present) ? (*eyes_model->get_groups())[0]->get_index_count() : 0; + std::size_t lateral_ocelli_index_count = (lateral_ocelli_present) ? (*lateral_ocelli_model->get_groups())[0]->get_index_count() : 0; + std::size_t median_ocellus_index_count = (median_ocellus_present) ? (*median_ocellus_model->get_groups())[0]->get_index_count() : 0; + std::size_t forewings_index_count = (wings_present) ? (*forewings_model->get_groups())[0]->get_index_count() : 0; + std::size_t hindwings_index_count = (wings_present) ? (*hindwings_model->get_groups())[0]->get_index_count() : 0; + std::size_t exoskeleton_index_count = + mesosoma_index_count + + legs_index_count + + head_index_count + + mandibles_index_count + + antennae_index_count + + waist_index_count + + gaster_index_count + + sting_index_count; + + // Calculate transform from legs space to body space + const math::transform& legs_to_body = math::transform::identity; + + // Reskin leg bones + std::unordered_set old_procoxa_l_indices; + if (auto it = legs_skeleton.bone_map.find("procoxa_l"); it != legs_skeleton.bone_map.end()) + old_procoxa_l_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_procoxa_l_indices, procoxa_l_bone_index, legs_to_body); + std::unordered_set old_profemur_l_indices; + if (auto it = legs_skeleton.bone_map.find("profemur_l"); it != legs_skeleton.bone_map.end()) + old_profemur_l_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_profemur_l_indices, profemur_l_bone_index, legs_to_body); + std::unordered_set old_protibia_l_indices; + if (auto it = legs_skeleton.bone_map.find("protibia_l"); it != legs_skeleton.bone_map.end()) + old_protibia_l_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_protibia_l_indices, protibia_l_bone_index, legs_to_body); + std::unordered_set old_protarsus_l_indices; + if (auto it = legs_skeleton.bone_map.find("protarsus1_l"); it != legs_skeleton.bone_map.end()) + old_protarsus_l_indices.emplace(it->second); + if (auto it = legs_skeleton.bone_map.find("protarsus2_l"); it != legs_skeleton.bone_map.end()) + old_protarsus_l_indices.emplace(it->second); + if (auto it = legs_skeleton.bone_map.find("protarsus3_l"); it != legs_skeleton.bone_map.end()) + old_protarsus_l_indices.emplace(it->second); + if (auto it = legs_skeleton.bone_map.find("protarsus4_l"); it != legs_skeleton.bone_map.end()) + old_protarsus_l_indices.emplace(it->second); + if (auto it = legs_skeleton.bone_map.find("protarsus5_l"); it != legs_skeleton.bone_map.end()) + old_protarsus_l_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_protarsus_l_indices, protarsus_l_bone_index, legs_to_body); + + std::unordered_set old_procoxa_r_indices; + if (auto it = legs_skeleton.bone_map.find("procoxa_r"); it != legs_skeleton.bone_map.end()) + old_procoxa_r_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_procoxa_r_indices, procoxa_r_bone_index, legs_to_body); + std::unordered_set old_profemur_r_indices; + if (auto it = legs_skeleton.bone_map.find("profemur_r"); it != legs_skeleton.bone_map.end()) + old_profemur_r_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_profemur_r_indices, profemur_r_bone_index, legs_to_body); + std::unordered_set old_protibia_r_indices; + if (auto it = legs_skeleton.bone_map.find("protibia_r"); it != legs_skeleton.bone_map.end()) + old_protibia_r_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_protibia_r_indices, protibia_r_bone_index, legs_to_body); + std::unordered_set old_protarsus_r_indices; + if (auto it = legs_skeleton.bone_map.find("protarsus1_r"); it != legs_skeleton.bone_map.end()) + old_protarsus_r_indices.emplace(it->second); + if (auto it = legs_skeleton.bone_map.find("protarsus2_r"); it != legs_skeleton.bone_map.end()) + old_protarsus_r_indices.emplace(it->second); + if (auto it = legs_skeleton.bone_map.find("protarsus3_r"); it != legs_skeleton.bone_map.end()) + old_protarsus_r_indices.emplace(it->second); + if (auto it = legs_skeleton.bone_map.find("protarsus4_r"); it != legs_skeleton.bone_map.end()) + old_protarsus_r_indices.emplace(it->second); + if (auto it = legs_skeleton.bone_map.find("protarsus5_r"); it != legs_skeleton.bone_map.end()) + old_protarsus_r_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_protarsus_r_indices, protarsus_r_bone_index, legs_to_body); + + std::unordered_set old_mesocoxa_l_indices; + if (auto it = legs_skeleton.bone_map.find("mesocoxa_l"); it != legs_skeleton.bone_map.end()) + old_mesocoxa_l_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_mesocoxa_l_indices, mesocoxa_l_bone_index, legs_to_body); + std::unordered_set old_mesofemur_l_indices; + if (auto it = legs_skeleton.bone_map.find("mesofemur_l"); it != legs_skeleton.bone_map.end()) + old_mesofemur_l_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_mesofemur_l_indices, mesofemur_l_bone_index, legs_to_body); + std::unordered_set old_mesotibia_l_indices; + if (auto it = legs_skeleton.bone_map.find("mesotibia_l"); it != legs_skeleton.bone_map.end()) + old_mesotibia_l_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_mesotibia_l_indices, mesotibia_l_bone_index, legs_to_body); + std::unordered_set old_mesotarsus_l_indices; + if (auto it = legs_skeleton.bone_map.find("mesotarsus1_l"); it != legs_skeleton.bone_map.end()) + old_mesotarsus_l_indices.emplace(it->second); + if (auto it = legs_skeleton.bone_map.find("mesotarsus2_l"); it != legs_skeleton.bone_map.end()) + old_mesotarsus_l_indices.emplace(it->second); + if (auto it = legs_skeleton.bone_map.find("mesotarsus3_l"); it != legs_skeleton.bone_map.end()) + old_mesotarsus_l_indices.emplace(it->second); + if (auto it = legs_skeleton.bone_map.find("mesotarsus4_l"); it != legs_skeleton.bone_map.end()) + old_mesotarsus_l_indices.emplace(it->second); + if (auto it = legs_skeleton.bone_map.find("mesotarsus5_l"); it != legs_skeleton.bone_map.end()) + old_mesotarsus_l_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_mesotarsus_l_indices, mesotarsus_l_bone_index, legs_to_body); + + std::unordered_set old_mesocoxa_r_indices; + if (auto it = legs_skeleton.bone_map.find("mesocoxa_r"); it != legs_skeleton.bone_map.end()) + old_mesocoxa_r_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_mesocoxa_r_indices, mesocoxa_r_bone_index, legs_to_body); + std::unordered_set old_mesofemur_r_indices; + if (auto it = legs_skeleton.bone_map.find("mesofemur_r"); it != legs_skeleton.bone_map.end()) + old_mesofemur_r_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_mesofemur_r_indices, mesofemur_r_bone_index, legs_to_body); + std::unordered_set old_mesotibia_r_indices; + if (auto it = legs_skeleton.bone_map.find("mesotibia_r"); it != legs_skeleton.bone_map.end()) + old_mesotibia_r_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_mesotibia_r_indices, mesotibia_r_bone_index, legs_to_body); + std::unordered_set old_mesotarsus_r_indices; + if (auto it = legs_skeleton.bone_map.find("mesotarsus1_r"); it != legs_skeleton.bone_map.end()) + old_mesotarsus_r_indices.emplace(it->second); + if (auto it = legs_skeleton.bone_map.find("mesotarsus2_r"); it != legs_skeleton.bone_map.end()) + old_mesotarsus_r_indices.emplace(it->second); + if (auto it = legs_skeleton.bone_map.find("mesotarsus3_r"); it != legs_skeleton.bone_map.end()) + old_mesotarsus_r_indices.emplace(it->second); + if (auto it = legs_skeleton.bone_map.find("mesotarsus4_r"); it != legs_skeleton.bone_map.end()) + old_mesotarsus_r_indices.emplace(it->second); + if (auto it = legs_skeleton.bone_map.find("mesotarsus5_r"); it != legs_skeleton.bone_map.end()) + old_mesotarsus_r_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_mesotarsus_r_indices, mesotarsus_r_bone_index, legs_to_body); + + std::unordered_set old_metacoxa_l_indices; + if (auto it = legs_skeleton.bone_map.find("metacoxa_l"); it != legs_skeleton.bone_map.end()) + old_metacoxa_l_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_metacoxa_l_indices, metacoxa_l_bone_index, legs_to_body); + std::unordered_set old_metafemur_l_indices; + if (auto it = legs_skeleton.bone_map.find("metafemur_l"); it != legs_skeleton.bone_map.end()) + old_metafemur_l_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_metafemur_l_indices, metafemur_l_bone_index, legs_to_body); + std::unordered_set old_metatibia_l_indices; + if (auto it = legs_skeleton.bone_map.find("metatibia_l"); it != legs_skeleton.bone_map.end()) + old_metatibia_l_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_metatibia_l_indices, metatibia_l_bone_index, legs_to_body); + std::unordered_set old_metatarsus_l_indices; + if (auto it = legs_skeleton.bone_map.find("metatarsus1_l"); it != legs_skeleton.bone_map.end()) + old_metatarsus_l_indices.emplace(it->second); + if (auto it = legs_skeleton.bone_map.find("metatarsus2_l"); it != legs_skeleton.bone_map.end()) + old_metatarsus_l_indices.emplace(it->second); + if (auto it = legs_skeleton.bone_map.find("metatarsus3_l"); it != legs_skeleton.bone_map.end()) + old_metatarsus_l_indices.emplace(it->second); + if (auto it = legs_skeleton.bone_map.find("metatarsus4_l"); it != legs_skeleton.bone_map.end()) + old_metatarsus_l_indices.emplace(it->second); + if (auto it = legs_skeleton.bone_map.find("metatarsus5_l"); it != legs_skeleton.bone_map.end()) + old_metatarsus_l_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_metatarsus_l_indices, metatarsus_l_bone_index, legs_to_body); + + std::unordered_set old_metacoxa_r_indices; + if (auto it = legs_skeleton.bone_map.find("metacoxa_r"); it != legs_skeleton.bone_map.end()) + old_metacoxa_r_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_metacoxa_r_indices, metacoxa_r_bone_index, legs_to_body); + std::unordered_set old_metafemur_r_indices; + if (auto it = legs_skeleton.bone_map.find("metafemur_r"); it != legs_skeleton.bone_map.end()) + old_metafemur_r_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_metafemur_r_indices, metafemur_r_bone_index, legs_to_body); + std::unordered_set old_metatibia_r_indices; + if (auto it = legs_skeleton.bone_map.find("metatibia_r"); it != legs_skeleton.bone_map.end()) + old_metatibia_r_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_metatibia_r_indices, metatibia_r_bone_index, legs_to_body); + std::unordered_set old_metatarsus_r_indices; + if (auto it = legs_skeleton.bone_map.find("metatarsus1_r"); it != legs_skeleton.bone_map.end()) + old_metatarsus_r_indices.emplace(it->second); + if (auto it = legs_skeleton.bone_map.find("metatarsus2_r"); it != legs_skeleton.bone_map.end()) + old_metatarsus_r_indices.emplace(it->second); + if (auto it = legs_skeleton.bone_map.find("metatarsus3_r"); it != legs_skeleton.bone_map.end()) + old_metatarsus_r_indices.emplace(it->second); + if (auto it = legs_skeleton.bone_map.find("metatarsus4_r"); it != legs_skeleton.bone_map.end()) + old_metatarsus_r_indices.emplace(it->second); + if (auto it = legs_skeleton.bone_map.find("metatarsus5_r"); it != legs_skeleton.bone_map.end()) + old_metatarsus_r_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data + legs_vbo_offset, legs_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_metatarsus_r_indices, metatarsus_r_bone_index, legs_to_body); + + // Calculate transform from head space to body space + math::transform head_to_body = bind_pose_ss.at(mesosoma_bone) * mesosoma_skeleton.bind_pose.at(mesosoma_skeleton.bone_map.at("head")); + + // Reskin head bone + std::unordered_set old_head_bone_indices; + if (auto it = head_skeleton.bone_map.find("head"); it != head_skeleton.bone_map.end()) + old_head_bone_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data + head_vbo_offset, head_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_head_bone_indices, head_bone_index, head_to_body); + + // Calculate transforms from mandibles space to body space + math::transform mandible_l_to_body = bind_pose_ss.at(head_bone) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("mandible_l")); + math::transform mandible_r_to_body = bind_pose_ss.at(head_bone) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("mandible_r")); + + // Reskin mandible bones + std::unordered_set old_head_mandible_l_bone_indices; + if (auto it = mandibles_skeleton.bone_map.find("mandible_l"); it != mandibles_skeleton.bone_map.end()) + old_head_mandible_l_bone_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data + mandibles_vbo_offset, mandibles_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_head_mandible_l_bone_indices, mandible_l_bone_index, mandible_l_to_body); + std::unordered_set old_head_mandible_r_bone_indices; + if (auto it = mandibles_skeleton.bone_map.find("mandible_r"); it != mandibles_skeleton.bone_map.end()) + old_head_mandible_r_bone_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data + mandibles_vbo_offset, mandibles_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_head_mandible_r_bone_indices, mandible_r_bone_index, mandible_r_to_body); + + // Calculate transforms from antennae space to body space + math::transform antenna_l_to_body = bind_pose_ss.at(head_bone) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("antenna_l")); + math::transform antenna_r_to_body = bind_pose_ss.at(head_bone) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("antenna_r")); + + // Reskin antennomere1 bones + std::unordered_set old_antennomere1_l_indices; + if (auto it = antennae_skeleton.bone_map.find("antennomere1_l"); it != antennae_skeleton.bone_map.end()) + old_antennomere1_l_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data + antennae_vbo_offset, antennae_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_antennomere1_l_indices, antennomere1_l_bone_index, antenna_l_to_body); + std::unordered_set old_antennomere1_r_indices; + if (auto it = antennae_skeleton.bone_map.find("antennomere1_r"); it != antennae_skeleton.bone_map.end()) + old_antennomere1_r_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data + antennae_vbo_offset, antennae_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_antennomere1_r_indices, antennomere1_r_bone_index, antenna_r_to_body); + + // Reskin antennomere2+ bones + const std::vector antennomere_bone_names = + { + "antennomere2", + "antennomere3", + "antennomere4", + "antennomere5", + "antennomere6", + "antennomere7", + "antennomere8", + "antennomere9", + "antennomere10", + "antennomere11", + "antennomere12", + "antennomere13" + }; + std::unordered_set old_antennomere_l_indices; + for (const std::string& bone_name: antennomere_bone_names) + if (auto it = antennae_skeleton.bone_map.find(bone_name + "_l"); it != antennae_skeleton.bone_map.end()) + old_antennomere_l_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data + antennae_vbo_offset, antennae_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_antennomere_l_indices, antennomere2_l_bone_index, antenna_l_to_body); + std::unordered_set old_antennomere_r_indices; + for (const std::string& bone_name: antennomere_bone_names) + if (auto it = antennae_skeleton.bone_map.find(bone_name + "_r"); it != antennae_skeleton.bone_map.end()) + old_antennomere_r_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data + antennae_vbo_offset, antennae_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_antennomere_r_indices, antennomere2_r_bone_index, antenna_r_to_body); + + // Calculate transform from waist space to body space + math::transform waist_to_body = bind_pose_ss.at(mesosoma_bone) * mesosoma_skeleton.bind_pose.at(mesosoma_skeleton.bone_map.at("petiole")); + + // Reskin waist bones + std::unordered_set old_petiole_bone_indices; + if (auto it = waist_skeleton.bone_map.find("petiole"); it != waist_skeleton.bone_map.end()) + old_petiole_bone_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data + waist_vbo_offset, waist_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_petiole_bone_indices, petiole_bone_index, waist_to_body); + if (postpetiole_present) + { + std::unordered_set old_postpetiole_bone_indices; + if (auto it = waist_skeleton.bone_map.find("postpetiole"); it != waist_skeleton.bone_map.end()) + old_postpetiole_bone_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data + waist_vbo_offset, waist_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_postpetiole_bone_indices, postpetiole_bone_index, waist_to_body); + } + + // Calculate transform from gaster space to body space + math::transform gaster_to_body = bind_pose_ss.at(bone_parent_index(gaster_bone)) * waist_skeleton.bind_pose.at(waist_skeleton.bone_map.at("gaster")); + + // Reskin gaster bones + std::unordered_set old_gaster_bone_indices; + if (auto it = gaster_skeleton.bone_map.find("gaster"); it != gaster_skeleton.bone_map.end()) + old_gaster_bone_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data + gaster_vbo_offset, gaster_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_gaster_bone_indices, gaster_bone_index, gaster_to_body); + + if (sting_present) + { + // Calculate transform from sting space to body space + math::transform sting_to_body = gaster_to_body * gaster_skeleton.bind_pose.at(gaster_skeleton.bone_map.at("sting")); + + // Reskin sting bones + std::unordered_set old_sting_bone_indices; + if (auto it = sting_skeleton->bone_map.find("sting"); it != sting_skeleton->bone_map.end()) + old_sting_bone_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data + sting_vbo_offset, sting_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_sting_bone_indices, sting_bone_index, sting_to_body); + } + + if (eyes_present) + { + // Calculate transforms from eyes space to body space + math::transform eye_l_to_body = bind_pose_ss.at(head_bone) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("eye_l")); + math::transform eye_r_to_body = bind_pose_ss.at(head_bone) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("eye_r")); + + // Reskin eye bones + std::unordered_set old_eye_l_bone_indices; + if (auto it = eyes_skeleton->bone_map.find("eye_l"); it != eyes_skeleton->bone_map.end()) + old_eye_l_bone_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data + eyes_vbo_offset, eyes_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_eye_l_bone_indices, head_bone_index, eye_l_to_body); + std::unordered_set old_eye_r_bone_indices; + if (auto it = eyes_skeleton->bone_map.find("eye_r"); it != eyes_skeleton->bone_map.end()) + old_eye_r_bone_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data + eyes_vbo_offset, eyes_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_eye_r_bone_indices, head_bone_index, eye_r_to_body); + } + + if (lateral_ocelli_present) + { + // Calculate transforms from lateral ocelli space to body space + math::transform ocellus_l_to_body = bind_pose_ss.at(head_bone) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("ocellus_l")); + math::transform ocellus_r_to_body = bind_pose_ss.at(head_bone) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("ocellus_r")); + + // Reskin lateral ocelli bones + std::unordered_set old_ocellus_l_bone_indices; + if (auto it = lateral_ocelli_skeleton->bone_map.find("ocellus_l"); it != lateral_ocelli_skeleton->bone_map.end()) + old_ocellus_l_bone_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data + lateral_ocelli_vbo_offset, lateral_ocelli_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_ocellus_l_bone_indices, head_bone_index, ocellus_l_to_body); + std::unordered_set old_ocellus_r_bone_indices; + if (auto it = lateral_ocelli_skeleton->bone_map.find("ocellus_r"); it != lateral_ocelli_skeleton->bone_map.end()) + old_ocellus_r_bone_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data + lateral_ocelli_vbo_offset, lateral_ocelli_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_ocellus_r_bone_indices, head_bone_index, ocellus_r_to_body); + } + + if (median_ocellus_present) + { + // Calculate transforms from lateral ocelli space to body space + math::transform ocellus_m_to_body = bind_pose_ss.at(head_bone) * head_skeleton.bind_pose.at(head_skeleton.bone_map.at("ocellus_m")); + + // Reskin lateral ocelli bones + std::unordered_set old_ocellus_m_bone_indices; + if (auto it = median_ocellus_skeleton->bone_map.find("ocellus_m"); it != median_ocellus_skeleton->bone_map.end()) + old_ocellus_m_bone_indices.emplace(it->second); + reskin_vertices(vertex_buffer_data + median_ocellus_vbo_offset, median_ocellus_index_count, *position_attribute, *normal_attribute, *tangent_attribute, *bone_index_attribute, old_ocellus_m_bone_indices, head_bone_index, ocellus_m_to_body); + } + + // Upload vertex buffer data to model VBO + model->get_vertex_buffer()->repurpose(gl::buffer_usage::static_draw, vertex_buffer_size, vertex_buffer_data); + + // Construct exoskeleton model group + render::model_group* exoskeleton_group = model->add_group("exoskeleton"); + exoskeleton_group->set_material(exoskeleton_material); + exoskeleton_group->set_drawing_mode(gl::drawing_mode::triangles); + exoskeleton_group->set_start_index(0); + exoskeleton_group->set_index_count(exoskeleton_index_count); + + std::size_t index_offset = exoskeleton_index_count; + if (eyes_present) + { + // Construct eyes model group + render::model_group* eyes_group = model->add_group("eyes"); + eyes_group->set_material((*eyes_model->get_groups())[0]->get_material()); + eyes_group->set_drawing_mode(gl::drawing_mode::triangles); + eyes_group->set_start_index(index_offset); + eyes_group->set_index_count(eyes_index_count); + index_offset += eyes_index_count; + } + + if (lateral_ocelli_present || median_ocellus_present) + { + // Construct ocelli model group + render::model_group* ocelli_group = model->add_group("ocelli"); + ocelli_group->set_drawing_mode(gl::drawing_mode::triangles); + ocelli_group->set_start_index(index_offset); + + std::size_t index_count = 0; + if (lateral_ocelli_present) + { + index_count += lateral_ocelli_index_count; + index_offset += lateral_ocelli_index_count; + ocelli_group->set_material((*lateral_ocelli_model->get_groups())[0]->get_material()); + } + if (median_ocellus_present) + { + index_count += median_ocellus_index_count; + index_offset += median_ocellus_index_count; + if (!lateral_ocelli_present) + ocelli_group->set_material((*median_ocellus_model->get_groups())[0]->get_material()); + } + + ocelli_group->set_index_count(index_count); + } + + if (wings_present) + { + // Construct forewings model group + render::model_group* forewings_group = model->add_group("forewings"); + forewings_group->set_material((*forewings_model->get_groups())[0]->get_material()); + forewings_group->set_drawing_mode(gl::drawing_mode::triangles); + forewings_group->set_start_index(index_offset); + forewings_group->set_index_count(forewings_index_count); + index_offset += forewings_index_count; + + // Construct hindwings model group + render::model_group* hindwings_group = model->add_group("hindwings"); + hindwings_group->set_material((*hindwings_model->get_groups())[0]->get_material()); + hindwings_group->set_drawing_mode(gl::drawing_mode::triangles); + hindwings_group->set_start_index(index_offset); + hindwings_group->set_index_count(hindwings_index_count); + index_offset += hindwings_index_count; + } + + // Calculate model bounding box + geom::aabb bounds = calculate_bounds(vertex_buffer_data, index_offset, *position_attribute); + model->set_bounds(bounds); + + // Free vertex buffer data + delete[] vertex_buffer_data; + + return model; +} + +void reskin_vertices +( + std::uint8_t* vertex_data, + std::size_t index_count, + const gl::vertex_attribute& position_attribute, + const gl::vertex_attribute& normal_attribute, + const gl::vertex_attribute& tangent_attribute, + const gl::vertex_attribute& bone_index_attribute, + const std::unordered_set& old_bone_indices, + std::uint8_t new_bone_index, + const math::transform& transform +) +{ + std::uint8_t* position_data = vertex_data + position_attribute.offset; + std::uint8_t* normal_data = vertex_data + normal_attribute.offset; + std::uint8_t* tangent_data = vertex_data + tangent_attribute.offset; + std::uint8_t* bone_index_data = vertex_data + bone_index_attribute.offset; + + for (std::size_t i = 0; i < index_count; ++i) + { + // Get bone index of current vertex + float* bone_index = reinterpret_cast(bone_index_data + bone_index_attribute.stride * i); + + // Skip irrelevant bones + if (!old_bone_indices.count(static_cast(*bone_index + 0.5f))) + continue; + + // Get vertex position + float* x = reinterpret_cast(position_data + position_attribute.stride * i); + float* y = x + 1; + float* z = y + 1; + + // Get vertex normal + float* nx = reinterpret_cast(normal_data + normal_attribute.stride * i); + float* ny = nx + 1; + float* nz = ny + 1; + + // Get vertex tangent + float* tx = reinterpret_cast(tangent_data + tangent_attribute.stride * i); + float* ty = tx + 1; + float* tz = ty + 1; + //float* bts = tz + 1; + + // Transform vertex attributes + float3 position = transform * float3{*x, *y, *z}; + float3 normal = math::normalize(transform.rotation * float3{*nx, *ny, *nz}); + float3 tangent = transform.rotation * float3{*tx, *ty, *tz}; + + // Update vertex data + *x = position.x(); + *y = position.y(); + *z = position.z(); + *nx = normal.x(); + *ny = normal.y(); + *nz = normal.z(); + *tx = tangent.x(); + *ty = tangent.y(); + *tz = tangent.z(); + //*bts = ... + *bone_index = static_cast(new_bone_index); + } +} + +geom::aabb calculate_bounds(std::uint8_t* vertex_data, std::size_t index_count, const gl::vertex_attribute& position_attribute) +{ + std::uint8_t* position_data = vertex_data + position_attribute.offset; + + geom::aabb bounds; + bounds.min_point.x() = std::numeric_limits::infinity(); + bounds.min_point.y() = std::numeric_limits::infinity(); + bounds.min_point.z() = std::numeric_limits::infinity(); + bounds.max_point.x() = -std::numeric_limits::infinity(); + bounds.max_point.y() = -std::numeric_limits::infinity(); + bounds.max_point.z() = -std::numeric_limits::infinity(); + + for (std::size_t i = 0; i < index_count; ++i) + { + // Get vertex position + float* x = reinterpret_cast(position_data + position_attribute.stride * i); + float* y = x + 1; + float* z = y + 1; + + bounds.min_point.x() = std::min(*x, bounds.min_point.x()); + bounds.min_point.y() = std::min(*y, bounds.min_point.y()); + bounds.min_point.z() = std::min(*z, bounds.min_point.z()); + bounds.max_point.x() = std::max(*x, bounds.max_point.x()); + bounds.max_point.y() = std::max(*y, bounds.max_point.y()); + bounds.max_point.z() = std::max(*z, bounds.max_point.z()); + } + + return bounds; +} + +} // namespace ant diff --git a/src/game/ant/morphogenesis.hpp b/src/game/ant/morphogenesis.hpp index 3171ad0..94d4fb5 100644 --- a/src/game/ant/morphogenesis.hpp +++ b/src/game/ant/morphogenesis.hpp @@ -21,9 +21,8 @@ #define ANTKEEPER_GAME_ANT_MORPHOGENESIS_HPP #include "game/ant/phenome.hpp" -#include "render/model.hpp" +#include -namespace game { namespace ant { /** @@ -36,6 +35,5 @@ namespace ant { render::model* morphogenesis(const phenome& phenome); } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_MORPHOGENESIS_HPP diff --git a/src/game/ant/phene/antennae.hpp b/src/game/ant/phene/antennae.hpp index 2bd29ea..4fce8bc 100644 --- a/src/game/ant/phene/antennae.hpp +++ b/src/game/ant/phene/antennae.hpp @@ -20,9 +20,8 @@ #ifndef ANTKEEPER_GAME_ANT_PHENE_ANTENNAE_HPP #define ANTKEEPER_GAME_ANT_PHENE_ANTENNAE_HPP -#include "render/model.hpp" +#include -namespace game { namespace ant { namespace phene { @@ -46,6 +45,5 @@ struct antennae } // namespace phene } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_PHENE_ANTENNAE_HPP diff --git a/src/game/ant/phene/body-size.hpp b/src/game/ant/phene/body-size.hpp index d369dee..383550e 100644 --- a/src/game/ant/phene/body-size.hpp +++ b/src/game/ant/phene/body-size.hpp @@ -20,9 +20,8 @@ #ifndef ANTKEEPER_GAME_ANT_PHENE_BODY_SIZE_HPP #define ANTKEEPER_GAME_ANT_PHENE_BODY_SIZE_HPP -#include "render/model.hpp" +#include -namespace game { namespace ant { namespace phene { @@ -45,6 +44,5 @@ struct body_size } // namespace phene } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_PHENE_BODY_SIZE_HPP diff --git a/src/game/ant/phene/cocoon.hpp b/src/game/ant/phene/cocoon.hpp index 7c4f1d2..c740db4 100644 --- a/src/game/ant/phene/cocoon.hpp +++ b/src/game/ant/phene/cocoon.hpp @@ -20,9 +20,8 @@ #ifndef ANTKEEPER_GAME_ANT_PHENE_COCOON_HPP #define ANTKEEPER_GAME_ANT_PHENE_COCOON_HPP -#include "render/model.hpp" +#include -namespace game { namespace ant { namespace phene { @@ -40,6 +39,5 @@ struct cocoon } // namespace phene } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_PHENE_COCOON_HPP diff --git a/src/game/ant/phene/diet.hpp b/src/game/ant/phene/diet.hpp index ecf0a1a..86084a6 100644 --- a/src/game/ant/phene/diet.hpp +++ b/src/game/ant/phene/diet.hpp @@ -20,7 +20,6 @@ #ifndef ANTKEEPER_GAME_ANT_PHENE_DIET_HPP #define ANTKEEPER_GAME_ANT_PHENE_DIET_HPP -namespace game { namespace ant { namespace phene { @@ -50,6 +49,5 @@ struct diet } // namespace phene } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_PHENE_DIET_HPP diff --git a/src/game/ant/phene/egg.hpp b/src/game/ant/phene/egg.hpp index 6fa82a3..c9536b1 100644 --- a/src/game/ant/phene/egg.hpp +++ b/src/game/ant/phene/egg.hpp @@ -20,9 +20,8 @@ #ifndef ANTKEEPER_GAME_ANT_PHENE_EGG_HPP #define ANTKEEPER_GAME_ANT_PHENE_EGG_HPP -#include "render/model.hpp" +#include -namespace game { namespace ant { namespace phene { @@ -37,6 +36,5 @@ struct egg } // namespace phene } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_PHENE_EGG_HPP diff --git a/src/game/ant/phene/eyes.hpp b/src/game/ant/phene/eyes.hpp index 544c56e..a8193b2 100644 --- a/src/game/ant/phene/eyes.hpp +++ b/src/game/ant/phene/eyes.hpp @@ -20,9 +20,8 @@ #ifndef ANTKEEPER_GAME_ANT_PHENE_EYES_HPP #define ANTKEEPER_GAME_ANT_PHENE_EYES_HPP -#include "render/model.hpp" +#include -namespace game { namespace ant { namespace phene { @@ -54,6 +53,5 @@ struct eyes } // namespace phene } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_PHENE_EYES_HPP diff --git a/src/game/ant/phene/foraging-time.hpp b/src/game/ant/phene/foraging-time.hpp index 21192ae..108c7a5 100644 --- a/src/game/ant/phene/foraging-time.hpp +++ b/src/game/ant/phene/foraging-time.hpp @@ -20,9 +20,8 @@ #ifndef ANTKEEPER_GAME_ANT_PHENE_FORAGING_TIME_HPP #define ANTKEEPER_GAME_ANT_PHENE_FORAGING_TIME_HPP -#include "render/model.hpp" +#include -namespace game { namespace ant { namespace phene { @@ -40,6 +39,5 @@ struct foraging_time } // namespace phene } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_PHENE_FORAGING_TIME_HPP diff --git a/src/game/ant/phene/founding-mode.hpp b/src/game/ant/phene/founding-mode.hpp index e22de82..f3aefcd 100644 --- a/src/game/ant/phene/founding-mode.hpp +++ b/src/game/ant/phene/founding-mode.hpp @@ -20,7 +20,6 @@ #ifndef ANTKEEPER_GAME_ANT_PHENE_FOUNDING_MODE_HPP #define ANTKEEPER_GAME_ANT_PHENE_FOUNDING_MODE_HPP -namespace game { namespace ant { namespace phene { @@ -43,6 +42,5 @@ enum class founding_mode } // namespace phene } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_PHENE_FOUNDING_MODE_HPP diff --git a/src/game/ant/phene/gaster.hpp b/src/game/ant/phene/gaster.hpp index 170794b..be06958 100644 --- a/src/game/ant/phene/gaster.hpp +++ b/src/game/ant/phene/gaster.hpp @@ -20,9 +20,8 @@ #ifndef ANTKEEPER_GAME_ANT_PHENE_GASTER_HPP #define ANTKEEPER_GAME_ANT_PHENE_GASTER_HPP -#include "render/model.hpp" +#include -namespace game { namespace ant { namespace phene { @@ -42,6 +41,5 @@ struct gaster } // namespace phene } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_PHENE_GASTER_HPP diff --git a/src/game/ant/phene/head.hpp b/src/game/ant/phene/head.hpp index 79f24b3..d7ed417 100644 --- a/src/game/ant/phene/head.hpp +++ b/src/game/ant/phene/head.hpp @@ -20,9 +20,8 @@ #ifndef ANTKEEPER_GAME_ANT_PHENE_HEAD_HPP #define ANTKEEPER_GAME_ANT_PHENE_HEAD_HPP -#include "render/model.hpp" +#include -namespace game { namespace ant { namespace phene { @@ -49,6 +48,5 @@ struct head } // namespace phene } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_PHENE_HEAD_HPP diff --git a/src/game/ant/phene/larva.hpp b/src/game/ant/phene/larva.hpp index 3839525..5f9745e 100644 --- a/src/game/ant/phene/larva.hpp +++ b/src/game/ant/phene/larva.hpp @@ -20,9 +20,8 @@ #ifndef ANTKEEPER_GAME_ANT_PHENE_LARVA_HPP #define ANTKEEPER_GAME_ANT_PHENE_LARVA_HPP -#include "render/model.hpp" +#include -namespace game { namespace ant { namespace phene { @@ -40,6 +39,5 @@ struct larva } // namespace phene } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_PHENE_LARVA_HPP diff --git a/src/game/ant/phene/legs.hpp b/src/game/ant/phene/legs.hpp index 7bbaabd..ad40bf3 100644 --- a/src/game/ant/phene/legs.hpp +++ b/src/game/ant/phene/legs.hpp @@ -20,9 +20,8 @@ #ifndef ANTKEEPER_GAME_ANT_PHENE_LEGS_HPP #define ANTKEEPER_GAME_ANT_PHENE_LEGS_HPP -#include "render/model.hpp" +#include -namespace game { namespace ant { namespace phene { @@ -45,6 +44,5 @@ struct legs } // namespace phene } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_PHENE_LEGS_HPP diff --git a/src/game/ant/phene/mandibles.hpp b/src/game/ant/phene/mandibles.hpp index 2e46387..88100ea 100644 --- a/src/game/ant/phene/mandibles.hpp +++ b/src/game/ant/phene/mandibles.hpp @@ -20,9 +20,8 @@ #ifndef ANTKEEPER_GAME_ANT_PHENE_MANDIBLES_HPP #define ANTKEEPER_GAME_ANT_PHENE_MANDIBLES_HPP -#include "render/model.hpp" +#include -namespace game { namespace ant { namespace phene { @@ -50,6 +49,5 @@ struct mandibles } // namespace phene } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_PHENE_MANDIBLES_HPP diff --git a/src/game/ant/phene/mesosoma.hpp b/src/game/ant/phene/mesosoma.hpp index c394d71..dcebb4f 100644 --- a/src/game/ant/phene/mesosoma.hpp +++ b/src/game/ant/phene/mesosoma.hpp @@ -20,9 +20,8 @@ #ifndef ANTKEEPER_GAME_ANT_PHENE_MESOSOMA_HPP #define ANTKEEPER_GAME_ANT_PHENE_MESOSOMA_HPP -#include "render/model.hpp" +#include -namespace game { namespace ant { namespace phene { @@ -51,6 +50,5 @@ struct mesosoma } // namespace phene } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_PHENE_MESOSOMA_HPP diff --git a/src/game/ant/phene/nest-site.hpp b/src/game/ant/phene/nest-site.hpp index 7fba179..33759f9 100644 --- a/src/game/ant/phene/nest-site.hpp +++ b/src/game/ant/phene/nest-site.hpp @@ -20,7 +20,6 @@ #ifndef ANTKEEPER_GAME_ANT_NEST_SITE_HPP #define ANTKEEPER_GAME_ANT_NEST_SITE_HPP -namespace game { namespace ant { namespace phene { @@ -35,6 +34,5 @@ enum class nest_site } // namespace phene } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_NEST_SITE_HPP diff --git a/src/game/ant/phene/ocelli.hpp b/src/game/ant/phene/ocelli.hpp index e8f265b..157f208 100644 --- a/src/game/ant/phene/ocelli.hpp +++ b/src/game/ant/phene/ocelli.hpp @@ -20,9 +20,8 @@ #ifndef ANTKEEPER_GAME_ANT_PHENE_OCELLI_HPP #define ANTKEEPER_GAME_ANT_PHENE_OCELLI_HPP -#include "render/model.hpp" +#include -namespace game { namespace ant { namespace phene { @@ -52,6 +51,5 @@ struct ocelli } // namespace phene } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_PHENE_OCELLI_HPP diff --git a/src/game/ant/phene/pigmentation.hpp b/src/game/ant/phene/pigmentation.hpp index 86ea1c6..1efee8c 100644 --- a/src/game/ant/phene/pigmentation.hpp +++ b/src/game/ant/phene/pigmentation.hpp @@ -20,9 +20,8 @@ #ifndef ANTKEEPER_GAME_ANT_PHENE_PIGMENTATION_HPP #define ANTKEEPER_GAME_ANT_PHENE_PIGMENTATION_HPP -#include "render/material.hpp" +#include -namespace game { namespace ant { namespace phene { @@ -37,6 +36,5 @@ struct pigmentation } // namespace phene } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_PHENE_PIGMENTATION_HPP diff --git a/src/game/ant/phene/pilosity.hpp b/src/game/ant/phene/pilosity.hpp index 457dfb3..0ee6b5b 100644 --- a/src/game/ant/phene/pilosity.hpp +++ b/src/game/ant/phene/pilosity.hpp @@ -20,7 +20,6 @@ #ifndef ANTKEEPER_GAME_ANT_PHENE_PILOSITY_HPP #define ANTKEEPER_GAME_ANT_PHENE_PILOSITY_HPP -namespace game { namespace ant { namespace phene { @@ -35,6 +34,5 @@ struct pilosity } // namespace phene } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_PHENE_PILOSITY_HPP diff --git a/src/game/ant/phene/sculpturing.hpp b/src/game/ant/phene/sculpturing.hpp index 482094c..efc89d9 100644 --- a/src/game/ant/phene/sculpturing.hpp +++ b/src/game/ant/phene/sculpturing.hpp @@ -20,9 +20,8 @@ #ifndef ANTKEEPER_GAME_ANT_PHENE_SCULPTURING_HPP #define ANTKEEPER_GAME_ANT_PHENE_SCULPTURING_HPP -#include "gl/texture-2d.hpp" +#include -namespace game { namespace ant { namespace phene { @@ -40,6 +39,5 @@ struct sculpturing } // namespace phene } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_PHENE_SCULPTURING_HPP diff --git a/src/game/ant/phene/sting.hpp b/src/game/ant/phene/sting.hpp index a7cc144..0f95cc0 100644 --- a/src/game/ant/phene/sting.hpp +++ b/src/game/ant/phene/sting.hpp @@ -20,9 +20,8 @@ #ifndef ANTKEEPER_GAME_ANT_PHENE_STING_HPP #define ANTKEEPER_GAME_ANT_PHENE_STING_HPP -#include "render/model.hpp" +#include -namespace game { namespace ant { namespace phene { @@ -40,6 +39,5 @@ struct sting } // namespace phene } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_PHENE_STING_HPP diff --git a/src/game/ant/phene/waist.hpp b/src/game/ant/phene/waist.hpp index e0082e8..117db76 100644 --- a/src/game/ant/phene/waist.hpp +++ b/src/game/ant/phene/waist.hpp @@ -20,9 +20,8 @@ #ifndef ANTKEEPER_GAME_ANT_PHENE_WAIST_HPP #define ANTKEEPER_GAME_ANT_PHENE_WAIST_HPP -#include "render/model.hpp" +#include -namespace game { namespace ant { namespace phene { @@ -69,6 +68,5 @@ struct waist } // namespace phene } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_PHENE_WAIST_HPP diff --git a/src/game/ant/phene/wings.hpp b/src/game/ant/phene/wings.hpp index 5c2c62c..a497979 100644 --- a/src/game/ant/phene/wings.hpp +++ b/src/game/ant/phene/wings.hpp @@ -20,9 +20,8 @@ #ifndef ANTKEEPER_GAME_ANT_PHENE_WINGS_HPP #define ANTKEEPER_GAME_ANT_PHENE_WINGS_HPP -#include "render/model.hpp" +#include -namespace game { namespace ant { namespace phene { @@ -61,6 +60,5 @@ struct wings } // namespace phene } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_PHENE_WINGS_HPP diff --git a/src/game/ant/phenome.cpp b/src/game/ant/phenome.cpp index 69cdecd..dd234af 100644 --- a/src/game/ant/phenome.cpp +++ b/src/game/ant/phenome.cpp @@ -19,7 +19,6 @@ #include "game/ant/phenome.hpp" -namespace game { namespace ant { phenome::phenome(const genome& genome, caste caste): @@ -112,4 +111,3 @@ phenome::phenome(): {} } // namespace ant -} // namespace game diff --git a/src/game/ant/phenome.hpp b/src/game/ant/phenome.hpp index dfc1420..9902466 100644 --- a/src/game/ant/phenome.hpp +++ b/src/game/ant/phenome.hpp @@ -45,7 +45,6 @@ #include "game/ant/genome.hpp" #include "game/ant/caste.hpp" -namespace game { namespace ant { /** @@ -89,6 +88,5 @@ struct phenome }; } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_PHENOME_HPP diff --git a/src/game/ant/species.hpp b/src/game/ant/species.hpp index 4522af9..b87a2a1 100644 --- a/src/game/ant/species.hpp +++ b/src/game/ant/species.hpp @@ -22,10 +22,9 @@ #include "game/ant/caste.hpp" #include "game/ant/phenome.hpp" -#include "render/model.hpp" +#include #include -namespace game { namespace ant { struct species @@ -38,6 +37,5 @@ struct species }; } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_SPECIES_HPP diff --git a/src/game/ant/subcaste.hpp b/src/game/ant/subcaste.hpp index 3c02fbf..898c2c8 100644 --- a/src/game/ant/subcaste.hpp +++ b/src/game/ant/subcaste.hpp @@ -20,7 +20,6 @@ #ifndef ANTKEEPER_GAME_ANT_SUBCASTE_HPP #define ANTKEEPER_GAME_ANT_SUBCASTE_HPP -namespace game { namespace ant { /** @@ -54,6 +53,5 @@ enum class subcaste }; } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_SUBCASTE_HPP diff --git a/src/game/ant/swarm.cpp b/src/game/ant/swarm.cpp index a6a169f..055ac0a 100644 --- a/src/game/ant/swarm.cpp +++ b/src/game/ant/swarm.cpp @@ -1,170 +1,179 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "game/ant/swarm.hpp" -#include "game/component/transform.hpp" -#include "game/component/steering.hpp" -#include "game/component/model.hpp" -#include "game/component/picking.hpp" -#include "resources/resource-manager.hpp" -#include "math/quaternion.hpp" -#include "config.hpp" -#include -#include - -namespace game { -namespace ant { - -/** - * Generates a random point in a unit sphere. - * - * @see https://math.stackexchange.com/questions/87230/picking-random-points-in-the-volume-of-sphere-with-uniform-probability/87238#87238 - */ -template -static math::vector3 sphere_random(Generator& rng) -{ - std::uniform_real_distribution distribution(T{-1}, T{1}); - - math::vector3 position; - for (std::size_t i = 0; i < 3; ++i) - position[i] = distribution(rng); - - return math::normalize(position) * std::cbrt(distribution(rng)); -} - -entity::id create_swarm(game::context& ctx) -{ - // Determine swarm properties - const float3 swarm_center = {0, 100, 0}; - const float swarm_radius = 25.0f; - const std::size_t male_count = 50; - const std::size_t queen_count = 50; - const std::size_t alate_count = male_count + queen_count; - - const float3 male_scale = {0.5, 0.5, 0.5}; - const float3 queen_scale = {1, 1, 1}; - - // Init transform component - game::component::transform transform; - transform.local = math::transform::identity; - transform.world = transform.local; - transform.warp = true; - - // Init picking component - game::component::picking picking; - picking.sphere = {float3{0, 0, 0}, 0.5f * 2.0f}; - std::uint32_t male_picking_flags = 0b01; - std::uint32_t queen_picking_flags = 0b10; - - // Create swarm entity - entity::id swarm_eid = ctx.entity_registry->create(); - transform.local.translation = swarm_center; - transform.world = transform.local; - transform.warp = true; - ctx.entity_registry->emplace(swarm_eid, transform); - - // Init male model component - game::component::model male_model; - male_model.render_model = ctx.resource_manager->load("male-boid.mdl"); - male_model.instance_count = 0; - male_model.layers = 1; - - // Init queen model component - game::component::model queen_model; - queen_model.render_model = ctx.resource_manager->load("queen-boid.mdl"); - queen_model.instance_count = 0; - queen_model.layers = 1; - - // Init steering component - game::component::steering steering; - steering.agent.mass = 1.0f; - steering.agent.velocity = {0, 0, 0}; - steering.agent.acceleration = {0, 0, 0}; - steering.agent.max_force = 4.0f; - steering.agent.max_speed = 5.0f; - steering.agent.max_speed_squared = steering.agent.max_speed * steering.agent.max_speed; - steering.agent.orientation = math::quaternion::identity(); - steering.agent.forward = steering.agent.orientation * config::global_forward; - steering.agent.up = steering.agent.orientation * config::global_up; - steering.wander_weight = 1.0f; - steering.wander_noise = math::radians(2000.0f); - steering.wander_distance = 10.0f; - steering.wander_radius = 8.0f; - steering.wander_angle = 0.0f; - steering.wander_angle2 = 0.0f; - steering.seek_weight = 0.2f; - steering.seek_target = swarm_center; - steering.flee_weight = 0.0f; - steering.sum_weights = steering.wander_weight + steering.seek_weight + steering.flee_weight; - - // Construct and seed random number generator - std::random_device seed; - std::mt19937 rng(seed()); - - // Create alates - for (std::size_t i = 0; i < alate_count; ++i) - { - // Generate random position in swarm sphere - steering.agent.position = swarm_center + sphere_random(rng) * swarm_radius; - transform.local.translation = steering.agent.position; - - entity::id alate_eid = ctx.entity_registry->create(); - ctx.entity_registry->emplace(alate_eid, steering); - - if (i < male_count) - { - // Create male - ctx.entity_registry->emplace(alate_eid, male_model); - - transform.local.scale = male_scale; - transform.world = transform.local; - ctx.entity_registry->emplace(alate_eid, transform); - - picking.flags = male_picking_flags; - ctx.entity_registry->emplace(alate_eid, picking); - } - else - { - // Create queen - ctx.entity_registry->emplace(alate_eid, queen_model); - - transform.local.scale = queen_scale; - transform.world = transform.local; - ctx.entity_registry->emplace(alate_eid, transform); - - picking.flags = queen_picking_flags; - ctx.entity_registry->emplace(alate_eid, picking); - } - } - - return swarm_eid; -} - -void destroy_swarm(game::context& ctx, entity::id swarm_eid) -{ - // Destroy alates - auto view = ctx.entity_registry->view(); - ctx.entity_registry->destroy(view.begin(), view.end()); - - // Destroy swarm - ctx.entity_registry->destroy(swarm_eid); -} - -} // namespace ant -} // namespace game +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include "game/ant/swarm.hpp" +#include "game/components/transform-component.hpp" +#include "game/components/steering-component.hpp" +#include "game/components/model-component.hpp" +#include "game/components/picking-component.hpp" +#include "game/components/caste-component.hpp" +#include +#include +#include +#include +#include + +namespace ant { + +/** + * Generates a random point in a unit sphere. + * + * @see https://math.stackexchange.com/questions/87230/picking-random-points-in-the-volume-of-sphere-with-uniform-probability/87238#87238 + */ +template +static math::vector3 sphere_random(Generator& rng) +{ + std::uniform_real_distribution distribution(T{-1}, T{1}); + + math::vector3 position; + for (std::size_t i = 0; i < 3; ++i) + position[i] = distribution(rng); + + return math::normalize(position) * std::cbrt(distribution(rng)); +} + +entity::id create_swarm(::context& ctx) +{ + // Determine swarm properties + const float3 swarm_center = {0, 100, 0}; + const float swarm_radius = 25.0f; + const std::size_t male_count = 50; + const std::size_t queen_count = 50; + const std::size_t alate_count = male_count + queen_count; + + const float3 male_scale = {0.5, 0.5, 0.5}; + const float3 queen_scale = {1, 1, 1}; + + // Init transform component + ::transform_component transform; + transform.local = math::transform::identity; + transform.world = transform.local; + transform.warp = true; + + // Init picking component + ::picking_component picking; + picking.sphere = {float3{0, 0, 0}, 0.5f * 2.0f}; + std::uint32_t male_picking_flags = 0b01; + std::uint32_t queen_picking_flags = 0b10; + + // Create swarm entity + entity::id swarm_eid = ctx.entity_registry->create(); + transform.local.translation = swarm_center; + transform.world = transform.local; + transform.warp = true; + ctx.entity_registry->emplace<::transform_component>(swarm_eid, transform); + + // Init male model component + ::model_component male_model; + male_model.render_model = ctx.resource_manager->load("male-boid.mdl"); + male_model.instance_count = 0; + male_model.layers = 1; + + // Init queen model component + ::model_component queen_model; + queen_model.render_model = ctx.resource_manager->load("queen-boid.mdl"); + queen_model.instance_count = 0; + queen_model.layers = 1; + + // Init steering component + ::steering_component steering; + steering.agent.mass = 1.0f; + steering.agent.velocity = {0, 0, 0}; + steering.agent.acceleration = {0, 0, 0}; + steering.agent.max_force = 4.0f; + steering.agent.max_speed = 5.0f; + steering.agent.max_speed_squared = steering.agent.max_speed * steering.agent.max_speed; + steering.agent.orientation = math::quaternion::identity(); + steering.agent.forward = steering.agent.orientation * config::global_forward; + steering.agent.up = steering.agent.orientation * config::global_up; + steering.wander_weight = 1.0f; + steering.wander_noise = math::radians(2000.0f); + steering.wander_distance = 10.0f; + steering.wander_radius = 8.0f; + steering.wander_angle = 0.0f; + steering.wander_angle2 = 0.0f; + steering.seek_weight = 0.2f; + steering.seek_target = swarm_center; + steering.flee_weight = 0.0f; + steering.sum_weights = steering.wander_weight + steering.seek_weight + steering.flee_weight; + + // Init queen caste component + ::caste_component queen_caste; + queen_caste.type = ::ant::caste::queen; + + // Init male caste component + ::caste_component male_caste; + male_caste.type = ::ant::caste::male; + + // Construct and seed random number generator + std::random_device seed; + std::mt19937 rng(seed()); + + // Create alates + for (std::size_t i = 0; i < alate_count; ++i) + { + // Generate random position in swarm sphere + steering.agent.position = swarm_center + sphere_random(rng) * swarm_radius; + transform.local.translation = steering.agent.position; + + entity::id alate_eid = ctx.entity_registry->create(); + ctx.entity_registry->emplace<::steering_component>(alate_eid, steering); + + if (i < male_count) + { + // Create male + ctx.entity_registry->emplace<::caste_component>(alate_eid, male_caste); + ctx.entity_registry->emplace<::model_component>(alate_eid, male_model); + + transform.local.scale = male_scale; + transform.world = transform.local; + ctx.entity_registry->emplace<::transform_component>(alate_eid, transform); + + picking.flags = male_picking_flags; + ctx.entity_registry->emplace<::picking_component>(alate_eid, picking); + } + else + { + // Create queen + ctx.entity_registry->emplace<::caste_component>(alate_eid, queen_caste); + ctx.entity_registry->emplace<::model_component>(alate_eid, queen_model); + + transform.local.scale = queen_scale; + transform.world = transform.local; + ctx.entity_registry->emplace<::transform_component>(alate_eid, transform); + + picking.flags = queen_picking_flags; + ctx.entity_registry->emplace<::picking_component>(alate_eid, picking); + } + } + + return swarm_eid; +} + +void destroy_swarm(::context& ctx, entity::id swarm_eid) +{ + // Destroy alates + auto view = ctx.entity_registry->view<::steering_component>(); + ctx.entity_registry->destroy(view.begin(), view.end()); + + // Destroy swarm + ctx.entity_registry->destroy(swarm_eid); +} + +} // namespace ant diff --git a/src/game/ant/swarm.hpp b/src/game/ant/swarm.hpp index 796edff..544ed56 100644 --- a/src/game/ant/swarm.hpp +++ b/src/game/ant/swarm.hpp @@ -21,15 +21,13 @@ #define ANTKEEPER_GAME_ANT_SWARM_HPP #include "game/context.hpp" -#include "entity/id.hpp" +#include -namespace game { namespace ant { -entity::id create_swarm(game::context& ctx); -void destroy_swarm(game::context& ctx, entity::id swarm_eid); +entity::id create_swarm(::context& ctx); +void destroy_swarm(::context& ctx, entity::id swarm_eid); } // namespace ant -} // namespace game #endif // ANTKEEPER_GAME_ANT_SWARM_HPP diff --git a/src/game/commands/commands.cpp b/src/game/commands/commands.cpp new file mode 100644 index 0000000..cb585fd --- /dev/null +++ b/src/game/commands/commands.cpp @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include "game/commands/commands.hpp" +#include "game/components/model-component.hpp" +#include "game/components/transform-component.hpp" +#include "game/components/celestial-body-component.hpp" +#include "game/components/terrain-component.hpp" +#include +#include + +namespace command { + +void translate(entity::registry& registry, entity::id eid, const float3& translation) +{ + const ::transform_component* transform = registry.try_get<::transform_component>(eid); + if (transform) + { + registry.patch<::transform_component> + ( + eid, + [&translation](auto& transform) + { + transform.local.translation += translation; + } + ); + } +} + +void rotate(entity::registry& registry, entity::id eid, float angle, const float3& axis) +{ + const ::transform_component* transform = registry.try_get<::transform_component>(eid); + if (transform) + { + registry.patch<::transform_component> + ( + eid, + [angle, &axis](auto& transform) + { + transform.local.rotation = math::normalize(math::angle_axis(angle, axis) * transform.local.rotation); + } + ); + } +} + +void move_to(entity::registry& registry, entity::id eid, const float3& position) +{ + const ::transform_component* transform = registry.try_get<::transform_component>(eid); + if (transform) + { + registry.patch<::transform_component> + ( + eid, + [&position](auto& transform) + { + transform.local.translation = position; + } + ); + } +} + +void warp_to(entity::registry& registry, entity::id eid, const float3& position) +{ + const ::transform_component* transform = registry.try_get<::transform_component>(eid); + if (transform) + { + registry.patch<::transform_component> + ( + eid, + [&position](auto& transform) + { + transform.local.translation = position; + transform.warp = true; + } + ); + } +} + +void set_scale(entity::registry& registry, entity::id eid, const float3& scale) +{ + const ::transform_component* transform = registry.try_get<::transform_component>(eid); + if (transform) + { + registry.patch<::transform_component> + ( + eid, + [&scale](auto& transform) + { + transform.local.scale = scale; + } + ); + } +} + +void set_transform(entity::registry& registry, entity::id eid, const math::transform& transform, bool warp) +{ + const ::transform_component* transform_component = registry.try_get<::transform_component>(eid); + if (transform_component) + { + registry.patch<::transform_component> + ( + eid, + [&other_transform = transform, warp](auto& transform) + { + transform.local = other_transform; + transform.warp = warp; + } + ); + } +} + +void place(entity::registry& registry, entity::id eid, entity::id celestial_body_id, double altitude, double latitude, double longitude) +{ + +} + +void assign_render_layers(entity::registry& registry, entity::id eid, unsigned int layers) +{ + const ::model_component* model = registry.try_get<::model_component>(eid); + if (model) + { + registry.patch<::model_component> + ( + eid, + [layers](auto& model) + { + model.layers = layers; + } + ); + } +} + +math::transform get_local_transform(entity::registry& registry, entity::id eid) +{ + const ::transform_component* transform = registry.try_get<::transform_component>(eid); + if (transform) + { + return transform->local; + } + + return math::transform::identity; +} + +math::transform get_world_transform(entity::registry& registry, entity::id eid) +{ + const ::transform_component* transform = registry.try_get<::transform_component>(eid); + if (transform) + { + return transform->world; + } + + return math::transform::identity; +} + +} // namespace command diff --git a/src/game/commands/commands.hpp b/src/game/commands/commands.hpp new file mode 100644 index 0000000..abd7a07 --- /dev/null +++ b/src/game/commands/commands.hpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_COMMANDS_HPP +#define ANTKEEPER_GAME_COMMANDS_HPP + +#include +#include +#include +#include + + +/// Commands which operate on entity::id components +namespace command { + +void translate(entity::registry& registry, entity::id eid, const float3& translation); +void rotate(entity::registry& registry, entity::id eid, float angle, const float3& axis); +void move_to(entity::registry& registry, entity::id eid, const float3& position); +void warp_to(entity::registry& registry, entity::id eid, const float3& position); +void set_scale(entity::registry& registry, entity::id eid, const float3& scale); +void set_transform(entity::registry& registry, entity::id eid, const math::transform& transform, bool warp = false); +void place(entity::registry& registry, entity::id eid, entity::id celestial_body_id, double altitude, double latitude, double longitude); +void assign_render_layers(entity::registry& registry, entity::id eid, unsigned int layers); +math::transform get_local_transform(entity::registry& registry, entity::id eid); +math::transform get_world_transform(entity::registry& registry, entity::id eid); + +} // namespace command + +#endif // ANTKEEPER_GAME_COMMANDS_HPP + diff --git a/src/game/component/atmosphere.hpp b/src/game/component/atmosphere.hpp deleted file mode 100644 index 51ee796..0000000 --- a/src/game/component/atmosphere.hpp +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_COMPONENT_ATMOSPHERE_HPP -#define ANTKEEPER_GAME_COMPONENT_ATMOSPHERE_HPP - -#include "utility/fundamental-types.hpp" - -namespace game { -namespace component { - -/// Atmosphere -struct atmosphere -{ - /// Elevation of the upper limit of the atmosphere, in meters. - double upper_limit; - - /// Index of refraction of air at sea level. - double index_of_refraction; - - /// Molar concentration of Rayleigh particles at sea level, in mol/m-3. - double rayleigh_concentration; - - /// Scale height of the exponential distribution of Rayleigh particles, in meters. - double rayleigh_scale_height; - - /// (Dependent) Rayleigh scattering coefficients. - double3 rayleigh_scattering; - - /// Molar concentration of Mie particles at sea level, in mol/m-3. - double mie_concentration; - - /// Scale height of the exponential distribution of Mie particles, in meters. - double mie_scale_height; - - /// Mie phase function anisotropy factor. - double mie_anisotropy; - - /// Mie single-scattering albedo. - double mie_albedo; - - /// (Dependent) Mie scattering coefficient. - double mie_scattering; - - /// (Dependent) Mie extinction coefficient. - double mie_extinction; - - /// Concentration of ozone in the atmosphere, unitless. - double ozone_concentration; - - /// Elevation of the lower limit of the triangular distribution of ozone particles, in meters. - double ozone_lower_limit; - - /// Elevation of the upper limit of the triangular distribution of ozone particles, in meters. - double ozone_upper_limit; - - /// Elevation of the mode of the triangular distribution of ozone particles, in meters. - double ozone_mode; - - /// (Dependent) Ozone absorption coefficients. - double3 ozone_absorption; - - /// Airglow illuminance, in lux. - double3 airglow_illuminance; -}; - -} // namespace component -} // namespace game - -#endif // ANTKEEPER_GAME_COMPONENT_ATMOSPHERE_HPP diff --git a/src/game/component/behavior.hpp b/src/game/component/behavior.hpp deleted file mode 100644 index 5e29edf..0000000 --- a/src/game/component/behavior.hpp +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_COMPONENT_BEHAVIOR_HPP -#define ANTKEEPER_GAME_COMPONENT_BEHAVIOR_HPP - -#include "entity/ebt.hpp" - -namespace game { -namespace component { - -struct behavior -{ - const entity::ebt::node* behavior_tree; -}; - -} // namespace component -} // namespace game - -#endif // ANTKEEPER_GAME_COMPONENT_BEHAVIOR_HPP - diff --git a/src/game/component/blackbody.hpp b/src/game/component/blackbody.hpp deleted file mode 100644 index d048469..0000000 --- a/src/game/component/blackbody.hpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_COMPONENT_BLACKBODY_HPP -#define ANTKEEPER_GAME_COMPONENT_BLACKBODY_HPP - -namespace game { -namespace component { - -/// Blackbody radiator -struct blackbody -{ - /// Effective temperature, in Kelvin. - double temperature; - - /// (Dependent) RGB spectral luminance, in cd/m^2. - double3 luminance; -}; - -} // namespace component -} // namespace game - -#endif // ANTKEEPER_GAME_COMPONENT_BLACKBODY_HPP diff --git a/src/game/component/brush.hpp b/src/game/component/brush.hpp deleted file mode 100644 index 3fd5676..0000000 --- a/src/game/component/brush.hpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_COMPONENT_BRUSH_HPP -#define ANTKEEPER_GAME_COMPONENT_BRUSH_HPP - -namespace game { -namespace component { - -struct brush -{ - float radius; -}; - -} // namespace component -} // namespace game - -#endif // ANTKEEPER_GAME_COMPONENT_BRUSH_HPP diff --git a/src/game/component/camera.hpp b/src/game/component/camera.hpp deleted file mode 100644 index 40352bf..0000000 --- a/src/game/component/camera.hpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_COMPONENT_CAMERA_HPP -#define ANTKEEPER_GAME_COMPONENT_CAMERA_HPP - -#include "scene/camera.hpp" - -namespace game { -namespace component { - -/// Camera scene object component. -struct camera -{ - /// Pointer to camera scene object - scene::camera* object; -}; - -} // namespace component -} // namespace game - -#endif // ANTKEEPER_GAME_COMPONENT_CAMERA_HPP - diff --git a/src/game/component/cavity.hpp b/src/game/component/cavity.hpp deleted file mode 100644 index 3a1f92b..0000000 --- a/src/game/component/cavity.hpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_COMPONENT_CAVITY_HPP -#define ANTKEEPER_GAME_COMPONENT_CAVITY_HPP - -#include "utility/fundamental-types.hpp" - -namespace game { -namespace component { - -struct cavity -{ - float3 position; - float radius; -}; - -} // namespace component -} // namespace game - -#endif // ANTKEEPER_GAME_COMPONENT_CAVITY_HPP - diff --git a/src/game/component/celestial-body.hpp b/src/game/component/celestial-body.hpp deleted file mode 100644 index 6b14691..0000000 --- a/src/game/component/celestial-body.hpp +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_COMPONENT_CELESTIAL_BODY_HPP -#define ANTKEEPER_GAME_COMPONENT_CELESTIAL_BODY_HPP - -namespace game { -namespace component { - -/// A simple celestial body. -struct celestial_body -{ - /// Mean radius of the body, in meters. - double radius; - - /// Mass of the body, in kilograms. - double mass; - - /// Polynomial coefficients, in descending order of degree, of the right ascension of the body's north pole, in radians, w.r.t. Julian centuries (36525 days) from epoch. - std::vector pole_ra; - - /// Polynomial coefficients, in descending order of degree, of the declination of the body's north pole, in radians, w.r.t. Julian centuries (36525 days) from epoch. - std::vector pole_dec; - - /// Polynomial coefficients, in descending order of degree, of the rotation state of the body's prime meridian, in radians, w.r.t. days from epoch. - std::vector prime_meridian; - - /* - /// Polynomial coefficients, in descending order of degree, of the nutation and precession angles, in radians. Angles are calculated as `x + y * d`, where `d` is the days from epoch. - std::vector> nutation_precession_angles; - - /// Nutation and precession amplitudes of the right ascension of the body's north pole, in radians. - std::vector nutation_precession_ra; - - /// Nutation and precession amplitudes of the declination of the body's north pole, in radians. - std::vector nutation_precession_dec; - - /// Nutation and precession amplitudes of the rotation state of the body's prime meridian, in radians. - std::vector nutation_precession_pm; - */ - - /// Geometric albedo - double albedo; -}; - -} // namespace component -} // namespace game - -#endif // ANTKEEPER_GAME_COMPONENT_CELESTIAL_BODY_HPP diff --git a/src/game/component/chamber.hpp b/src/game/component/chamber.hpp deleted file mode 100644 index c2a871d..0000000 --- a/src/game/component/chamber.hpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_COMPONENT_CHAMBER_HPP -#define ANTKEEPER_GAME_COMPONENT_CHAMBER_HPP - -#include -#include -#include "entity/id.hpp" - -namespace game { -namespace component { - -/// Ant nest chamber. -struct chamber -{ - /// Entity ID of shaft to which the chamber is connected - entity::id shaft_eid; - - /// Distance along shaft at which the chamber is located - float distance; - - /// Entity ID of the chamber above this chamber - entity::id previous_chamber_eid; - - /// Entity ID of the chamber below this chamber - entity::id next_chamber_eid; - - float outer_radius; - float inner_radius; - float inner_sector_angle; - float tile_radius; - //std::unordered_set> tiles; -}; - -} // namespace component -} // namespace game - -#endif // ANTKEEPER_GAME_COMPONENT_CHAMBER_HPP diff --git a/src/game/component/collision.hpp b/src/game/component/collision.hpp deleted file mode 100644 index 30f9cc6..0000000 --- a/src/game/component/collision.hpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_COMPONENT_COLLISION_HPP -#define ANTKEEPER_GAME_COMPONENT_COLLISION_HPP - -#include "geom/aabb.hpp" -#include "geom/mesh.hpp" -#include "geom/mesh-accelerator.hpp" - -namespace game { -namespace component { - -struct collision -{ - geom::mesh* mesh; - geom::aabb bounds; - geom::mesh_accelerator mesh_accelerator; -}; - -} // namespace component -} // namespace game - -#endif // ANTKEEPER_GAME_COMPONENT_COLLISION_HPP - diff --git a/src/game/component/constraint-stack.hpp b/src/game/component/constraint-stack.hpp deleted file mode 100644 index 6383e19..0000000 --- a/src/game/component/constraint-stack.hpp +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_COMPONENT_CONSTRAINT_STACK_HPP -#define ANTKEEPER_GAME_COMPONENT_CONSTRAINT_STACK_HPP - -#include "entity/id.hpp" - -namespace game { -namespace component { - -/** - * Causes an ordered stack of constraints to be applied to an entity. - * - * @see game::component::constraint_stack_node - * @see game::component::constraint - */ -struct constraint_stack -{ - /// Priority number, with lower priorities evaluated first. - int priority; - - /// ID of the entity containing the first constraint stack node. - entity::id head; -}; - -/** - * Single node in a constraint stack. Allows toggling and weighing of constraints and links to the following constraint stack node (if any). - * - * @see game::component::constraint_stack - * @see game::component::constraint - */ -struct constraint_stack_node -{ - /// Enables or disables the constraint. - bool active; - - /// Controls the amount of influence the constraint has on the final result. - float weight; - - /// ID of the entity containing the next constraint in the constraint stack. - entity::id next; -}; - -} // namespace component -} // namespace game - -#endif // ANTKEEPER_GAME_COMPONENT_CONSTRAINT_STACK_HPP diff --git a/src/game/component/constraint/child-of.hpp b/src/game/component/constraint/child-of.hpp deleted file mode 100644 index b617534..0000000 --- a/src/game/component/constraint/child-of.hpp +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_COMPONENT_CONSTRAINT_CHILD_OF_HPP -#define ANTKEEPER_GAME_COMPONENT_CONSTRAINT_CHILD_OF_HPP - -#include "entity/id.hpp" -#include "utility/fundamental-types.hpp" - -namespace game { -namespace component { -namespace constraint { - -/** - * Makes the entity a child of the target entity. - */ -struct child_of -{ - /// Target entity ID. - entity::id target; -}; - -} // namespace constraint -} // namespace component -} // namespace game - -#endif // ANTKEEPER_GAME_COMPONENT_CONSTRAINT_CHILD_OF_HPP diff --git a/src/game/component/constraint/constraint.hpp b/src/game/component/constraint/constraint.hpp deleted file mode 100644 index e7879d6..0000000 --- a/src/game/component/constraint/constraint.hpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_COMPONENT_CONSTRAINT_HPP -#define ANTKEEPER_GAME_COMPONENT_CONSTRAINT_HPP - -namespace game { -namespace component { - -/// Transform constraint components. -namespace constraint {} - -} // namespace component -} // namespace game - -#include "game/component/constraint/child-of.hpp" -#include "game/component/constraint/copy-rotation.hpp" -#include "game/component/constraint/copy-scale.hpp" -#include "game/component/constraint/copy-transform.hpp" -#include "game/component/constraint/copy-translation.hpp" -#include "game/component/constraint/ease-to.hpp" -#include "game/component/constraint/pivot.hpp" -#include "game/component/constraint/spring-rotation.hpp" -#include "game/component/constraint/spring-to.hpp" -#include "game/component/constraint/spring-translation.hpp" -#include "game/component/constraint/three-dof.hpp" -#include "game/component/constraint/track-to.hpp" - -#endif // ANTKEEPER_GAME_COMPONENT_CONSTRAINT_HPP diff --git a/src/game/component/constraint/copy-rotation.hpp b/src/game/component/constraint/copy-rotation.hpp deleted file mode 100644 index 3967220..0000000 --- a/src/game/component/constraint/copy-rotation.hpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_COMPONENT_CONSTRAINT_COPY_ROTATION_HPP -#define ANTKEEPER_GAME_COMPONENT_CONSTRAINT_COPY_ROTATION_HPP - -#include "entity/id.hpp" - -namespace game { -namespace component { -namespace constraint { - -/** - * Copies the rotation of a target entity. - */ -struct copy_rotation -{ - /// Target entity ID. - entity::id target; -}; - -} // namespace constraint -} // namespace component -} // namespace game - -#endif // ANTKEEPER_GAME_COMPONENT_CONSTRAINT_COPY_ROTATION_HPP diff --git a/src/game/component/constraint/copy-scale.hpp b/src/game/component/constraint/copy-scale.hpp deleted file mode 100644 index f794f82..0000000 --- a/src/game/component/constraint/copy-scale.hpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_COMPONENT_CONSTRAINT_COPY_SCALE_HPP -#define ANTKEEPER_GAME_COMPONENT_CONSTRAINT_COPY_SCALE_HPP - -#include "entity/id.hpp" - -namespace game { -namespace component { -namespace constraint { - -/** - * Copies the scale of a target entity. - */ -struct copy_scale -{ - /// Target entity ID. - entity::id target; - - /// Copy X scale. - bool copy_x; - - /// Copy Y scale. - bool copy_y; - - /// Copy Z scale. - bool copy_z; -}; - -} // namespace constraint -} // namespace component -} // namespace game - -#endif // ANTKEEPER_GAME_COMPONENT_CONSTRAINT_COPY_SCALE_HPP diff --git a/src/game/component/constraint/copy-transform.hpp b/src/game/component/constraint/copy-transform.hpp deleted file mode 100644 index c871a3f..0000000 --- a/src/game/component/constraint/copy-transform.hpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_COMPONENT_CONSTRAINT_COPY_TRANSFORM_HPP -#define ANTKEEPER_GAME_COMPONENT_CONSTRAINT_COPY_TRANSFORM_HPP - -#include "entity/id.hpp" - -namespace game { -namespace component { -namespace constraint { - -/** - * Copies the transform of a target entity. - */ -struct copy_transform -{ - /// Target entity ID. - entity::id target; -}; - -} // namespace constraint -} // namespace component -} // namespace game - -#endif // ANTKEEPER_GAME_COMPONENT_CONSTRAINT_COPY_TRANSFORM_HPP diff --git a/src/game/component/constraint/copy-translation.hpp b/src/game/component/constraint/copy-translation.hpp deleted file mode 100644 index 18c1ff2..0000000 --- a/src/game/component/constraint/copy-translation.hpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_COMPONENT_CONSTRAINT_COPY_TRANSLATION_HPP -#define ANTKEEPER_GAME_COMPONENT_CONSTRAINT_COPY_TRANSLATION_HPP - -#include "entity/id.hpp" - -namespace game { -namespace component { -namespace constraint { - -/** - * Copies the translation of a target entity. - */ -struct copy_translation -{ - /// Target entity ID. - entity::id target; - - /// Copy X translation. - bool copy_x; - - /// Copy Y translation. - bool copy_y; - - /// Copy Z translation. - bool copy_z; - - /// Invert the copied X translation. - bool invert_x; - - /// Invert the copied Y translation. - bool invert_y; - - /// Invert the copied Z translation. - bool invert_z; - - /// Add the copied translation. - bool offset; -}; - -} // namespace constraint -} // namespace component -} // namespace game - -#endif // ANTKEEPER_GAME_COMPONENT_CONSTRAINT_COPY_TRANSLATION_HPP diff --git a/src/game/component/constraint/ease-to.hpp b/src/game/component/constraint/ease-to.hpp deleted file mode 100644 index 7ca8c0b..0000000 --- a/src/game/component/constraint/ease-to.hpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_COMPONENT_CONSTRAINT_EASE_TO_HPP -#define ANTKEEPER_GAME_COMPONENT_CONSTRAINT_EASE_TO_HPP - -#include "entity/id.hpp" -#include "utility/fundamental-types.hpp" - -namespace game { -namespace component { -namespace constraint { - -/** - * Eases toward a target entity. - */ -struct ease_to -{ - /// Target entity ID. - entity::id target; - - /// Start position. - float3 start; - - /// Total duration of the ease. - float duration; - - /// Elapsed time since ease began. - float t; - - /// Pointer to the interpolation function. - float3 (*function)(const float3&, const float3&, float); -}; - -} // namespace constraint -} // namespace component -} // namespace game - -#endif // ANTKEEPER_GAME_COMPONENT_CONSTRAINT_EASE_TO_HPP diff --git a/src/game/component/constraint/pivot.hpp b/src/game/component/constraint/pivot.hpp deleted file mode 100644 index f57550c..0000000 --- a/src/game/component/constraint/pivot.hpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_COMPONENT_CONSTRAINT_PIVOT_HPP -#define ANTKEEPER_GAME_COMPONENT_CONSTRAINT_PIVOT_HPP - -#include "entity/id.hpp" -#include "utility/fundamental-types.hpp" - -namespace game { -namespace component { -namespace constraint { - -/** - * Pivots around a target entity. - */ -struct pivot -{ - /// Target entity ID. - entity::id target; - - /// Pivot point offset. - float3 offset; -}; - -} // namespace constraint -} // namespace component -} // namespace game - -#endif // ANTKEEPER_GAME_COMPONENT_CONSTRAINT_PIVOT_HPP diff --git a/src/game/component/constraint/spring-rotation.hpp b/src/game/component/constraint/spring-rotation.hpp deleted file mode 100644 index 3964d4b..0000000 --- a/src/game/component/constraint/spring-rotation.hpp +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_COMPONENT_CONSTRAINT_SPRING_ROTATION_HPP -#define ANTKEEPER_GAME_COMPONENT_CONSTRAINT_SPRING_ROTATION_HPP - -#include "animation/spring.hpp" -#include "utility/fundamental-types.hpp" - -namespace game { -namespace component { -namespace constraint { - -/** - * - */ -struct spring_rotation -{ - /// Yaw, pitch, and roll angle spring. - numeric_spring spring; -}; - -} // namespace constraint -} // namespace component -} // namespace game - -#endif // ANTKEEPER_GAME_COMPONENT_CONSTRAINT_SPRING_ROTATION_HPP diff --git a/src/game/component/constraint/spring-to.hpp b/src/game/component/constraint/spring-to.hpp deleted file mode 100644 index 74b4ba4..0000000 --- a/src/game/component/constraint/spring-to.hpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_COMPONENT_CONSTRAINT_SPRING_TO_HPP -#define ANTKEEPER_GAME_COMPONENT_CONSTRAINT_SPRING_TO_HPP - -#include "entity/id.hpp" -#include "animation/spring.hpp" -#include "utility/fundamental-types.hpp" - -namespace game { -namespace component { -namespace constraint { - -/** - * Springs to a target entity. - */ -struct spring_to -{ - /// Target entity ID. - entity::id target; - - /// Translation spring. - numeric_spring translation; - - /// Rotation spring. - numeric_spring rotation; - - /// Spring translation. - bool spring_translation; - - /// Spring rotation. - bool spring_rotation; -}; - -} // namespace constraint -} // namespace component -} // namespace game - -#endif // ANTKEEPER_GAME_COMPONENT_CONSTRAINT_SPRING_TO_HPP diff --git a/src/game/component/constraint/spring-translation.hpp b/src/game/component/constraint/spring-translation.hpp deleted file mode 100644 index fd2e396..0000000 --- a/src/game/component/constraint/spring-translation.hpp +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_COMPONENT_CONSTRAINT_SPRING_TRANSLATION_HPP -#define ANTKEEPER_GAME_COMPONENT_CONSTRAINT_SPRING_TRANSLATION_HPP - -#include "animation/spring.hpp" -#include "utility/fundamental-types.hpp" - -namespace game { -namespace component { -namespace constraint { - -/** - * - */ -struct spring_translation -{ - /// Translation spring. - numeric_spring spring; -}; - -} // namespace constraint -} // namespace component -} // namespace game - -#endif // ANTKEEPER_GAME_COMPONENT_CONSTRAINT_SPRING_TRANSLATION_HPP diff --git a/src/game/component/constraint/three-dof.hpp b/src/game/component/constraint/three-dof.hpp deleted file mode 100644 index 1d1b952..0000000 --- a/src/game/component/constraint/three-dof.hpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_COMPONENT_CONSTRAINT_THREE_DOF_HPP -#define ANTKEEPER_GAME_COMPONENT_CONSTRAINT_THREE_DOF_HPP - -namespace game { -namespace component { -namespace constraint { - -/** - * Builds rotation from 3DoF angles. - */ -struct three_dof -{ - /// Yaw rotation angle, in radians. - float yaw; - - /// Pitch rotation angle, in radians. - float pitch; - - /// Roll rotation angle, in radians. - float roll; -}; - -} // namespace constraint -} // namespace component -} // namespace game - -#endif // ANTKEEPER_GAME_COMPONENT_CONSTRAINT_THREE_DOF_HPP diff --git a/src/game/component/constraint/track-to.hpp b/src/game/component/constraint/track-to.hpp deleted file mode 100644 index 749e925..0000000 --- a/src/game/component/constraint/track-to.hpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_COMPONENT_CONSTRAINT_TRACK_TO_HPP -#define ANTKEEPER_GAME_COMPONENT_CONSTRAINT_TRACK_TO_HPP - -#include "entity/id.hpp" -#include "utility/fundamental-types.hpp" - -namespace game { -namespace component { -namespace constraint { - -/** - * Rotates a transform to face a target. - */ -struct track_to -{ - /// Target entity ID. - entity::id target; - - /// Up direction vector. - float3 up; -}; - -} // namespace constraint -} // namespace component -} // namespace game - -#endif // ANTKEEPER_GAME_COMPONENT_CONSTRAINT_TRACK_TO_HPP diff --git a/src/game/component/diffuse-reflector.hpp b/src/game/component/diffuse-reflector.hpp deleted file mode 100644 index d698ca3..0000000 --- a/src/game/component/diffuse-reflector.hpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_COMPONENT_DIFFUSE_REFLECTOR_HPP -#define ANTKEEPER_GAME_COMPONENT_DIFFUSE_REFLECTOR_HPP - -namespace game { -namespace component { - -struct diffuse_reflector -{ - double albedo; -}; - -} // namespace component -} // namespace game - -#endif // ANTKEEPER_GAME_COMPONENT_DIFFUSE_REFLECTOR_HPP diff --git a/src/game/component/light.hpp b/src/game/component/light.hpp deleted file mode 100644 index 30093e0..0000000 --- a/src/game/component/light.hpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_COMPONENT_LIGHT_HPP -#define ANTKEEPER_GAME_COMPONENT_LIGHT_HPP - -#include "utility/fundamental-types.hpp" -#include "scene/light.hpp" - -namespace game { -namespace component { - -struct light -{ - scene::light_type type; - float3 color; - float intensity; - float3 attenuation; - float2 cutoff; -}; - -} // namespace component -} // namespace game - -#endif // ANTKEEPER_GAME_COMPONENT_LIGHT_HPP - diff --git a/src/game/component/locomotion.hpp b/src/game/component/locomotion.hpp deleted file mode 100644 index 82dafdc..0000000 --- a/src/game/component/locomotion.hpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_COMPONENT_PLACEMENT_HPP -#define ANTKEEPER_GAME_COMPONENT_PLACEMENT_HPP - -#include "geom/mesh.hpp" -#include "utility/fundamental-types.hpp" - -namespace game { -namespace component { - -struct locomotion -{ - //const geom::mesh::face* triangle; - //float3 barycentric_position; - - float yaw; -}; - -} // namespace component -} // namespace game - -#endif // ANTKEEPER_GAME_COMPONENT_PLACEMENT_HPP - diff --git a/src/game/component/model.hpp b/src/game/component/model.hpp deleted file mode 100644 index fa4ce88..0000000 --- a/src/game/component/model.hpp +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_COMPONENT_MODEL_HPP -#define ANTKEEPER_GAME_COMPONENT_MODEL_HPP - -#include "render/model.hpp" -#include - -namespace game { -namespace component { - -struct model -{ - render::model* render_model; - std::unordered_map materials; - int instance_count; - unsigned int layers; -}; - -} // namespace component -} // namespace game - -#endif // ANTKEEPER_GAME_COMPONENT_MODEL_HPP diff --git a/src/game/component/nest.hpp b/src/game/component/nest.hpp deleted file mode 100644 index c585962..0000000 --- a/src/game/component/nest.hpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_COMPONENT_NEST_HPP -#define ANTKEEPER_GAME_COMPONENT_NEST_HPP - -#include -#include "entity/id.hpp" - -namespace game { -namespace component { - -struct nest -{ - std::vector chambers; - float helix_radius; - float helix_pitch; - float helix_chirality; - float helix_turns; -}; - -} // namespace component -} // namespace game - -#endif // ANTKEEPER_GAME_COMPONENT_NEST_HPP diff --git a/src/game/component/observer.hpp b/src/game/component/observer.hpp deleted file mode 100644 index 24c1b3f..0000000 --- a/src/game/component/observer.hpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_COMPONENT_OBSERVER_HPP -#define ANTKEEPER_GAME_COMPONENT_OBSERVER_HPP - -#include "entity/id.hpp" - -namespace game { -namespace component { - -/** - * - */ -struct observer -{ - /// Entity ID of a celestial body to which the observer position is relative. - entity::id reference_body_eid; - - /// Elevation of the observer, in radians. - double elevation; - - /// Latitude of the observer, in radians. - double latitude; - - /// Longitude of the observer, in radians. - double longitude; -}; - -} // namespace component -} // namespace game - -#endif // ANTKEEPER_GAME_COMPONENT_OBSERVER_HPP diff --git a/src/game/component/orbit.hpp b/src/game/component/orbit.hpp deleted file mode 100644 index 35856ef..0000000 --- a/src/game/component/orbit.hpp +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_COMPONENT_ORBIT_HPP -#define ANTKEEPER_GAME_COMPONENT_ORBIT_HPP - -#include "entity/id.hpp" -#include "math/vector.hpp" - -namespace game { -namespace component { - -struct orbit -{ - /// Entity ID of the parent orbit. - entity::id parent; - - /// Index of the orbit in the ephemeris. - int ephemeris_index; - - /// Orbit scale, for two-body orbits with one ephemeris item. - double scale; - - /// Cartesian position of the orbit, w.r.t. the ICRF frame. - math::vector3 position; -}; - -} // namespace component -} // namespace game - -#endif // ANTKEEPER_GAME_COMPONENT_ORBIT_HPP diff --git a/src/game/component/picking.hpp b/src/game/component/picking.hpp deleted file mode 100644 index c23f1ee..0000000 --- a/src/game/component/picking.hpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_COMPONENT_PICKING_HPP -#define ANTKEEPER_GAME_COMPONENT_PICKING_HPP - -#include "geom/primitive/sphere.hpp" -#include - -namespace game { -namespace component { - -struct picking -{ - /// Picking sphere. - geom::primitive::sphere sphere; - - /// Picking flags. - std::uint32_t flags; -}; - -} // namespace component -} // namespace game - -#endif // ANTKEEPER_GAME_COMPONENT_PICKING_HPP diff --git a/src/game/component/portal.hpp b/src/game/component/portal.hpp deleted file mode 100644 index 64cf7f6..0000000 --- a/src/game/component/portal.hpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_COMPONENT_PORTAL_HPP -#define ANTKEEPER_GAME_COMPONENT_PORTAL_HPP - -namespace game { -namespace component { - -struct portal -{ - -}; - -} // namespace component -} // namespace game - -#endif // ANTKEEPER_GAME_COMPONENT_PORTAL_HPP diff --git a/src/game/component/spring.hpp b/src/game/component/spring.hpp deleted file mode 100644 index 8dd2d5a..0000000 --- a/src/game/component/spring.hpp +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_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/component/steering.hpp b/src/game/component/steering.hpp deleted file mode 100644 index bd11d3a..0000000 --- a/src/game/component/steering.hpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_COMPONENT_STEERING_HPP -#define ANTKEEPER_GAME_COMPONENT_STEERING_HPP - -#include "ai/steering/agent.hpp" - -namespace game { -namespace component { - -struct steering -{ - /// Steering agent. - ai::steering::agent agent; - - // Wander behavior - float wander_weight; - float wander_noise; - float wander_distance; - float wander_radius; - float wander_angle; - float wander_angle2; - - // Seek behavior - float seek_weight; - float3 seek_target; - - // Flee behavior - float flee_weight; - - /// Sum of steering behavior weights - float sum_weights; -}; - -} // namespace component -} // namespace game - -#endif // ANTKEEPER_GAME_COMPONENT_STEERING_HPP diff --git a/src/game/component/terrain.hpp b/src/game/component/terrain.hpp deleted file mode 100644 index 7b071b2..0000000 --- a/src/game/component/terrain.hpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_COMPONENT_TERRAIN_HPP -#define ANTKEEPER_GAME_COMPONENT_TERRAIN_HPP - -#include "render/material.hpp" -#include - -namespace game { -namespace component { - -struct terrain -{ - /// Function object which returns elevation (in meters) given latitude (radians) and longitude (radians). - std::function elevation; - - /// Maximum level of detail (maximum quadtree depth level) - std::size_t max_lod; - - /// Material for terrain patches; - render::material* patch_material; -}; - -} // namespace component -} // namespace game - -#endif // ANTKEEPER_GAME_COMPONENT_TERRAIN_HPP diff --git a/src/game/component/tool.hpp b/src/game/component/tool.hpp deleted file mode 100644 index 757aceb..0000000 --- a/src/game/component/tool.hpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_COMPONENT_TOOL_HPP -#define ANTKEEPER_GAME_COMPONENT_TOOL_HPP - -#include - -namespace game { -namespace component { - -struct tool -{ - std::function activated; - std::function deactivated; - std::function active; -}; - -} // namespace component -} // namespace game - -#endif // ANTKEEPER_GAME_COMPONENT_TOOL_HPP - diff --git a/src/game/component/trackable.hpp b/src/game/component/trackable.hpp deleted file mode 100644 index cb8c45a..0000000 --- a/src/game/component/trackable.hpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_COMPONENT_TRACKABLE_HPP -#define ANTKEEPER_GAME_COMPONENT_TRACKABLE_HPP - -#include "utility/fundamental-types.hpp" - -namespace game { -namespace component { - -struct trackable -{ - float distance; -}; - -} // namespace component -} // namespace game - -#endif // ANTKEEPER_GAME_COMPONENT_TRACKABLE_HPP diff --git a/src/game/component/transform.hpp b/src/game/component/transform.hpp deleted file mode 100644 index 46d79e2..0000000 --- a/src/game/component/transform.hpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_COMPONENT_TRANSFORM_HPP -#define ANTKEEPER_GAME_COMPONENT_TRANSFORM_HPP - -#include "math/transform-type.hpp" - -namespace game { -namespace component { - -struct transform -{ - math::transform local; - math::transform world; - bool warp; -}; - -} // namespace component -} // namespace game - -#endif // ANTKEEPER_GAME_COMPONENT_TRANSFORM_HPP - diff --git a/src/game/components/atmosphere-component.hpp b/src/game/components/atmosphere-component.hpp new file mode 100644 index 0000000..797b351 --- /dev/null +++ b/src/game/components/atmosphere-component.hpp @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_ATMOSPHERE_COMPONENT_HPP +#define ANTKEEPER_GAME_ATMOSPHERE_COMPONENT_HPP + +#include + + +/// Atmosphere +struct atmosphere_component +{ + /// Elevation of the upper limit of the atmosphere, in meters. + double upper_limit; + + /// Index of refraction of air at sea level. + double index_of_refraction; + + /// Molar concentration of Rayleigh particles at sea level, in mol/m-3. + double rayleigh_concentration; + + /// Scale height of the exponential distribution of Rayleigh particles, in meters. + double rayleigh_scale_height; + + /// (Dependent) Rayleigh scattering coefficients. + double3 rayleigh_scattering; + + /// Molar concentration of Mie particles at sea level, in mol/m-3. + double mie_concentration; + + /// Scale height of the exponential distribution of Mie particles, in meters. + double mie_scale_height; + + /// Mie phase function anisotropy factor. + double mie_anisotropy; + + /// Mie single-scattering albedo. + double mie_albedo; + + /// (Dependent) Mie scattering coefficient. + double mie_scattering; + + /// (Dependent) Mie extinction coefficient. + double mie_extinction; + + /// Concentration of ozone in the atmosphere, unitless. + double ozone_concentration; + + /// Elevation of the lower limit of the triangular distribution of ozone particles, in meters. + double ozone_lower_limit; + + /// Elevation of the upper limit of the triangular distribution of ozone particles, in meters. + double ozone_upper_limit; + + /// Elevation of the mode of the triangular distribution of ozone particles, in meters. + double ozone_mode; + + /// (Dependent) Ozone absorption coefficients. + double3 ozone_absorption; + + /// Airglow illuminance, in lux. + double3 airglow_illuminance; +}; + + +#endif // ANTKEEPER_GAME_ATMOSPHERE_COMPONENT_HPP diff --git a/src/game/components/behavior-component.hpp b/src/game/components/behavior-component.hpp new file mode 100644 index 0000000..c36d35c --- /dev/null +++ b/src/game/components/behavior-component.hpp @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_BEHAVIOR_COMPONENT_HPP +#define ANTKEEPER_GAME_BEHAVIOR_COMPONENT_HPP + + +struct behavior_component +{ + +}; + + +#endif // ANTKEEPER_GAME_BEHAVIOR_COMPONENT_HPP + diff --git a/src/game/components/blackbody-component.hpp b/src/game/components/blackbody-component.hpp new file mode 100644 index 0000000..2d07325 --- /dev/null +++ b/src/game/components/blackbody-component.hpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_BLACKBODY_COMPONENT_HPP +#define ANTKEEPER_GAME_BLACKBODY_COMPONENT_HPP + + +/// Blackbody radiator +struct blackbody_component +{ + /// Effective temperature, in Kelvin. + double temperature; + + /// (Dependent) RGB spectral luminance, in cd/m^2. + double3 luminance; +}; + + +#endif // ANTKEEPER_GAME_BLACKBODY_COMPONENT_HPP diff --git a/src/game/components/camera-component.hpp b/src/game/components/camera-component.hpp new file mode 100644 index 0000000..bd16633 --- /dev/null +++ b/src/game/components/camera-component.hpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_CAMERA_COMPONENT_HPP +#define ANTKEEPER_GAME_CAMERA_COMPONENT_HPP + +#include + + +/// Camera scene object component. +struct camera_component +{ + /// Pointer to camera scene object + scene::camera* object; +}; + + +#endif // ANTKEEPER_GAME_CAMERA_COMPONENT_HPP + diff --git a/src/game/components/caste-component.hpp b/src/game/components/caste-component.hpp new file mode 100644 index 0000000..b9b2d40 --- /dev/null +++ b/src/game/components/caste-component.hpp @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_CASTE_COMPONENT_HPP +#define ANTKEEPER_GAME_CASTE_COMPONENT_HPP + +#include "game/ant/caste.hpp" + + +struct caste_component +{ + /// Caste type. + ::ant::caste type; + + /// Subcaste type. + //::ant::subcaste subtype; +}; + + +#endif // ANTKEEPER_GAME_CASTE_COMPONENT_HPP diff --git a/src/game/components/cavity-component.hpp b/src/game/components/cavity-component.hpp new file mode 100644 index 0000000..d975697 --- /dev/null +++ b/src/game/components/cavity-component.hpp @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_CAVITY_COMPONENT_HPP +#define ANTKEEPER_GAME_CAVITY_COMPONENT_HPP + +#include + + +struct cavity_component +{ + float3 position; + float radius; +}; + + +#endif // ANTKEEPER_GAME_CAVITY_COMPONENT_HPP + diff --git a/src/game/components/celestial-body-component.hpp b/src/game/components/celestial-body-component.hpp new file mode 100644 index 0000000..4100a0c --- /dev/null +++ b/src/game/components/celestial-body-component.hpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_CELESTIAL_BODY_COMPONENT_HPP +#define ANTKEEPER_GAME_CELESTIAL_BODY_COMPONENT_HPP + + +/// A simple celestial body. +struct celestial_body_component +{ + /// Mean radius of the body, in meters. + double radius; + + /// Mass of the body, in kilograms. + double mass; + + /// Polynomial coefficients, in descending order of degree, of the right ascension of the body's north pole, in radians, w.r.t. Julian centuries (36525 days) from epoch. + std::vector pole_ra; + + /// Polynomial coefficients, in descending order of degree, of the declination of the body's north pole, in radians, w.r.t. Julian centuries (36525 days) from epoch. + std::vector pole_dec; + + /// Polynomial coefficients, in descending order of degree, of the rotation state of the body's prime meridian, in radians, w.r.t. days from epoch. + std::vector prime_meridian; + + /* + /// Polynomial coefficients, in descending order of degree, of the nutation and precession angles, in radians. Angles are calculated as `x + y * d`, where `d` is the days from epoch. + std::vector> nutation_precession_angles; + + /// Nutation and precession amplitudes of the right ascension of the body's north pole, in radians. + std::vector nutation_precession_ra; + + /// Nutation and precession amplitudes of the declination of the body's north pole, in radians. + std::vector nutation_precession_dec; + + /// Nutation and precession amplitudes of the rotation state of the body's prime meridian, in radians. + std::vector nutation_precession_pm; + */ + + /// Geometric albedo + double albedo; +}; + + +#endif // ANTKEEPER_GAME_CELESTIAL_BODY_COMPONENT_HPP diff --git a/src/game/components/chamber-component.hpp b/src/game/components/chamber-component.hpp new file mode 100644 index 0000000..993e85a --- /dev/null +++ b/src/game/components/chamber-component.hpp @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_CHAMBER_COMPONENT_HPP +#define ANTKEEPER_GAME_CHAMBER_COMPONENT_HPP + +#include +#include +#include + + +/// Ant nest chamber. +struct chamber_component +{ + /// Entity ID of shaft to which the chamber is connected + entity::id shaft_eid; + + /// Distance along shaft at which the chamber is located + float distance; + + /// Entity ID of the chamber above this chamber + entity::id previous_chamber_eid; + + /// Entity ID of the chamber below this chamber + entity::id next_chamber_eid; + + float outer_radius; + float inner_radius; + float inner_sector_angle; + float tile_radius; + //std::unordered_set> tiles; +}; + + +#endif // ANTKEEPER_GAME_CHAMBER_COMPONENT_HPP diff --git a/src/game/components/collision-component.hpp b/src/game/components/collision-component.hpp new file mode 100644 index 0000000..00500ba --- /dev/null +++ b/src/game/components/collision-component.hpp @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_COLLISION_COMPONENT_HPP +#define ANTKEEPER_GAME_COLLISION_COMPONENT_HPP + +#include +#include +#include + + +struct collision_component +{ + geom::mesh* mesh; + geom::aabb bounds; + geom::mesh_accelerator mesh_accelerator; +}; + + +#endif // ANTKEEPER_GAME_COLLISION_COMPONENT_HPP + diff --git a/src/game/components/constraint-stack-component.hpp b/src/game/components/constraint-stack-component.hpp new file mode 100644 index 0000000..7a324e5 --- /dev/null +++ b/src/game/components/constraint-stack-component.hpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_CONSTRAINT_STACK_COMPONENT_HPP +#define ANTKEEPER_GAME_CONSTRAINT_STACK_COMPONENT_HPP + +#include + + +/** + * Causes an ordered stack of constraints to be applied to an entity. + */ +struct constraint_stack_component +{ + /// Priority number, with lower priorities evaluated first. + int priority; + + /// ID of the entity containing the first constraint stack node. + entity::id head; +}; + +/** + * Single node in a constraint stack. Allows toggling and weighing of constraints and links to the following constraint stack node (if any). + */ +struct constraint_stack_node_component +{ + /// Enables or disables the constraint. + bool active; + + /// Controls the amount of influence the constraint has on the final result. + float weight; + + /// ID of the entity containing the next constraint in the constraint stack. + entity::id next; +}; + + +#endif // ANTKEEPER_GAME_CONSTRAINT_STACK_COMPONENT_HPP diff --git a/src/game/components/diffuse-reflector-component.hpp b/src/game/components/diffuse-reflector-component.hpp new file mode 100644 index 0000000..87dfd8a --- /dev/null +++ b/src/game/components/diffuse-reflector-component.hpp @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_DIFFUSE_REFLECTOR_COMPONENT_HPP +#define ANTKEEPER_GAME_DIFFUSE_REFLECTOR_COMPONENT_HPP + + +struct diffuse_reflector_component +{ + double albedo; +}; + + +#endif // ANTKEEPER_GAME_DIFFUSE_REFLECTOR_COMPONENT_HPP diff --git a/src/game/components/light-component.hpp b/src/game/components/light-component.hpp new file mode 100644 index 0000000..62b20b0 --- /dev/null +++ b/src/game/components/light-component.hpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_LIGHT_COMPONENT_HPP +#define ANTKEEPER_GAME_LIGHT_COMPONENT_HPP + +#include +#include + + +struct light_component +{ + scene::light_type type; + float3 color; + float intensity; + float3 attenuation; + float2 cutoff; +}; + + +#endif // ANTKEEPER_GAME_LIGHT_COMPONENT_HPP + diff --git a/src/game/components/locomotion-component.hpp b/src/game/components/locomotion-component.hpp new file mode 100644 index 0000000..fa8ffd0 --- /dev/null +++ b/src/game/components/locomotion-component.hpp @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_PLACEMENT_COMPONENT_HPP +#define ANTKEEPER_GAME_PLACEMENT_COMPONENT_HPP + +#include +#include + + +struct locomotion_component +{ + //const geom::mesh::face* triangle; + //float3 barycentric_position; + + float yaw; +}; + + +#endif // ANTKEEPER_GAME_PLACEMENT_COMPONENT_HPP + diff --git a/src/game/components/model-component.hpp b/src/game/components/model-component.hpp new file mode 100644 index 0000000..ca33a2c --- /dev/null +++ b/src/game/components/model-component.hpp @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_MODEL_COMPONENT_HPP +#define ANTKEEPER_GAME_MODEL_COMPONENT_HPP + +#include +#include + + +struct model_component +{ + render::model* render_model; + std::unordered_map materials; + int instance_count; + unsigned int layers; +}; + + +#endif // ANTKEEPER_GAME_MODEL_COMPONENT_HPP diff --git a/src/game/components/nest-component.hpp b/src/game/components/nest-component.hpp new file mode 100644 index 0000000..be381d9 --- /dev/null +++ b/src/game/components/nest-component.hpp @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_NEST_COMPONENT_HPP +#define ANTKEEPER_GAME_NEST_COMPONENT_HPP + +#include +#include + + +struct nest_component +{ + std::vector chambers; + float helix_radius; + float helix_pitch; + float helix_chirality; + float helix_turns; +}; + + +#endif // ANTKEEPER_GAME_NEST_COMPONENT_HPP diff --git a/src/game/components/observer-component.hpp b/src/game/components/observer-component.hpp new file mode 100644 index 0000000..3983673 --- /dev/null +++ b/src/game/components/observer-component.hpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_OBSERVER_COMPONENT_HPP +#define ANTKEEPER_GAME_OBSERVER_COMPONENT_HPP + +#include + + +/** + * + */ +struct observer_component +{ + /// Entity ID of a celestial body to which the observer position is relative. + entity::id reference_body_eid; + + /// Elevation of the observer, in radians. + double elevation; + + /// Latitude of the observer, in radians. + double latitude; + + /// Longitude of the observer, in radians. + double longitude; +}; + + +#endif // ANTKEEPER_GAME_OBSERVER_COMPONENT_HPP diff --git a/src/game/components/orbit-component.hpp b/src/game/components/orbit-component.hpp new file mode 100644 index 0000000..93c0b1c --- /dev/null +++ b/src/game/components/orbit-component.hpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_ORBIT_COMPONENT_HPP +#define ANTKEEPER_GAME_ORBIT_COMPONENT_HPP + +#include +#include + + +struct orbit_component +{ + /// Entity ID of the parent orbit. + entity::id parent; + + /// Index of the orbit in the ephemeris. + int ephemeris_index; + + /// Orbit scale, for two-body orbits with one ephemeris item. + double scale; + + /// Cartesian position of the orbit, w.r.t. the ICRF frame. + math::vector3 position; +}; + + +#endif // ANTKEEPER_GAME_ORBIT_COMPONENT_HPP diff --git a/src/game/components/picking-component.hpp b/src/game/components/picking-component.hpp new file mode 100644 index 0000000..d5008e5 --- /dev/null +++ b/src/game/components/picking-component.hpp @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_PICKING_COMPONENT_HPP +#define ANTKEEPER_GAME_PICKING_COMPONENT_HPP + +#include +#include + + +struct picking_component +{ + /// Picking sphere. + geom::primitive::sphere sphere; + + /// Picking flags. + std::uint32_t flags; +}; + + +#endif // ANTKEEPER_GAME_PICKING_COMPONENT_HPP diff --git a/src/game/components/portal-component.hpp b/src/game/components/portal-component.hpp new file mode 100644 index 0000000..8192e1a --- /dev/null +++ b/src/game/components/portal-component.hpp @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_PORTAL_COMPONENT_HPP +#define ANTKEEPER_GAME_PORTAL_COMPONENT_HPP + + +struct portal_component +{ + +}; + + +#endif // ANTKEEPER_GAME_PORTAL_COMPONENT_HPP diff --git a/src/game/components/spring-component.hpp b/src/game/components/spring-component.hpp new file mode 100644 index 0000000..0bd09f7 --- /dev/null +++ b/src/game/components/spring-component.hpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_SPRING_COMPONENT_HPP +#define ANTKEEPER_GAME_SPRING_COMPONENT_HPP + +#include +#include +#include + + +/** + * Numeric spring with one component. + */ +struct spring1_component +{ + /// Numeric spring with one component. + numeric_spring spring; + + /// Spring solved callback. + std::function callback; +}; + +/** + * Numeric spring with two components. + */ +struct spring2_component +{ + /// Numeric spring with two components. + numeric_spring spring; + + /// Spring solved callback. + std::function callback; +}; + +/** + * Numeric spring with three components. + */ +struct spring3_component +{ + /// Numeric spring with three components. + numeric_spring spring; + + /// Spring solved callback. + std::function callback; +}; + +/** + * Numeric spring with four components. + */ +struct spring4_component +{ + /// Numeric spring with four components. + numeric_spring spring; + + /// Spring solved callback. + std::function callback; +}; + + +#endif // ANTKEEPER_GAME_SPRING_COMPONENT_HPP diff --git a/src/game/components/steering-component.hpp b/src/game/components/steering-component.hpp new file mode 100644 index 0000000..c408fe6 --- /dev/null +++ b/src/game/components/steering-component.hpp @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_STEERING_COMPONENT_HPP +#define ANTKEEPER_GAME_STEERING_COMPONENT_HPP + +#include + + +struct steering_component +{ + /// Steering agent. + ai::steering::agent agent; + + // Wander behavior + float wander_weight; + float wander_noise; + float wander_distance; + float wander_radius; + float wander_angle; + float wander_angle2; + + // Seek behavior + float seek_weight; + float3 seek_target; + + // Flee behavior + float flee_weight; + + /// Sum of steering behavior weights + float sum_weights; +}; + + +#endif // ANTKEEPER_GAME_STEERING_COMPONENT_HPP diff --git a/src/game/components/terrain-component.hpp b/src/game/components/terrain-component.hpp new file mode 100644 index 0000000..d4b4fca --- /dev/null +++ b/src/game/components/terrain-component.hpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_TERRAIN_COMPONENT_HPP +#define ANTKEEPER_GAME_TERRAIN_COMPONENT_HPP + +#include +#include + + +struct terrain_component +{ + /// Function object which returns elevation (in meters) given latitude (radians) and longitude (radians). + std::function elevation; + + /// Maximum level of detail (maximum quadtree depth level) + std::size_t max_lod; + + /// Material for terrain patches; + render::material* patch_material; +}; + + +#endif // ANTKEEPER_GAME_TERRAIN_COMPONENT_HPP diff --git a/src/game/components/tool-component.hpp b/src/game/components/tool-component.hpp new file mode 100644 index 0000000..e856e4a --- /dev/null +++ b/src/game/components/tool-component.hpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_TOOL_COMPONENT_HPP +#define ANTKEEPER_GAME_TOOL_COMPONENT_HPP + +#include + + +struct tool_component +{ + std::function activated; + std::function deactivated; + std::function active; +}; + + +#endif // ANTKEEPER_GAME_TOOL_COMPONENT_HPP + diff --git a/src/game/components/trackable-component.hpp b/src/game/components/trackable-component.hpp new file mode 100644 index 0000000..a0b8058 --- /dev/null +++ b/src/game/components/trackable-component.hpp @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_TRACKABLE_COMPONENT_HPP +#define ANTKEEPER_GAME_TRACKABLE_COMPONENT_HPP + +#include + + +struct trackable_component +{ + float distance; +}; + + +#endif // ANTKEEPER_GAME_TRACKABLE_COMPONENT_HPP diff --git a/src/game/components/transform-component.hpp b/src/game/components/transform-component.hpp new file mode 100644 index 0000000..f84d501 --- /dev/null +++ b/src/game/components/transform-component.hpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_TRANSFORM_COMPONENT_HPP +#define ANTKEEPER_GAME_TRANSFORM_COMPONENT_HPP + +#include + + +struct transform_component +{ + math::transform local; + math::transform world; + bool warp; +}; + + +#endif // ANTKEEPER_GAME_TRANSFORM_COMPONENT_HPP + diff --git a/src/game/constraints/child-of-constraint.hpp b/src/game/constraints/child-of-constraint.hpp new file mode 100644 index 0000000..fc48183 --- /dev/null +++ b/src/game/constraints/child-of-constraint.hpp @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_CHILD_OF_CONSTRAINT_HPP +#define ANTKEEPER_GAME_CHILD_OF_CONSTRAINT_HPP + +#include +#include + + +/** + * Makes the entity a child of the target entity. + */ +struct child_of_constraint +{ + /// Target entity ID. + entity::id target; +}; + + +#endif // ANTKEEPER_GAME_CHILD_OF_CONSTRAINT_HPP diff --git a/src/game/constraints/copy-rotation-constraint.hpp b/src/game/constraints/copy-rotation-constraint.hpp new file mode 100644 index 0000000..3584bf4 --- /dev/null +++ b/src/game/constraints/copy-rotation-constraint.hpp @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_COPY_ROTATION_CONSTRAINT_HPP +#define ANTKEEPER_GAME_COPY_ROTATION_CONSTRAINT_HPP + +#include + + +/** + * Copies the rotation of a target entity. + */ +struct copy_rotation_constraint +{ + /// Target entity ID. + entity::id target; +}; + + +#endif // ANTKEEPER_GAME_COPY_ROTATION_CONSTRAINT_HPP diff --git a/src/game/constraints/copy-scale-constraint.hpp b/src/game/constraints/copy-scale-constraint.hpp new file mode 100644 index 0000000..fc00fb7 --- /dev/null +++ b/src/game/constraints/copy-scale-constraint.hpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_COPY_SCALE_CONSTRAINT_HPP +#define ANTKEEPER_GAME_COPY_SCALE_CONSTRAINT_HPP + +#include + + +/** + * Copies the scale of a target entity. + */ +struct copy_scale_constraint +{ + /// Target entity ID. + entity::id target; + + /// Copy X scale. + bool copy_x; + + /// Copy Y scale. + bool copy_y; + + /// Copy Z scale. + bool copy_z; +}; + + +#endif // ANTKEEPER_GAME_COPY_SCALE_CONSTRAINT_HPP diff --git a/src/game/constraints/copy-transform-constraint.hpp b/src/game/constraints/copy-transform-constraint.hpp new file mode 100644 index 0000000..001b5d1 --- /dev/null +++ b/src/game/constraints/copy-transform-constraint.hpp @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_COPY_TRANSFORM_CONSTRAINT_HPP +#define ANTKEEPER_GAME_COPY_TRANSFORM_CONSTRAINT_HPP + +#include + + +/** + * Copies the transform of a target entity. + */ +struct copy_transform_constraint +{ + /// Target entity ID. + entity::id target; +}; + + +#endif // ANTKEEPER_GAME_COPY_TRANSFORM_CONSTRAINT_HPP diff --git a/src/game/constraints/copy-translation-constraint.hpp b/src/game/constraints/copy-translation-constraint.hpp new file mode 100644 index 0000000..de0fc90 --- /dev/null +++ b/src/game/constraints/copy-translation-constraint.hpp @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_COPY_TRANSLATION_CONSTRAINT_HPP +#define ANTKEEPER_GAME_COPY_TRANSLATION_CONSTRAINT_HPP + +#include + + +/** + * Copies the translation of a target entity. + */ +struct copy_translation_constraint +{ + /// Target entity ID. + entity::id target; + + /// Copy X translation. + bool copy_x; + + /// Copy Y translation. + bool copy_y; + + /// Copy Z translation. + bool copy_z; + + /// Invert the copied X translation. + bool invert_x; + + /// Invert the copied Y translation. + bool invert_y; + + /// Invert the copied Z translation. + bool invert_z; + + /// Add the copied translation. + bool offset; +}; + + +#endif // ANTKEEPER_GAME_COPY_TRANSLATION_CONSTRAINT_HPP diff --git a/src/game/constraints/ease-to-constraint.hpp b/src/game/constraints/ease-to-constraint.hpp new file mode 100644 index 0000000..c4d48f2 --- /dev/null +++ b/src/game/constraints/ease-to-constraint.hpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_EASE_TO_CONSTRAINT_HPP +#define ANTKEEPER_GAME_EASE_TO_CONSTRAINT_HPP + +#include +#include + + +/** + * Eases toward a target entity. + */ +struct ease_to_constraint +{ + /// Target entity ID. + entity::id target; + + /// Start position. + float3 start; + + /// Total duration of the ease. + float duration; + + /// Elapsed time since ease began. + float t; + + /// Pointer to the interpolation function. + float3 (*function)(const float3&, const float3&, float); +}; + + +#endif // ANTKEEPER_GAME_EASE_TO_CONSTRAINT_HPP diff --git a/src/game/constraints/pivot-constraint.hpp b/src/game/constraints/pivot-constraint.hpp new file mode 100644 index 0000000..39d085c --- /dev/null +++ b/src/game/constraints/pivot-constraint.hpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_PIVOT_CONSTRAINT_HPP +#define ANTKEEPER_GAME_PIVOT_CONSTRAINT_HPP + +#include +#include + + +/** + * Pivots around a target entity. + */ +struct pivot_constraint +{ + /// Target entity ID. + entity::id target; + + /// Pivot point offset. + float3 offset; +}; + + +#endif // ANTKEEPER_GAME_PIVOT_CONSTRAINT_HPP diff --git a/src/game/constraints/spring-rotation-constraint.hpp b/src/game/constraints/spring-rotation-constraint.hpp new file mode 100644 index 0000000..4185241 --- /dev/null +++ b/src/game/constraints/spring-rotation-constraint.hpp @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_SPRING_ROTATION_CONSTRAINT_HPP +#define ANTKEEPER_GAME_SPRING_ROTATION_CONSTRAINT_HPP + +#include +#include + + +/** + * + */ +struct spring_rotation_constraint +{ + /// Yaw, pitch, and roll angle spring. + numeric_spring spring; +}; + + +#endif // ANTKEEPER_GAME_SPRING_ROTATION_CONSTRAINT_HPP diff --git a/src/game/constraints/spring-to-constraint.hpp b/src/game/constraints/spring-to-constraint.hpp new file mode 100644 index 0000000..e74c264 --- /dev/null +++ b/src/game/constraints/spring-to-constraint.hpp @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_SPRING_TO_CONSTRAINT_HPP +#define ANTKEEPER_GAME_SPRING_TO_CONSTRAINT_HPP + +#include +#include +#include + + +/** + * Springs to a target entity. + */ +struct spring_to_constraint +{ + /// Target entity ID. + entity::id target; + + /// Translation spring. + numeric_spring translation; + + /// Rotation spring. + numeric_spring rotation; + + /// Spring translation. + bool spring_translation; + + /// Spring rotation. + bool spring_rotation; +}; + + +#endif // ANTKEEPER_GAME_SPRING_TO_CONSTRAINT_HPP diff --git a/src/game/constraints/spring-translation-constraint.hpp b/src/game/constraints/spring-translation-constraint.hpp new file mode 100644 index 0000000..827269c --- /dev/null +++ b/src/game/constraints/spring-translation-constraint.hpp @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_SPRING_TRANSLATION_CONSTRAINT_HPP +#define ANTKEEPER_GAME_SPRING_TRANSLATION_CONSTRAINT_HPP + +#include +#include + + +/** + * + */ +struct spring_translation_constraint +{ + /// Translation spring. + numeric_spring spring; +}; + + +#endif // ANTKEEPER_GAME_SPRING_TRANSLATION_CONSTRAINT_HPP diff --git a/src/game/constraints/three-dof-constraint.hpp b/src/game/constraints/three-dof-constraint.hpp new file mode 100644 index 0000000..6a29502 --- /dev/null +++ b/src/game/constraints/three-dof-constraint.hpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_THREE_DOF_CONSTRAINT_HPP +#define ANTKEEPER_GAME_THREE_DOF_CONSTRAINT_HPP + + +/** + * Builds rotation from 3DoF angles. + */ +struct three_dof_constraint +{ + /// Yaw rotation angle, in radians. + float yaw; + + /// Pitch rotation angle, in radians. + float pitch; + + /// Roll rotation angle, in radians. + float roll; +}; + + +#endif // ANTKEEPER_GAME_THREE_DOF_CONSTRAINT_HPP diff --git a/src/game/constraints/track-to-constraint.hpp b/src/game/constraints/track-to-constraint.hpp new file mode 100644 index 0000000..beedaf5 --- /dev/null +++ b/src/game/constraints/track-to-constraint.hpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_TRACK_TO_CONSTRAINT_HPP +#define ANTKEEPER_GAME_TRACK_TO_CONSTRAINT_HPP + +#include +#include + + +/** + * Rotates a transform to face a target. + */ +struct track_to_constraint +{ + /// Target entity ID. + entity::id target; + + /// Up direction vector. + float3 up; +}; + + +#endif // ANTKEEPER_GAME_TRACK_TO_CONSTRAINT_HPP diff --git a/src/game/context.cpp b/src/game/context.cpp new file mode 100644 index 0000000..8c00572 --- /dev/null +++ b/src/game/context.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include "game/context.hpp" +#include "game/systems/orbit-system.hpp" + + +context::context() +{} + +context::~context() +{} + diff --git a/src/game/context.hpp b/src/game/context.hpp index eedf721..cabf144 100644 --- a/src/game/context.hpp +++ b/src/game/context.hpp @@ -20,36 +20,36 @@ #ifndef ANTKEEPER_GAME_CONTEXT_HPP #define ANTKEEPER_GAME_CONTEXT_HPP -#include "animation/tween.hpp" -#include "app/input-manager.hpp" -#include "app/window-manager.hpp" -#include "entity/id.hpp" -#include "entity/registry.hpp" -#include "event/subscription.hpp" +#include +#include +#include +#include +#include +#include #include "game/ecoregion.hpp" #include "game/loop.hpp" #include "game/state/base.hpp" -#include "geom/aabb.hpp" -#include "gl/framebuffer.hpp" -#include "gl/rasterizer.hpp" -#include "gl/texture-2d.hpp" -#include "gl/vertex-array.hpp" -#include "gl/vertex-buffer.hpp" -#include "i18n/string-map.hpp" -#include "input/action-map.hpp" -#include "input/action.hpp" -#include "input/mapper.hpp" -#include "math/moving-average.hpp" -#include "render/anti-aliasing-method.hpp" -#include "render/material-property.hpp" -#include "render/material.hpp" -#include "resources/json.hpp" -#include "scene/scene.hpp" -#include "type/bitmap-font.hpp" -#include "type/typeface.hpp" -#include "utility/dict.hpp" -#include "utility/fundamental-types.hpp" -#include "utility/state-machine.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include @@ -74,32 +74,6 @@ namespace debug class cli; } -namespace game -{ - namespace system - { - class subterrain; - class terrain; - class vegetation; - class spatial; - class astronomy; - class blackbody; - class atmosphere; - class orbit; - class behavior; - class collision; - class constraint; - class locomotion; - class camera; - class nest; - class render; - class steering; - class spring; - } - - struct control_profile; -} - namespace render { class bloom_pass; @@ -117,11 +91,33 @@ namespace render class ground_pass; } -namespace game { + +// Forward declaration of system types. +class subterrain_system; +class terrain_system; +class spatial_system; +class astronomy_system; +class blackbody_system; +class atmosphere_system; +class orbit_system; +class behavior_system; +class collision_system; +class constraint_system; +class locomotion_system; +class camera_system; +class nest_system; +class render_system; +class steering_system; +class spring_system; + +struct control_profile; /// Container for data shared between game states. struct context { + context(); + ~context(); + // Command-line options std::optional option_continue; std::optional option_data; @@ -177,13 +173,17 @@ struct context // Action maps, actions, and action event handling std::string control_profile_filename; - game::control_profile* control_profile; + ::control_profile* control_profile; + + std::unordered_map actions; + input::action_map window_action_map; + input::action_map menu_action_map; + input::action_map movement_action_map; + input::action_map nuptial_flight_action_map; input::mapper input_mapper; - input::action_map window_actions; + input::action fullscreen_action; input::action screenshot_action; - std::vector> window_action_subscriptions; - input::action_map menu_actions; input::action menu_up_action; input::action menu_down_action; input::action menu_left_action; @@ -191,9 +191,6 @@ struct context input::action menu_select_action; input::action menu_back_action; input::action menu_modifier_action; - std::vector> menu_action_subscriptions; - std::vector> menu_mouse_subscriptions; - input::action_map movement_actions; input::action move_forward_action; input::action move_back_action; input::action move_left_action; @@ -201,15 +198,20 @@ struct context input::action move_up_action; input::action move_down_action; input::action pause_action; + input::action pick_mate_action; + + std::vector> window_action_subscriptions; + std::vector> menu_action_subscriptions; + std::vector> menu_mouse_subscriptions; std::vector> movement_action_subscriptions; - + // Debugging scene::text* frame_time_text; debug::cli* cli; // Hierarchichal state machine - hsm::state_machine state_machine; + hsm::state_machine<::state::base> state_machine; std::function resume_callback; // Queue for scheduling "next frame" function calls @@ -221,7 +223,7 @@ struct context bool mouse_look; /// Game loop - game::loop loop; + ::loop loop; // Framebuffers gl::texture_2d* hdr_color_texture; @@ -327,22 +329,23 @@ struct context std::unordered_map entities; // Systems - game::system::behavior* behavior_system; - game::system::camera* camera_system; - game::system::collision* collision_system; - game::system::constraint* constraint_system; - game::system::locomotion* locomotion_system; - game::system::steering* steering_system; - game::system::render* render_system; - 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::blackbody* blackbody_system; - game::system::atmosphere* atmosphere_system; - game::system::astronomy* astronomy_system; - game::system::orbit* orbit_system; + ::behavior_system* behavior_system; + ::camera_system* camera_system; + ::collision_system* collision_system; + ::constraint_system* constraint_system; + ::locomotion_system* locomotion_system; + ::steering_system* steering_system; + ::render_system* render_system; + ::subterrain_system* subterrain_system; + ::terrain_system* terrain_system; + ::spring_system* spring_system; + ::spatial_system* spatial_system; + ::blackbody_system* blackbody_system; + ::atmosphere_system* atmosphere_system; + ::astronomy_system* astronomy_system; + ::orbit_system* orbit_system; + + std::unique_ptr<::orbit_system> unique_orbit; double3 rgb_wavelengths; @@ -351,6 +354,5 @@ struct context render::anti_aliasing_method anti_aliasing_method; }; -} // namespace game #endif // ANTKEEPER_GAME_CONTEXT_HPP diff --git a/src/game/control-profile.cpp b/src/game/control-profile.cpp index 38b8689..95a040b 100644 --- a/src/game/control-profile.cpp +++ b/src/game/control-profile.cpp @@ -18,11 +18,11 @@ */ #include "game/control-profile.hpp" -#include "resources/serializer.hpp" -#include "resources/serialize-error.hpp" -#include "resources/deserializer.hpp" -#include "resources/deserialize-error.hpp" -#include "debug/log.hpp" +#include +#include +#include +#include +#include /** * Serializes a control profile. @@ -34,7 +34,7 @@ * @throw serialize_error Unsupported mapping type. */ template <> -void serializer::serialize(const game::control_profile& profile, serialize_context& ctx) +void serializer<::control_profile>::serialize(const ::control_profile& profile, serialize_context& ctx) { // Write number of mappings std::uint64_t size = static_cast(profile.mappings.size()); @@ -97,7 +97,7 @@ void serializer::serialize(const game::control_profile& p * @throw deserialize_error Unsupported mapping type. */ template <> -void deserializer::deserialize(game::control_profile& profile, deserialize_context& ctx) +void deserializer<::control_profile>::deserialize(::control_profile& profile, deserialize_context& ctx) { profile.mappings.clear(); diff --git a/src/game/control-profile.hpp b/src/game/control-profile.hpp index c2a23f3..c0b86e5 100644 --- a/src/game/control-profile.hpp +++ b/src/game/control-profile.hpp @@ -20,13 +20,12 @@ #ifndef ANTKEEER_GAME_CONTROL_PROFILE_HPP #define ANTKEEER_GAME_CONTROL_PROFILE_HPP -#include "input/mapping.hpp" -#include "utility/dict.hpp" +#include +#include #include #include #include -namespace game { struct control_profile { @@ -38,6 +37,5 @@ public: dict settings; }; -} // namespace game #endif // ANTKEEER_GAME_CONTROL_PROFILE_HPP diff --git a/src/game/controls.cpp b/src/game/controls.cpp index e38c4ea..7f6ccce 100644 --- a/src/game/controls.cpp +++ b/src/game/controls.cpp @@ -22,16 +22,15 @@ #include "game/menu.hpp" #include "game/control-profile.hpp" #include "game/state/pause-menu.hpp" -#include "resources/resource-manager.hpp" -#include "resources/json.hpp" -#include "input/modifier-key.hpp" -#include "utility/hash/fnv1a.hpp" +#include +#include +#include +#include using namespace hash::literals; -namespace game { -void reset_control_profile(game::control_profile& profile) +void reset_control_profile(::control_profile& profile) { auto& mappings = profile.mappings; auto& settings = profile.settings; @@ -39,25 +38,31 @@ void reset_control_profile(game::control_profile& profile) mappings.clear(); settings.clear(); - // Window controls + // Fullscreen mappings.emplace("fullscreen"_fnv1a32, new input::key_mapping(nullptr, input::scancode::f11, 0, false)); mappings.emplace("fullscreen"_fnv1a32, new input::key_mapping(nullptr, input::scancode::enter, input::modifier_key::alt, false)); + + // Screenshot mappings.emplace("screenshot"_fnv1a32, new input::key_mapping(nullptr, input::scancode::f12, 0, false)); mappings.emplace("screenshot"_fnv1a32, new input::key_mapping(nullptr, input::scancode::print_screen, 0, false)); - // Menu controls + // Menu up mappings.emplace("menu_up"_fnv1a32, new input::key_mapping(nullptr, input::scancode::up, 0, true)); mappings.emplace("menu_up"_fnv1a32, new input::key_mapping(nullptr, input::scancode::w, 0, true)); mappings.emplace("menu_up"_fnv1a32, new input::key_mapping(nullptr, input::scancode::i, 0, true)); mappings.emplace("menu_up"_fnv1a32, new input::gamepad_axis_mapping(nullptr, input::gamepad_axis::left_stick_y, true)); mappings.emplace("menu_up"_fnv1a32, new input::gamepad_axis_mapping(nullptr, input::gamepad_axis::right_stick_y, true)); mappings.emplace("menu_up"_fnv1a32, new input::gamepad_button_mapping(nullptr, input::gamepad_button::dpad_up)); + + // Menu down mappings.emplace("menu_down"_fnv1a32, new input::key_mapping(nullptr, input::scancode::down, 0, true)); mappings.emplace("menu_down"_fnv1a32, new input::key_mapping(nullptr, input::scancode::s, 0, true)); mappings.emplace("menu_down"_fnv1a32, new input::key_mapping(nullptr, input::scancode::k, 0, true)); mappings.emplace("menu_down"_fnv1a32, new input::gamepad_axis_mapping(nullptr, input::gamepad_axis::left_stick_y, false)); mappings.emplace("menu_down"_fnv1a32, new input::gamepad_axis_mapping(nullptr, input::gamepad_axis::right_stick_y, false)); mappings.emplace("menu_down"_fnv1a32, new input::gamepad_button_mapping(nullptr, input::gamepad_button::dpad_down)); + + // Menu left mappings.emplace("menu_left"_fnv1a32, new input::key_mapping(nullptr, input::scancode::left, 0, true)); mappings.emplace("menu_left"_fnv1a32, new input::key_mapping(nullptr, input::scancode::a, 0, true)); mappings.emplace("menu_left"_fnv1a32, new input::key_mapping(nullptr, input::scancode::j, 0, true)); @@ -66,6 +71,8 @@ void reset_control_profile(game::control_profile& profile) mappings.emplace("menu_left"_fnv1a32, new input::gamepad_button_mapping(nullptr, input::gamepad_button::dpad_left)); mappings.emplace("menu_left"_fnv1a32, new input::mouse_scroll_mapping(nullptr, input::mouse_scroll_axis::x, true)); mappings.emplace("menu_left"_fnv1a32, new input::mouse_scroll_mapping(nullptr, input::mouse_scroll_axis::y, true)); + + // Menu right mappings.emplace("menu_right"_fnv1a32, new input::key_mapping(nullptr, input::scancode::right, 0, true)); mappings.emplace("menu_right"_fnv1a32, new input::key_mapping(nullptr, input::scancode::d, 0, true)); mappings.emplace("menu_right"_fnv1a32, new input::key_mapping(nullptr, input::scancode::l, 0, true)); @@ -74,36 +81,59 @@ void reset_control_profile(game::control_profile& profile) mappings.emplace("menu_right"_fnv1a32, new input::gamepad_button_mapping(nullptr, input::gamepad_button::dpad_right)); mappings.emplace("menu_right"_fnv1a32, new input::mouse_scroll_mapping(nullptr, input::mouse_scroll_axis::x, false)); mappings.emplace("menu_right"_fnv1a32, new input::mouse_scroll_mapping(nullptr, input::mouse_scroll_axis::y, false)); + + // Menu select mappings.emplace("menu_select"_fnv1a32, new input::key_mapping(nullptr, input::scancode::enter, 0, false)); mappings.emplace("menu_select"_fnv1a32, new input::key_mapping(nullptr, input::scancode::space, 0, false)); mappings.emplace("menu_select"_fnv1a32, new input::key_mapping(nullptr, input::scancode::e, 0, false)); mappings.emplace("menu_select"_fnv1a32, new input::gamepad_button_mapping(nullptr, input::gamepad_button::a)); + + // Menu back mappings.emplace("menu_back"_fnv1a32, new input::key_mapping(nullptr, input::scancode::escape, 0, false)); mappings.emplace("menu_back"_fnv1a32, new input::key_mapping(nullptr, input::scancode::backspace, 0, false)); mappings.emplace("menu_back"_fnv1a32, new input::key_mapping(nullptr, input::scancode::q, 0, false)); mappings.emplace("menu_back"_fnv1a32, new input::gamepad_button_mapping(nullptr, input::gamepad_button::b)); mappings.emplace("menu_back"_fnv1a32, new input::gamepad_button_mapping(nullptr, input::gamepad_button::back)); + + // Menu modifier mappings.emplace("menu_modifier"_fnv1a32, new input::key_mapping(nullptr, input::scancode::left_shift, 0, false)); mappings.emplace("menu_modifier"_fnv1a32, new input::key_mapping(nullptr, input::scancode::right_shift, 0, false)); - // Movement controls + // Move forward mappings.emplace("move_forward"_fnv1a32, new input::key_mapping(nullptr, input::scancode::w, 0, false)); mappings.emplace("move_forward"_fnv1a32, new input::gamepad_axis_mapping(nullptr, input::gamepad_axis::left_stick_y, true)); + + // Move back mappings.emplace("move_back"_fnv1a32, new input::key_mapping(nullptr, input::scancode::s, 0, false)); mappings.emplace("move_back"_fnv1a32, new input::gamepad_axis_mapping(nullptr, input::gamepad_axis::left_stick_y, false)); + + // Move left mappings.emplace("move_left"_fnv1a32, new input::key_mapping(nullptr, input::scancode::a, 0, false)); mappings.emplace("move_left"_fnv1a32, new input::gamepad_axis_mapping(nullptr, input::gamepad_axis::left_stick_x, true)); + + // Move right mappings.emplace("move_right"_fnv1a32, new input::key_mapping(nullptr, input::scancode::d, 0, false)); mappings.emplace("move_right"_fnv1a32, new input::gamepad_axis_mapping(nullptr, input::gamepad_axis::left_stick_x, false)); + + // Move up mappings.emplace("move_up"_fnv1a32, new input::mouse_scroll_mapping(nullptr, input::mouse_scroll_axis::y, false)); mappings.emplace("move_up"_fnv1a32, new input::gamepad_axis_mapping(nullptr, input::gamepad_axis::right_trigger, false)); + + // Move down mappings.emplace("move_down"_fnv1a32, new input::mouse_scroll_mapping(nullptr, input::mouse_scroll_axis::y, true)); mappings.emplace("move_down"_fnv1a32, new input::gamepad_axis_mapping(nullptr, input::gamepad_axis::left_trigger, false)); + + // Pause mappings.emplace("pause"_fnv1a32, new input::key_mapping(nullptr, input::scancode::escape, 0, false)); mappings.emplace("pause"_fnv1a32, new input::gamepad_button_mapping(nullptr, input::gamepad_button::start)); + + // Pick mate + mappings.emplace("pick_mate"_fnv1a32, new input::mouse_button_mapping(nullptr, input::mouse_button::left)); + mappings.emplace("pick_mate"_fnv1a32, new input::mouse_button_mapping(nullptr, input::mouse_button::middle)); + mappings.emplace("pick_mate"_fnv1a32, new input::mouse_button_mapping(nullptr, input::mouse_button::right)); } -void apply_control_profile(game::context& ctx, const game::control_profile& profile) +void apply_control_profile(::context& ctx, const ::control_profile& profile) { auto add_mappings = [&profile](input::action_map& map, input::action& action, std::uint32_t key) { @@ -115,32 +145,35 @@ void apply_control_profile(game::context& ctx, const game::control_profile& prof }; // Window controls - ctx.window_actions.remove_mappings(); - add_mappings(ctx.window_actions, ctx.fullscreen_action, "fullscreen"_fnv1a32); - add_mappings(ctx.window_actions, ctx.screenshot_action, "screenshot"_fnv1a32); + ctx.window_action_map.remove_mappings(); + add_mappings(ctx.window_action_map, ctx.fullscreen_action, "fullscreen"_fnv1a32); + add_mappings(ctx.window_action_map, ctx.screenshot_action, "screenshot"_fnv1a32); // Menu controls - ctx.menu_actions.remove_mappings(); - add_mappings(ctx.menu_actions, ctx.menu_up_action, "menu_up"_fnv1a32); - add_mappings(ctx.menu_actions, ctx.menu_down_action, "menu_down"_fnv1a32); - add_mappings(ctx.menu_actions, ctx.menu_left_action, "menu_left"_fnv1a32); - add_mappings(ctx.menu_actions, ctx.menu_right_action, "menu_right"_fnv1a32); - add_mappings(ctx.menu_actions, ctx.menu_select_action, "menu_select"_fnv1a32); - add_mappings(ctx.menu_actions, ctx.menu_back_action, "menu_back"_fnv1a32); - add_mappings(ctx.menu_actions, ctx.menu_modifier_action, "menu_modifier"_fnv1a32); + ctx.menu_action_map.remove_mappings(); + add_mappings(ctx.menu_action_map, ctx.menu_up_action, "menu_up"_fnv1a32); + add_mappings(ctx.menu_action_map, ctx.menu_down_action, "menu_down"_fnv1a32); + add_mappings(ctx.menu_action_map, ctx.menu_left_action, "menu_left"_fnv1a32); + add_mappings(ctx.menu_action_map, ctx.menu_right_action, "menu_right"_fnv1a32); + add_mappings(ctx.menu_action_map, ctx.menu_select_action, "menu_select"_fnv1a32); + add_mappings(ctx.menu_action_map, ctx.menu_back_action, "menu_back"_fnv1a32); + add_mappings(ctx.menu_action_map, ctx.menu_modifier_action, "menu_modifier"_fnv1a32); // Movement controls - ctx.movement_actions.remove_mappings(); - add_mappings(ctx.movement_actions, ctx.move_forward_action, "move_forward"_fnv1a32); - add_mappings(ctx.movement_actions, ctx.move_back_action, "move_back"_fnv1a32); - add_mappings(ctx.movement_actions, ctx.move_left_action, "move_left"_fnv1a32); - add_mappings(ctx.movement_actions, ctx.move_right_action, "move_right"_fnv1a32); - add_mappings(ctx.movement_actions, ctx.move_up_action, "move_up"_fnv1a32); - add_mappings(ctx.movement_actions, ctx.move_down_action, "move_down"_fnv1a32); - add_mappings(ctx.movement_actions, ctx.pause_action, "pause"_fnv1a32); + ctx.movement_action_map.remove_mappings(); + add_mappings(ctx.movement_action_map, ctx.move_forward_action, "move_forward"_fnv1a32); + add_mappings(ctx.movement_action_map, ctx.move_back_action, "move_back"_fnv1a32); + add_mappings(ctx.movement_action_map, ctx.move_left_action, "move_left"_fnv1a32); + add_mappings(ctx.movement_action_map, ctx.move_right_action, "move_right"_fnv1a32); + add_mappings(ctx.movement_action_map, ctx.move_up_action, "move_up"_fnv1a32); + add_mappings(ctx.movement_action_map, ctx.move_down_action, "move_down"_fnv1a32); + add_mappings(ctx.movement_action_map, ctx.pause_action, "pause"_fnv1a32); + + // Nuptial flight controls + add_mappings(ctx.nuptial_flight_action_map, ctx.pick_mate_action, "pick_mate"_fnv1a32); } -void update_control_profile(game::context& ctx, game::control_profile& profile) +void update_control_profile(::context& ctx, ::control_profile& profile) { auto add_mappings = [&profile](const input::action_map& map, const input::action& action, std::uint32_t key) { @@ -180,29 +213,32 @@ void update_control_profile(game::context& ctx, game::control_profile& profile) profile.mappings.clear(); // Window controls - add_mappings(ctx.window_actions, ctx.fullscreen_action, "fullscreen"_fnv1a32); - add_mappings(ctx.window_actions, ctx.screenshot_action, "screenshot"_fnv1a32); + add_mappings(ctx.window_action_map, ctx.fullscreen_action, "fullscreen"_fnv1a32); + add_mappings(ctx.window_action_map, ctx.screenshot_action, "screenshot"_fnv1a32); // Menu controls - add_mappings(ctx.menu_actions, ctx.menu_up_action, "menu_up"_fnv1a32); - add_mappings(ctx.menu_actions, ctx.menu_down_action, "menu_down"_fnv1a32); - add_mappings(ctx.menu_actions, ctx.menu_left_action, "menu_left"_fnv1a32); - add_mappings(ctx.menu_actions, ctx.menu_right_action, "menu_right"_fnv1a32); - add_mappings(ctx.menu_actions, ctx.menu_select_action, "menu_select"_fnv1a32); - add_mappings(ctx.menu_actions, ctx.menu_back_action, "menu_back"_fnv1a32); - add_mappings(ctx.menu_actions, ctx.menu_modifier_action, "menu_modifier"_fnv1a32); + add_mappings(ctx.menu_action_map, ctx.menu_up_action, "menu_up"_fnv1a32); + add_mappings(ctx.menu_action_map, ctx.menu_down_action, "menu_down"_fnv1a32); + add_mappings(ctx.menu_action_map, ctx.menu_left_action, "menu_left"_fnv1a32); + add_mappings(ctx.menu_action_map, ctx.menu_right_action, "menu_right"_fnv1a32); + add_mappings(ctx.menu_action_map, ctx.menu_select_action, "menu_select"_fnv1a32); + add_mappings(ctx.menu_action_map, ctx.menu_back_action, "menu_back"_fnv1a32); + add_mappings(ctx.menu_action_map, ctx.menu_modifier_action, "menu_modifier"_fnv1a32); // Movement controls - add_mappings(ctx.movement_actions, ctx.move_forward_action, "move_forward"_fnv1a32); - add_mappings(ctx.movement_actions, ctx.move_back_action, "move_back"_fnv1a32); - add_mappings(ctx.movement_actions, ctx.move_left_action, "move_left"_fnv1a32); - add_mappings(ctx.movement_actions, ctx.move_right_action, "move_right"_fnv1a32); - add_mappings(ctx.movement_actions, ctx.move_up_action, "move_up"_fnv1a32); - add_mappings(ctx.movement_actions, ctx.move_down_action, "move_down"_fnv1a32); - add_mappings(ctx.movement_actions, ctx.pause_action, "pause"_fnv1a32); + add_mappings(ctx.movement_action_map, ctx.move_forward_action, "move_forward"_fnv1a32); + add_mappings(ctx.movement_action_map, ctx.move_back_action, "move_back"_fnv1a32); + add_mappings(ctx.movement_action_map, ctx.move_left_action, "move_left"_fnv1a32); + add_mappings(ctx.movement_action_map, ctx.move_right_action, "move_right"_fnv1a32); + add_mappings(ctx.movement_action_map, ctx.move_up_action, "move_up"_fnv1a32); + add_mappings(ctx.movement_action_map, ctx.move_down_action, "move_down"_fnv1a32); + add_mappings(ctx.movement_action_map, ctx.pause_action, "pause"_fnv1a32); + + // Nuptial flight controls + add_mappings(ctx.nuptial_flight_action_map, ctx.pick_mate_action, "pick_mate"_fnv1a32); } -void setup_window_controls(game::context& ctx) +void setup_window_controls(::context& ctx) { // Setup fullscreen control ctx.window_action_subscriptions.emplace_back @@ -223,13 +259,13 @@ void setup_window_controls(game::context& ctx) ( [&ctx](const auto& event) { - game::graphics::save_screenshot(ctx); + ::graphics::save_screenshot(ctx); } ) ); } -void setup_menu_controls(game::context& ctx) +void setup_menu_controls(::context& ctx) { // Setup menu controls ctx.menu_action_subscriptions.emplace_back @@ -242,7 +278,7 @@ void setup_menu_controls(game::context& ctx) if (*ctx.menu_item_index < 0) *ctx.menu_item_index = static_cast(ctx.menu_item_texts.size()) - 1; - game::menu::update_text_color(ctx); + ::menu::update_text_color(ctx); } ) ); @@ -256,7 +292,7 @@ void setup_menu_controls(game::context& ctx) if (*ctx.menu_item_index >= ctx.menu_item_texts.size()) *ctx.menu_item_index = 0; - game::menu::update_text_color(ctx); + ::menu::update_text_color(ctx); } ) ); @@ -319,7 +355,7 @@ void setup_menu_controls(game::context& ctx) ctx.menu_right_action.set_threshold_function(menu_action_threshold); } -void setup_game_controls(game::context& ctx) +void setup_game_controls(::context& ctx) { // Setup pause control ctx.movement_action_subscriptions.emplace_back @@ -336,10 +372,10 @@ void setup_game_controls(game::context& ctx) [&ctx]() { // Disable game controls - game::disable_game_controls(ctx); + ::disable_game_controls(ctx); // Push pause menu state - ctx.state_machine.emplace(new game::state::pause_menu(ctx)); + ctx.state_machine.emplace(new ::state::pause_menu(ctx)); } ); @@ -355,14 +391,14 @@ void setup_game_controls(game::context& ctx) ); } -void enable_window_controls(game::context& ctx) +void enable_window_controls(::context& ctx) { - ctx.window_actions.connect(ctx.input_manager->get_event_queue()); + ctx.window_action_map.enable(); } -void enable_menu_controls(game::context& ctx) +void enable_menu_controls(::context& ctx) { - ctx.menu_actions.connect(ctx.input_manager->get_event_queue()); + ctx.menu_action_map.enable(); // Function to select menu item at mouse position auto select_menu_item = [&ctx](const math::vector& mouse_position) -> bool @@ -402,7 +438,7 @@ void enable_menu_controls(game::context& ctx) if (y >= min_y && y <= max_y) { *ctx.menu_item_index = static_cast(i); - game::menu::update_text_color(ctx); + ::menu::update_text_color(ctx); return true; } } @@ -461,19 +497,27 @@ void enable_menu_controls(game::context& ctx) ); } -void enable_game_controls(game::context& ctx) +void enable_game_controls(::context& ctx) +{ + ctx.movement_action_map.enable(); +} + +void enable_nuptial_flight_controls(::context& ctx) { - ctx.movement_actions.connect(ctx.input_manager->get_event_queue()); + ctx.nuptial_flight_action_map.enable(); } -void disable_window_controls(game::context& ctx) +void disable_window_controls(::context& ctx) { - ctx.window_actions.disconnect(); + ctx.window_action_map.disable(); } -void disable_menu_controls(game::context& ctx) +void disable_menu_controls(::context& ctx) { - // Reset menu action states + ctx.menu_action_map.disable(); + + ctx.menu_mouse_subscriptions.clear(); + ctx.menu_up_action.reset(); ctx.menu_down_action.reset(); ctx.menu_left_action.reset(); @@ -481,14 +525,11 @@ void disable_menu_controls(game::context& ctx) ctx.menu_select_action.reset(); ctx.menu_back_action.reset(); ctx.menu_modifier_action.reset(); - - ctx.menu_actions.disconnect(); - ctx.menu_mouse_subscriptions.clear(); } -void disable_game_controls(game::context& ctx) +void disable_game_controls(::context& ctx) { - ctx.movement_actions.disconnect(); + ctx.movement_action_map.disable(); ctx.move_forward_action.reset(); ctx.move_back_action.reset(); @@ -499,4 +540,10 @@ void disable_game_controls(game::context& ctx) ctx.pause_action.reset(); } -} // namespace game +void disable_nuptial_flight_controls(::context& ctx) +{ + ctx.nuptial_flight_action_map.disable(); + + ctx.pick_mate_action.reset(); +} + diff --git a/src/game/controls.hpp b/src/game/controls.hpp index a6de50a..f3e44a0 100644 --- a/src/game/controls.hpp +++ b/src/game/controls.hpp @@ -21,18 +21,17 @@ #define ANTKEEPER_GAME_CONTROLS_HPP #include "game/context.hpp" -#include "resources/json.hpp" -#include "input/gamepad.hpp" +#include +#include #include -namespace game { /** * Resets a control profile to default settings. * * @param profile Control profile to reset. */ -void reset_control_profile(game::control_profile& profile); +void reset_control_profile(::control_profile& profile); /** * Applies a control profile to the game context. @@ -40,7 +39,7 @@ void reset_control_profile(game::control_profile& profile); * @param ctx Game context. * @param profile Control profile to apply. */ -void apply_control_profile(game::context& ctx, const game::control_profile& profile); +void apply_control_profile(::context& ctx, const ::control_profile& profile); /** * Updates a control profile after actions have been remapped. @@ -48,20 +47,21 @@ void apply_control_profile(game::context& ctx, const game::control_profile& prof * @param ctx Game context. * @param profile Control profile to update. */ -void update_control_profile(game::context& ctx, game::control_profile& profile); +void update_control_profile(::context& ctx, ::control_profile& profile); -void setup_window_controls(game::context& ctx); -void setup_menu_controls(game::context& ctx); -void setup_game_controls(game::context& ctx); +void setup_window_controls(::context& ctx); +void setup_menu_controls(::context& ctx); +void setup_game_controls(::context& ctx); -void enable_window_controls(game::context& ctx); -void enable_menu_controls(game::context& ctx); -void enable_game_controls(game::context& ctx); +void enable_window_controls(::context& ctx); +void enable_menu_controls(::context& ctx); +void enable_game_controls(::context& ctx); +void enable_nuptial_flight_controls(::context& ctx); -void disable_window_controls(game::context& ctx); -void disable_menu_controls(game::context& ctx); -void disable_game_controls(game::context& ctx); +void disable_window_controls(::context& ctx); +void disable_menu_controls(::context& ctx); +void disable_game_controls(::context& ctx); +void disable_nuptial_flight_controls(::context& ctx); -} // namespace game #endif // ANTKEEPER_GAME_CONTROLS_HPP diff --git a/src/game/ecoregion-loader.cpp b/src/game/ecoregion-loader.cpp index 1b55b70..ffa7c71 100644 --- a/src/game/ecoregion-loader.cpp +++ b/src/game/ecoregion-loader.cpp @@ -18,13 +18,13 @@ */ #include "game/ecoregion.hpp" -#include "resources/resource-loader.hpp" -#include "resources/resource-manager.hpp" -#include "resources/json.hpp" +#include +#include +#include #include template <> -game::ecoregion* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +::ecoregion* resource_loader<::ecoregion>::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) { // Load JSON data json* data = resource_loader::load(resource_manager, file, path); @@ -35,7 +35,7 @@ game::ecoregion* resource_loader::load(resource_manager* resour throw std::runtime_error("Invalid ecoregion file."); // Allocate and init ecoregion - game::ecoregion* ecoregion = new game::ecoregion(); + ::ecoregion* ecoregion = new ::ecoregion(); ecoregion->elevation = 0.0f; ecoregion->latitude = 0.0f; ecoregion->longitude = 0.0f; @@ -73,7 +73,7 @@ game::ecoregion* resource_loader::load(resource_manager* resour { // Allocate gene pool ecoregion->gene_pools.resize(ecoregion->gene_pools.size() + 1); - game::ant::gene_pool& gene_pool = ecoregion->gene_pools.back(); + ::ant::gene_pool& gene_pool = ecoregion->gene_pools.back(); // Read gene pool name if (auto name_element = gene_pool_element->find("name"); name_element != gene_pool_element->end()) @@ -88,12 +88,12 @@ game::ecoregion* resource_loader::load(resource_manager* resour for (auto antennae_element = antennae_elements->begin(); antennae_element != antennae_elements->end(); ++antennae_element) { float weight = 0.0f; - const game::ant::gene::antennae* gene = nullptr; + const ::ant::gene::antennae* gene = nullptr; if (auto weight_element = antennae_element->find("weight"); weight_element != antennae_element->end()) weight = weight_element->get(); if (auto gene_element = antennae_element->find("gene"); gene_element != antennae_element->end()) - gene = resource_manager->load(gene_element->get()); + gene = resource_manager->load<::ant::gene::antennae>(gene_element->get()); if (gene) { @@ -109,12 +109,12 @@ game::ecoregion* resource_loader::load(resource_manager* resour for (auto body_size_element = body_size_elements->begin(); body_size_element != body_size_elements->end(); ++body_size_element) { float weight = 0.0f; - const game::ant::gene::body_size* gene = nullptr; + const ::ant::gene::body_size* gene = nullptr; if (auto weight_element = body_size_element->find("weight"); weight_element != body_size_element->end()) weight = weight_element->get(); if (auto gene_element = body_size_element->find("gene"); gene_element != body_size_element->end()) - gene = resource_manager->load(gene_element->get()); + gene = resource_manager->load<::ant::gene::body_size>(gene_element->get()); if (gene) { @@ -130,12 +130,12 @@ game::ecoregion* resource_loader::load(resource_manager* resour for (auto cocoon_element = cocoon_elements->begin(); cocoon_element != cocoon_elements->end(); ++cocoon_element) { float weight = 0.0f; - const game::ant::gene::cocoon* gene = nullptr; + const ::ant::gene::cocoon* gene = nullptr; if (auto weight_element = cocoon_element->find("weight"); weight_element != cocoon_element->end()) weight = weight_element->get(); if (auto gene_element = cocoon_element->find("gene"); gene_element != cocoon_element->end()) - gene = resource_manager->load(gene_element->get()); + gene = resource_manager->load<::ant::gene::cocoon>(gene_element->get()); if (gene) { @@ -151,12 +151,12 @@ game::ecoregion* resource_loader::load(resource_manager* resour for (auto diet_element = diet_elements->begin(); diet_element != diet_elements->end(); ++diet_element) { float weight = 0.0f; - const game::ant::gene::diet* gene = nullptr; + const ::ant::gene::diet* gene = nullptr; if (auto weight_element = diet_element->find("weight"); weight_element != diet_element->end()) weight = weight_element->get(); if (auto gene_element = diet_element->find("gene"); gene_element != diet_element->end()) - gene = resource_manager->load(gene_element->get()); + gene = resource_manager->load<::ant::gene::diet>(gene_element->get()); if (gene) { @@ -172,12 +172,12 @@ game::ecoregion* resource_loader::load(resource_manager* resour for (auto egg_element = egg_elements->begin(); egg_element != egg_elements->end(); ++egg_element) { float weight = 0.0f; - const game::ant::gene::egg* gene = nullptr; + const ::ant::gene::egg* gene = nullptr; if (auto weight_element = egg_element->find("weight"); weight_element != egg_element->end()) weight = weight_element->get(); if (auto gene_element = egg_element->find("gene"); gene_element != egg_element->end()) - gene = resource_manager->load(gene_element->get()); + gene = resource_manager->load<::ant::gene::egg>(gene_element->get()); if (gene) { @@ -193,12 +193,12 @@ game::ecoregion* resource_loader::load(resource_manager* resour for (auto eyes_element = eyes_elements->begin(); eyes_element != eyes_elements->end(); ++eyes_element) { float weight = 0.0f; - const game::ant::gene::eyes* gene = nullptr; + const ::ant::gene::eyes* gene = nullptr; if (auto weight_element = eyes_element->find("weight"); weight_element != eyes_element->end()) weight = weight_element->get(); if (auto gene_element = eyes_element->find("gene"); gene_element != eyes_element->end()) - gene = resource_manager->load(gene_element->get()); + gene = resource_manager->load<::ant::gene::eyes>(gene_element->get()); if (gene) { @@ -214,12 +214,12 @@ game::ecoregion* resource_loader::load(resource_manager* resour for (auto foraging_time_element = foraging_time_elements->begin(); foraging_time_element != foraging_time_elements->end(); ++foraging_time_element) { float weight = 0.0f; - const game::ant::gene::foraging_time* gene = nullptr; + const ::ant::gene::foraging_time* gene = nullptr; if (auto weight_element = foraging_time_element->find("weight"); weight_element != foraging_time_element->end()) weight = weight_element->get(); if (auto gene_element = foraging_time_element->find("gene"); gene_element != foraging_time_element->end()) - gene = resource_manager->load(gene_element->get()); + gene = resource_manager->load<::ant::gene::foraging_time>(gene_element->get()); if (gene) { @@ -235,12 +235,12 @@ game::ecoregion* resource_loader::load(resource_manager* resour for (auto founding_mode_element = founding_mode_elements->begin(); founding_mode_element != founding_mode_elements->end(); ++founding_mode_element) { float weight = 0.0f; - const game::ant::gene::founding_mode* gene = nullptr; + const ::ant::gene::founding_mode* gene = nullptr; if (auto weight_element = founding_mode_element->find("weight"); weight_element != founding_mode_element->end()) weight = weight_element->get(); if (auto gene_element = founding_mode_element->find("gene"); gene_element != founding_mode_element->end()) - gene = resource_manager->load(gene_element->get()); + gene = resource_manager->load<::ant::gene::founding_mode>(gene_element->get()); if (gene) { @@ -256,12 +256,12 @@ game::ecoregion* resource_loader::load(resource_manager* resour for (auto gaster_element = gaster_elements->begin(); gaster_element != gaster_elements->end(); ++gaster_element) { float weight = 0.0f; - const game::ant::gene::gaster* gene = nullptr; + const ::ant::gene::gaster* gene = nullptr; if (auto weight_element = gaster_element->find("weight"); weight_element != gaster_element->end()) weight = weight_element->get(); if (auto gene_element = gaster_element->find("gene"); gene_element != gaster_element->end()) - gene = resource_manager->load(gene_element->get()); + gene = resource_manager->load<::ant::gene::gaster>(gene_element->get()); if (gene) { @@ -277,12 +277,12 @@ game::ecoregion* resource_loader::load(resource_manager* resour for (auto head_element = head_elements->begin(); head_element != head_elements->end(); ++head_element) { float weight = 0.0f; - const game::ant::gene::head* gene = nullptr; + const ::ant::gene::head* gene = nullptr; if (auto weight_element = head_element->find("weight"); weight_element != head_element->end()) weight = weight_element->get(); if (auto gene_element = head_element->find("gene"); gene_element != head_element->end()) - gene = resource_manager->load(gene_element->get()); + gene = resource_manager->load<::ant::gene::head>(gene_element->get()); if (gene) { @@ -298,12 +298,12 @@ game::ecoregion* resource_loader::load(resource_manager* resour for (auto larva_element = larva_elements->begin(); larva_element != larva_elements->end(); ++larva_element) { float weight = 0.0f; - const game::ant::gene::larva* gene = nullptr; + const ::ant::gene::larva* gene = nullptr; if (auto weight_element = larva_element->find("weight"); weight_element != larva_element->end()) weight = weight_element->get(); if (auto gene_element = larva_element->find("gene"); gene_element != larva_element->end()) - gene = resource_manager->load(gene_element->get()); + gene = resource_manager->load<::ant::gene::larva>(gene_element->get()); if (gene) { @@ -319,12 +319,12 @@ game::ecoregion* resource_loader::load(resource_manager* resour for (auto legs_element = legs_elements->begin(); legs_element != legs_elements->end(); ++legs_element) { float weight = 0.0f; - const game::ant::gene::legs* gene = nullptr; + const ::ant::gene::legs* gene = nullptr; if (auto weight_element = legs_element->find("weight"); weight_element != legs_element->end()) weight = weight_element->get(); if (auto gene_element = legs_element->find("gene"); gene_element != legs_element->end()) - gene = resource_manager->load(gene_element->get()); + gene = resource_manager->load<::ant::gene::legs>(gene_element->get()); if (gene) { @@ -340,12 +340,12 @@ game::ecoregion* resource_loader::load(resource_manager* resour for (auto mandibles_element = mandibles_elements->begin(); mandibles_element != mandibles_elements->end(); ++mandibles_element) { float weight = 0.0f; - const game::ant::gene::mandibles* gene = nullptr; + const ::ant::gene::mandibles* gene = nullptr; if (auto weight_element = mandibles_element->find("weight"); weight_element != mandibles_element->end()) weight = weight_element->get(); if (auto gene_element = mandibles_element->find("gene"); gene_element != mandibles_element->end()) - gene = resource_manager->load(gene_element->get()); + gene = resource_manager->load<::ant::gene::mandibles>(gene_element->get()); if (gene) { @@ -361,12 +361,12 @@ game::ecoregion* resource_loader::load(resource_manager* resour for (auto mesosoma_element = mesosoma_elements->begin(); mesosoma_element != mesosoma_elements->end(); ++mesosoma_element) { float weight = 0.0f; - const game::ant::gene::mesosoma* gene = nullptr; + const ::ant::gene::mesosoma* gene = nullptr; if (auto weight_element = mesosoma_element->find("weight"); weight_element != mesosoma_element->end()) weight = weight_element->get(); if (auto gene_element = mesosoma_element->find("gene"); gene_element != mesosoma_element->end()) - gene = resource_manager->load(gene_element->get()); + gene = resource_manager->load<::ant::gene::mesosoma>(gene_element->get()); if (gene) { @@ -382,12 +382,12 @@ game::ecoregion* resource_loader::load(resource_manager* resour for (auto nest_site_element = nest_site_elements->begin(); nest_site_element != nest_site_elements->end(); ++nest_site_element) { float weight = 0.0f; - const game::ant::gene::nest_site* gene = nullptr; + const ::ant::gene::nest_site* gene = nullptr; if (auto weight_element = nest_site_element->find("weight"); weight_element != nest_site_element->end()) weight = weight_element->get(); if (auto gene_element = nest_site_element->find("gene"); gene_element != nest_site_element->end()) - gene = resource_manager->load(gene_element->get()); + gene = resource_manager->load<::ant::gene::nest_site>(gene_element->get()); if (gene) { @@ -403,12 +403,12 @@ game::ecoregion* resource_loader::load(resource_manager* resour for (auto ocelli_element = ocelli_elements->begin(); ocelli_element != ocelli_elements->end(); ++ocelli_element) { float weight = 0.0f; - const game::ant::gene::ocelli* gene = nullptr; + const ::ant::gene::ocelli* gene = nullptr; if (auto weight_element = ocelli_element->find("weight"); weight_element != ocelli_element->end()) weight = weight_element->get(); if (auto gene_element = ocelli_element->find("gene"); gene_element != ocelli_element->end()) - gene = resource_manager->load(gene_element->get()); + gene = resource_manager->load<::ant::gene::ocelli>(gene_element->get()); if (gene) { @@ -424,12 +424,12 @@ game::ecoregion* resource_loader::load(resource_manager* resour for (auto pigmentation_element = pigmentation_elements->begin(); pigmentation_element != pigmentation_elements->end(); ++pigmentation_element) { float weight = 0.0f; - const game::ant::gene::pigmentation* gene = nullptr; + const ::ant::gene::pigmentation* gene = nullptr; if (auto weight_element = pigmentation_element->find("weight"); weight_element != pigmentation_element->end()) weight = weight_element->get(); if (auto gene_element = pigmentation_element->find("gene"); gene_element != pigmentation_element->end()) - gene = resource_manager->load(gene_element->get()); + gene = resource_manager->load<::ant::gene::pigmentation>(gene_element->get()); if (gene) { @@ -445,12 +445,12 @@ game::ecoregion* resource_loader::load(resource_manager* resour for (auto pilosity_element = pilosity_elements->begin(); pilosity_element != pilosity_elements->end(); ++pilosity_element) { float weight = 0.0f; - const game::ant::gene::pilosity* gene = nullptr; + const ::ant::gene::pilosity* gene = nullptr; if (auto weight_element = pilosity_element->find("weight"); weight_element != pilosity_element->end()) weight = weight_element->get(); if (auto gene_element = pilosity_element->find("gene"); gene_element != pilosity_element->end()) - gene = resource_manager->load(gene_element->get()); + gene = resource_manager->load<::ant::gene::pilosity>(gene_element->get()); if (gene) { @@ -466,12 +466,12 @@ game::ecoregion* resource_loader::load(resource_manager* resour for (auto sculpturing_element = sculpturing_elements->begin(); sculpturing_element != sculpturing_elements->end(); ++sculpturing_element) { float weight = 0.0f; - const game::ant::gene::sculpturing* gene = nullptr; + const ::ant::gene::sculpturing* gene = nullptr; if (auto weight_element = sculpturing_element->find("weight"); weight_element != sculpturing_element->end()) weight = weight_element->get(); if (auto gene_element = sculpturing_element->find("gene"); gene_element != sculpturing_element->end()) - gene = resource_manager->load(gene_element->get()); + gene = resource_manager->load<::ant::gene::sculpturing>(gene_element->get()); if (gene) { @@ -487,12 +487,12 @@ game::ecoregion* resource_loader::load(resource_manager* resour for (auto sting_element = sting_elements->begin(); sting_element != sting_elements->end(); ++sting_element) { float weight = 0.0f; - const game::ant::gene::sting* gene = nullptr; + const ::ant::gene::sting* gene = nullptr; if (auto weight_element = sting_element->find("weight"); weight_element != sting_element->end()) weight = weight_element->get(); if (auto gene_element = sting_element->find("gene"); gene_element != sting_element->end()) - gene = resource_manager->load(gene_element->get()); + gene = resource_manager->load<::ant::gene::sting>(gene_element->get()); if (gene) { @@ -508,12 +508,12 @@ game::ecoregion* resource_loader::load(resource_manager* resour for (auto waist_element = waist_elements->begin(); waist_element != waist_elements->end(); ++waist_element) { float weight = 0.0f; - const game::ant::gene::waist* gene = nullptr; + const ::ant::gene::waist* gene = nullptr; if (auto weight_element = waist_element->find("weight"); weight_element != waist_element->end()) weight = weight_element->get(); if (auto gene_element = waist_element->find("gene"); gene_element != waist_element->end()) - gene = resource_manager->load(gene_element->get()); + gene = resource_manager->load<::ant::gene::waist>(gene_element->get()); if (gene) { @@ -529,12 +529,12 @@ game::ecoregion* resource_loader::load(resource_manager* resour for (auto wings_element = wings_elements->begin(); wings_element != wings_elements->end(); ++wings_element) { float weight = 0.0f; - const game::ant::gene::wings* gene = nullptr; + const ::ant::gene::wings* gene = nullptr; if (auto weight_element = wings_element->find("weight"); weight_element != wings_element->end()) weight = weight_element->get(); if (auto gene_element = wings_element->find("gene"); gene_element != wings_element->end()) - gene = resource_manager->load(gene_element->get()); + gene = resource_manager->load<::ant::gene::wings>(gene_element->get()); if (gene) { diff --git a/src/game/ecoregion.hpp b/src/game/ecoregion.hpp index dfb3ad1..dcb0d59 100644 --- a/src/game/ecoregion.hpp +++ b/src/game/ecoregion.hpp @@ -20,13 +20,12 @@ #ifndef ANTKEEPER_GAME_ECOREGION_HPP #define ANTKEEPER_GAME_ECOREGION_HPP -#include "utility/fundamental-types.hpp" -#include "render/material.hpp" +#include +#include #include "game/ant/gene-pool.hpp" #include #include -namespace game { /** * @@ -61,6 +60,5 @@ struct ecoregion std::discrete_distribution gene_pool_distribution; }; -} // namespace game #endif // ANTKEEPER_GAME_ECOREGION_HPP diff --git a/src/game/fonts.cpp b/src/game/fonts.cpp index 90147a2..c328556 100644 --- a/src/game/fonts.cpp +++ b/src/game/fonts.cpp @@ -18,19 +18,18 @@ */ #include "game/fonts.hpp" -#include "type/type.hpp" -#include "resources/resource-manager.hpp" -#include "gl/texture-wrapping.hpp" -#include "gl/texture-filter.hpp" -#include "render/material.hpp" -#include "render/material-flags.hpp" -#include "utility/hash/fnv1a.hpp" +#include +#include +#include +#include +#include +#include +#include #include "game/strings.hpp" #include using namespace hash::literals; -namespace game { static void build_bitmap_font(const type::typeface& typeface, float size, const std::unordered_set& charset, type::bitmap_font& font, render::material& material, gl::shader_program* shader_program) { @@ -69,7 +68,7 @@ static void build_bitmap_font(const type::typeface& typeface, float size, const material.set_shader_program(shader_program); } -void load_fonts(game::context& ctx) +void load_fonts(::context& ctx) { // Load dyslexia-friendly typeface (if enabled) bool dyslexia_font_loaded = false; @@ -148,4 +147,3 @@ void load_fonts(game::context& ctx) } } -} // namespace game diff --git a/src/game/fonts.hpp b/src/game/fonts.hpp index ab2ca6f..45a47c0 100644 --- a/src/game/fonts.hpp +++ b/src/game/fonts.hpp @@ -22,10 +22,8 @@ #include "game/context.hpp" -namespace game { -void load_fonts(game::context& ctx); +void load_fonts(::context& ctx); -} // namespace game #endif // ANTKEEPER_GAME_FONTS_HPP diff --git a/src/game/graphics.cpp b/src/game/graphics.cpp index c20df9d..4f2a9e3 100644 --- a/src/game/graphics.cpp +++ b/src/game/graphics.cpp @@ -18,16 +18,16 @@ */ #include "game/graphics.hpp" -#include "config.hpp" -#include "debug/log.hpp" -#include "gl/framebuffer.hpp" -#include "gl/texture-2d.hpp" -#include "gl/texture-filter.hpp" -#include "gl/texture-wrapping.hpp" -#include "render/passes/bloom-pass.hpp" -#include "render/passes/final-pass.hpp" -#include "render/passes/fxaa-pass.hpp" -#include "render/passes/resample-pass.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include @@ -35,12 +35,11 @@ #include #include -namespace game { namespace graphics { -static void reroute_framebuffers(game::context& ctx); +static void reroute_framebuffers(::context& ctx); -void create_framebuffers(game::context& ctx) +void create_framebuffers(::context& ctx) { debug::log::trace("Creating framebuffers..."); @@ -88,7 +87,7 @@ void create_framebuffers(game::context& ctx) debug::log::trace("Created framebuffers"); } -void destroy_framebuffers(game::context& ctx) +void destroy_framebuffers(::context& ctx) { debug::log::trace("Destroying framebuffers..."); @@ -120,7 +119,7 @@ void destroy_framebuffers(game::context& ctx) debug::log::trace("Destroyed framebuffers"); } -void change_render_resolution(game::context& ctx, float scale) +void change_render_resolution(::context& ctx, float scale) { debug::log::trace("Changing render resolution to {}...", scale); @@ -149,17 +148,19 @@ void change_render_resolution(game::context& ctx, float scale) if (viewport_size.x() != ctx.render_resolution.x() || viewport_size.y() != ctx.render_resolution.y()) { ctx.resample_pass->set_enabled(true); + debug::log::debug("Resample pass enabled"); } else { ctx.resample_pass->set_enabled(false); + debug::log::debug("Resample pass disabled"); } reroute_framebuffers(ctx); debug::log::trace("Changed render resolution to {}", scale); } -void save_screenshot(game::context& ctx) +void save_screenshot(::context& ctx) { // Determine timestamped screenshot filename const auto time = std::chrono::floor(std::chrono::system_clock::now()); @@ -197,19 +198,21 @@ void save_screenshot(game::context& ctx) } -void select_anti_aliasing_method(game::context& ctx, render::anti_aliasing_method method) +void select_anti_aliasing_method(::context& ctx, render::anti_aliasing_method method) { // Switch AA method switch (method) { // Off case render::anti_aliasing_method::none: + debug::log::debug("Anti-aliasing disabled"); ctx.fxaa_pass->set_enabled(false); reroute_framebuffers(ctx); break; // FXAA case render::anti_aliasing_method::fxaa: + debug::log::debug("Anti-aliasing enabled (FXAA)"); ctx.fxaa_pass->set_enabled(true); reroute_framebuffers(ctx); break; @@ -219,7 +222,7 @@ void select_anti_aliasing_method(game::context& ctx, render::anti_aliasing_metho ctx.anti_aliasing_method = method; } -void reroute_framebuffers(game::context& ctx) +void reroute_framebuffers(::context& ctx) { if (ctx.fxaa_pass->is_enabled()) { @@ -248,4 +251,3 @@ void reroute_framebuffers(game::context& ctx) } } // namespace graphics -} // namespace game diff --git a/src/game/graphics.hpp b/src/game/graphics.hpp index 7fa7482..a4b2a2a 100644 --- a/src/game/graphics.hpp +++ b/src/game/graphics.hpp @@ -21,19 +21,17 @@ #define ANTKEEPER_GAME_GRAPHICS_HPP #include "game/context.hpp" -#include "render/anti-aliasing-method.hpp" +#include -namespace game { namespace graphics { -void create_framebuffers(game::context& ctx); -void destroy_framebuffers(game::context& ctx); -void change_render_resolution(game::context& ctx, float scale); -void save_screenshot(game::context& ctx); -void toggle_bloom(game::context& ctx, bool enabled); -void select_anti_aliasing_method(game::context& ctx, render::anti_aliasing_method method); +void create_framebuffers(::context& ctx); +void destroy_framebuffers(::context& ctx); +void change_render_resolution(::context& ctx, float scale); +void save_screenshot(::context& ctx); +void toggle_bloom(::context& ctx, bool enabled); +void select_anti_aliasing_method(::context& ctx, render::anti_aliasing_method method); } // namespace graphics -} // namespace game #endif // ANTKEEPER_GAME_GRAPHICS_HPP diff --git a/src/game/load.cpp b/src/game/load.cpp index 0ec7abe..36e51aa 100644 --- a/src/game/load.cpp +++ b/src/game/load.cpp @@ -18,14 +18,13 @@ */ #include "game/load.hpp" -#include "debug/log.hpp" -#include "resources/json.hpp" -#include "resources/resource-manager.hpp" +#include +#include +#include -namespace game { namespace load { -void colony(game::context& ctx, const std::filesystem::path& path) +void colony(::context& ctx, const std::filesystem::path& path) { const std::string path_string = path.string(); @@ -43,4 +42,3 @@ void colony(game::context& ctx, const std::filesystem::path& path) } } // namespace load -} // namespace game diff --git a/src/game/load.hpp b/src/game/load.hpp index 154bdb0..e221f3d 100644 --- a/src/game/load.hpp +++ b/src/game/load.hpp @@ -22,15 +22,13 @@ #include "game/context.hpp" -namespace game { namespace load { /** * Loads a colony */ -void colony(game::context& ctx, const std::filesystem::path& path); +void colony(::context& ctx, const std::filesystem::path& path); } // namespace load -} // namespace game #endif // ANTKEEPER_GAME_LOAD_HPP diff --git a/src/game/loaders/control-profile-loader.cpp b/src/game/loaders/control-profile-loader.cpp new file mode 100644 index 0000000..06a7560 --- /dev/null +++ b/src/game/loaders/control-profile-loader.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include +#include "game/control-profile.hpp" + +template <> +::control_profile* resource_loader<::control_profile>::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +{ + ::control_profile* profile = new ::control_profile(); + + deserialize_context ctx(file); + deserializer<::control_profile>().deserialize(*profile, ctx); + + return profile; +} + +template <> +void resource_loader<::control_profile>::save(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path, const ::control_profile* profile) +{ + serialize_context ctx(file); + serializer<::control_profile>().serialize(*profile, ctx); +} diff --git a/src/game/loaders/entity-archetype-loader.cpp b/src/game/loaders/entity-archetype-loader.cpp new file mode 100644 index 0000000..be5c9ff --- /dev/null +++ b/src/game/loaders/entity-archetype-loader.cpp @@ -0,0 +1,334 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include +#include "game/components/atmosphere-component.hpp" +#include "game/components/behavior-component.hpp" +#include "game/components/collision-component.hpp" +#include "game/components/diffuse-reflector-component.hpp" +#include "game/components/terrain-component.hpp" +#include "game/components/transform-component.hpp" +#include "game/components/model-component.hpp" +#include "game/components/orbit-component.hpp" +#include "game/components/blackbody-component.hpp" +#include "game/components/celestial-body-component.hpp" +#include +#include +#include +#include + +static bool load_component_atmosphere(entity::archetype& archetype, const json& element) +{ + ::atmosphere_component component; + + if (element.contains("upper_limit")) + component.upper_limit = element["upper_limit"].get(); + if (element.contains("index_of_refraction")) + component.index_of_refraction = element["index_of_refraction"].get(); + + if (element.contains("rayleigh_concentration")) + component.rayleigh_concentration = element["rayleigh_concentration"].get(); + if (element.contains("rayleigh_scale_height")) + component.rayleigh_scale_height = element["rayleigh_scale_height"].get(); + + if (element.contains("mie_concentration")) + component.mie_concentration = element["mie_concentration"].get(); + if (element.contains("mie_scale_height")) + component.mie_scale_height = element["mie_scale_height"].get(); + if (element.contains("mie_anisotropy")) + component.mie_anisotropy = element["mie_anisotropy"].get(); + if (element.contains("mie_albedo")) + component.mie_albedo = element["mie_albedo"].get(); + + if (element.contains("ozone_concentration")) + component.ozone_concentration = element["ozone_concentration"].get(); + if (element.contains("ozone_lower_limit")) + component.ozone_lower_limit = element["ozone_lower_limit"].get(); + if (element.contains("ozone_upper_limit")) + component.ozone_upper_limit = element["ozone_upper_limit"].get(); + if (element.contains("ozone_mode")) + component.ozone_mode = element["ozone_mode"].get(); + + if (element.contains("airglow_illuminance")) + { + const auto& airglow_illuminance = element["airglow_illuminance"]; + component.airglow_illuminance.x() = airglow_illuminance[0].get(); + component.airglow_illuminance.y() = airglow_illuminance[1].get(); + component.airglow_illuminance.z() = airglow_illuminance[2].get(); + } + + archetype.stamps.push_back + ( + [component](entt::handle& handle) + { + handle.emplace_or_replace(component); + } + ); + + return true; +} + +static bool load_component_blackbody(entity::archetype& archetype, const json& element) +{ + ::blackbody_component component; + component.temperature = 0.0; + + if (element.contains("temperature")) + component.temperature = element["temperature"].get(); + + archetype.stamps.push_back + ( + [component](entt::handle& handle) + { + handle.emplace_or_replace(component); + } + ); + + return true; +} + +static bool load_component_celestial_body(entity::archetype& archetype, const json& element) +{ + ::celestial_body_component component; + + if (element.contains("radius")) + component.radius = element["radius"].get(); + if (element.contains("mass")) + component.mass = element["mass"].get(); + if (element.contains("pole_ra")) + { + component.pole_ra.clear(); + auto& pole_ra_element = element["pole_ra"]; + for (auto it = pole_ra_element.rbegin(); it != pole_ra_element.rend(); ++it) + component.pole_ra.push_back(math::radians(it->get())); + } + if (element.contains("pole_dec")) + { + component.pole_dec.clear(); + auto& pole_dec_element = element["pole_dec"]; + for (auto it = pole_dec_element.rbegin(); it != pole_dec_element.rend(); ++it) + component.pole_dec.push_back(math::radians(it->get())); + } + if (element.contains("prime_meridian")) + { + component.prime_meridian.clear(); + auto& prime_meridian_element = element["prime_meridian"]; + for (auto it = prime_meridian_element.rbegin(); it != prime_meridian_element.rend(); ++it) + component.prime_meridian.push_back(math::radians(it->get())); + } + if (element.contains("albedo")) + component.albedo = element["albedo"].get(); + + archetype.stamps.push_back + ( + [component](entt::handle& handle) + { + handle.emplace_or_replace(component); + } + ); + + return true; +} + +static bool load_component_collision(entity::archetype& archetype, resource_manager& resource_manager, const json& element) +{ + ::collision_component component; + component.mesh = nullptr; + + if (element.contains("file")) + { + component.mesh = resource_manager.load(element["file"].get()); + } + + archetype.stamps.push_back + ( + [component](entt::handle& handle) + { + handle.emplace_or_replace(component); + } + ); + + return (component.mesh != nullptr); +} + +static bool load_component_diffuse_reflector(entity::archetype& archetype, const json& element) +{ + ::diffuse_reflector_component component; + component.albedo = 0.0; + + if (element.contains("albedo")) + component.albedo = element["albedo"].get(); + + archetype.stamps.push_back + ( + [component](entt::handle& handle) + { + handle.emplace_or_replace(component); + } + ); + + return true; +} + +static bool load_component_model(entity::archetype& archetype, resource_manager& resource_manager, const json& element) +{ + ::model_component component; + component.instance_count = 0; + //component.layers = ~0; + component.layers = 1; + + if (element.contains("file")) + { + component.render_model = resource_manager.load(element["file"].get()); + } + + archetype.stamps.push_back + ( + [component](entt::handle& handle) + { + handle.emplace_or_replace(component); + } + ); + + return true; +} + +static bool load_component_orbit(entity::archetype& archetype, const json& element) +{ + ::orbit_component component; + + component.parent = entt::null; + component.ephemeris_index = -1; + component.scale = 1.0; + component.position = {0, 0, 0}; + + if (element.contains("ephemeris_index")) + component.ephemeris_index = element["ephemeris_index"].get(); + if (element.contains("scale")) + component.scale = element["scale"].get(); + + archetype.stamps.push_back + ( + [component](entt::handle& handle) + { + handle.emplace_or_replace(component); + } + ); + + return true; +} + +static bool load_component_transform(entity::archetype& archetype, const json& element) +{ + ::transform_component component; + component.local = math::transform::identity; + component.warp = true; + + if (element.contains("translation")) + { + auto translation = element["translation"]; + component.local.translation.x() = translation[0].get(); + component.local.translation.y() = translation[1].get(); + component.local.translation.z() = translation[2].get(); + } + + if (element.contains("rotation")) + { + auto translation = element["rotation"]; + component.local.rotation.w() = translation[0].get(); + component.local.rotation.x() = translation[1].get(); + component.local.rotation.y() = translation[2].get(); + component.local.rotation.z() = translation[3].get(); + } + + if (element.contains("scale")) + { + auto translation = element["scale"]; + component.local.scale.x() = translation[0].get(); + component.local.scale.y() = translation[1].get(); + component.local.scale.z() = translation[2].get(); + } + + component.world = component.local; + + archetype.stamps.push_back + ( + [component](entt::handle& handle) + { + handle.emplace_or_replace(component); + } + ); + + return true; +} + +static bool load_component(entity::archetype& archetype, resource_manager& resource_manager, json::const_iterator element) +{ + if (element.key() == "atmosphere") + return load_component_atmosphere(archetype, element.value()); + // if (element.key() == "behavior") + // return load_component_behavior(archetype, resource_manager, element.value()); + if (element.key() == "blackbody") + return load_component_blackbody(archetype, element.value()); + if (element.key() == "celestial_body") + return load_component_celestial_body(archetype, element.value()); + if (element.key() == "collision") + return load_component_collision(archetype, resource_manager, element.value()); + if (element.key() == "diffuse_reflector") + return load_component_diffuse_reflector(archetype, element.value()); + if (element.key() == "model") + return load_component_model(archetype, resource_manager, element.value()); + if (element.key() == "orbit") + return load_component_orbit(archetype, element.value()); + if (element.key() == "transform") + return load_component_transform(archetype, element.value()); + + //throw std::runtime_error("Unknown component type \"" + element.key() + "\""); + + return false; +} + +template <> +entity::archetype* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) +{ + // Allocate archetype + entity::archetype* archetype = new entity::archetype(); + + // Read file into buffer + std::size_t size = static_cast(PHYSFS_fileLength(file)); + std::string buffer; + buffer.resize(size); + PHYSFS_readBytes(file, &buffer[0], size); + + // Parse JSON data from file buffer + json data = nlohmann::json::parse(buffer, nullptr, true, true); + + // Load components from table rows + for (json::const_iterator element = data.cbegin(); element != data.cend(); ++element) + { + if (!load_component(*archetype, *resource_manager, element)) + { + throw std::runtime_error("Failed to load component \"" + element.key() + "\""); + } + } + + return archetype; +} diff --git a/src/game/loop.cpp b/src/game/loop.cpp index c27efe8..7c2098e 100644 --- a/src/game/loop.cpp +++ b/src/game/loop.cpp @@ -20,7 +20,6 @@ #include "game/loop.hpp" #include -namespace game { loop::loop(): update_callback([](double, double){}), @@ -85,4 +84,3 @@ void loop::tick() render_callback(accumulator * update_frequency); } -} // namespace game diff --git a/src/game/loop.hpp b/src/game/loop.hpp index 56cd0ff..be36e15 100644 --- a/src/game/loop.hpp +++ b/src/game/loop.hpp @@ -23,7 +23,6 @@ #include #include -namespace game { /** * Game loop with fixed timestep update calls and variable timestep render calls. @@ -107,6 +106,5 @@ inline double loop::get_update_period() const return update_period; } -} // namespace game #endif // ANTKEEPER_GAME_LOOP_HPP diff --git a/src/game/main.cpp b/src/game/main.cpp new file mode 100644 index 0000000..7188d4d --- /dev/null +++ b/src/game/main.cpp @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include +#include +#include +#include "game/context.hpp" +#include "game/state/boot.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char* argv[]) +{ + // Get time at which the application launched + const auto launch_time = std::chrono::system_clock::now(); + + // Enable console UTF-8 output and VT100 sequences (for colored text) + debug::console::enable_utf8(); + debug::console::enable_vt100(); + + // Subscribe log to cout function to message logged events + auto log_to_cout_subscription = debug::log::default_logger().get_message_logged_channel().subscribe + ( + [&launch_time](const auto& event) + { + static const char* severities[] = + { + "trace", + "debug", + "info", + "warning", + "error", + "fatal" + }; + + static const std::string colors[] = + { + std::format("{}", ansi::fg_white), + std::format("{}", ansi::fg_bright_blue), + std::format("{}", ansi::fg_bright_green), + std::format("{}", ansi::fg_yellow), + std::format("{}", ansi::fg_red), + std::format("{}{}", ansi::fg_white, ansi::bg_bright_red) + }; + + std::osyncstream(std::cout) << std::format + ( + "[{:8.03f}] {}{}: {}:{}:{}: {}{}\n", + std::chrono::duration(event.time - launch_time).count(), + colors[static_cast(event.severity)], + //severities[static_cast(event.severity)], + static_cast(event.severity), + std::filesystem::path(event.location.file_name()).filename().string(), + event.location.line(), + event.location.column(), + event.message, + ansi::reset + ); + } + ); + + // Determine path to log archive + const std::filesystem::path log_archive_path = get_shared_config_path() / config::application_name / "logs"; + + // Set up log archive + bool log_archive_exists = false; + try + { + // Create log archive if it doesn't exist + if (std::filesystem::create_directories(log_archive_path)) + { + debug::log::debug("Created log archive \"{}\"", log_archive_path.string()); + } + else + { + // Clean pre-existing log archive + try + { + // Detect and sort archived logs + std::set log_archive; + for (const auto& entry: std::filesystem::directory_iterator{log_archive_path}) + { + if (entry.is_regular_file() && + entry.path().extension() == ".log") + { + log_archive.emplace(entry.path()); + } + } + + debug::log::debug("Detected {} archived log{} at \"{}\"", log_archive.size(), log_archive.size() != 1 ? "s" : "", log_archive_path.string()); + + // Delete expired logs + if (!log_archive.empty()) + { + for (std::size_t i = log_archive.size() + 1; i > config::debug_log_archive_capacity; --i) + { + std::filesystem::remove(*log_archive.begin()); + debug::log::debug("Deleted expired log file \"{}\"", log_archive.begin()->string()); + log_archive.erase(log_archive.begin()); + } + } + } + catch (const std::filesystem::filesystem_error& e) + { + debug::log::error("An error occured while cleaning the log archive \"{}\": {}", log_archive_path.string(), e.what()); + } + } + + log_archive_exists = true; + } + catch (const std::filesystem::filesystem_error& e) + { + debug::log::error("Failed to create log archive at \"{}\": {}", log_archive_path.string(), e.what()); + } + + // Set up logging to file + std::shared_ptr log_to_file_subscription; + std::filesystem::path log_filepath; + if (config::debug_log_archive_capacity && log_archive_exists) + { + // Determine log filename + const auto time = std::chrono::floor(launch_time); + const std::string log_filename = std::format("{0}-{1:%Y%m%d}T{1:%H%M%S}Z.log", config::application_slug, time); + + // Open log file + log_filepath = log_archive_path / log_filename; + const std::string log_filepath_string = log_filepath.string(); + auto log_filestream = std::make_shared(log_filepath); + + if (log_filestream->is_open()) + { + debug::log::debug("Opened log file \"{}\"", log_filepath_string); + + // Write log file header + (*log_filestream) << "time\tfile\tline\tcolumn\tseverity\tmessage"; + + if (log_filestream->good()) + { + // Subscribe log to file function to message logged events + log_to_file_subscription = debug::log::default_logger().get_message_logged_channel().subscribe + ( + [&launch_time, log_filestream](const auto& event) + { + std::osyncstream(*log_filestream) << std::format + ( + "\n{:.03f}\t{}\t{}\t{}\t{}\t{}", + std::chrono::duration(event.time - launch_time).count(), + std::filesystem::path(event.location.file_name()).filename().string(), + event.location.line(), + event.location.column(), + static_cast(event.severity), + event.message + ); + } + ); + + // Unsubscribe log to cout function from message logged events on release builds + #if defined(NDEBUG) + log_to_cout_subscription->unsubscribe(); + #endif + } + else + { + debug::log::error("Failed to write to log file \"{}\"", log_filepath_string); + } + } + else + { + debug::log::error("Failed to open log file \"{}\"", log_filepath_string); + } + } + + // Log application name and version string, followed by launch time + debug::log::info("{0} {1}; {2:%Y%m%d}T{2:%H%M%S}Z", config::application_name, config::application_version_string, std::chrono::floor(launch_time)); + + // Start marker + debug::log::debug("Hi! 🐜"); + + try + { + // Allocate game context + ::context ctx; + + // Enter boot state + ctx.state_machine.emplace(new ::state::boot(ctx, argc, argv)); + } + catch (const std::exception& e) + { + debug::log::fatal("Unhandled exception: {}", e.what()); + + #if defined(NDEBUG) + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", std::format("Unhandled exception: {}", e.what()).c_str(), nullptr); + #endif + + return EXIT_FAILURE; + } + + // Clean exit marker + debug::log::debug("Bye! 🐜"); + + return EXIT_SUCCESS; +} diff --git a/src/game/menu.cpp b/src/game/menu.cpp index 3cca780..bdf49ce 100644 --- a/src/game/menu.cpp +++ b/src/game/menu.cpp @@ -18,20 +18,19 @@ */ #include "game/menu.hpp" -#include "scene/text.hpp" -#include "animation/animation.hpp" -#include "animation/animator.hpp" -#include "animation/ease.hpp" -#include "config.hpp" +#include +#include +#include +#include +#include #include -#include "math/glsl.hpp" +#include using namespace math::glsl; -namespace game { namespace menu { -void init_menu_item_index(game::context& ctx, const std::string& menu_name) +void init_menu_item_index(::context& ctx, const std::string& menu_name) { if (auto it = ctx.menu_item_indices.find(menu_name); it != ctx.menu_item_indices.end()) { @@ -44,7 +43,7 @@ void init_menu_item_index(game::context& ctx, const std::string& menu_name) } } -void update_text_font(game::context& ctx) +void update_text_font(::context& ctx) { for (auto [name, value]: ctx.menu_item_texts) { @@ -59,7 +58,7 @@ void update_text_font(game::context& ctx) } } -void update_text_color(game::context& ctx) +void update_text_color(::context& ctx) { for (std::size_t i = 0; i < ctx.menu_item_texts.size(); ++i) { @@ -73,7 +72,7 @@ void update_text_color(game::context& ctx) } } -void update_text_tweens(game::context& ctx) +void update_text_tweens(::context& ctx) { for (auto [name, value]: ctx.menu_item_texts) { @@ -83,7 +82,7 @@ void update_text_tweens(game::context& ctx) } } -void align_text(game::context& ctx, bool center, bool has_back, float anchor_y) +void align_text(::context& ctx, bool center, bool has_back, float anchor_y) { @@ -166,7 +165,7 @@ void align_text(game::context& ctx, bool center, bool has_back, float anchor_y) } } -void refresh_text(game::context& ctx) +void refresh_text(::context& ctx) { for (auto [name, value]: ctx.menu_item_texts) { @@ -176,7 +175,7 @@ void refresh_text(game::context& ctx) } } -void add_text_to_ui(game::context& ctx) +void add_text_to_ui(::context& ctx) { for (auto [name, value]: ctx.menu_item_texts) { @@ -186,7 +185,7 @@ void add_text_to_ui(game::context& ctx) } } -void remove_text_from_ui(game::context& ctx) +void remove_text_from_ui(::context& ctx) { for (auto [name, value]: ctx.menu_item_texts) { @@ -196,7 +195,7 @@ void remove_text_from_ui(game::context& ctx) } } -void delete_text(game::context& ctx) +void delete_text(::context& ctx) { for (auto [name, value]: ctx.menu_item_texts) { @@ -207,14 +206,14 @@ void delete_text(game::context& ctx) ctx.menu_item_texts.clear(); } -void delete_animations(game::context& ctx) +void delete_animations(::context& ctx) { ctx.animator->remove_animation(ctx.menu_fade_animation); delete ctx.menu_fade_animation; ctx.menu_fade_animation = nullptr; } -void clear_callbacks(game::context& ctx) +void clear_callbacks(::context& ctx) { // Clear menu item callbacks ctx.menu_left_callbacks.clear(); @@ -223,7 +222,7 @@ void clear_callbacks(game::context& ctx) ctx.menu_back_callback = nullptr; } -void setup_animations(game::context& ctx) +void setup_animations(::context& ctx) { ctx.menu_fade_animation = new animation(); animation_channel* opacity_channel = ctx.menu_fade_animation->add_channel(0); @@ -250,7 +249,7 @@ void setup_animations(game::context& ctx) ctx.animator->add_animation(ctx.menu_fade_animation); } -void fade_in(game::context& ctx, const std::function& end_callback) +void fade_in(::context& ctx, const std::function& end_callback) { ctx.menu_fade_animation->set_interpolator(ease::out_cubic); animation_channel* opacity_channel = ctx.menu_fade_animation->get_channel(0); @@ -282,7 +281,7 @@ void fade_in(game::context& ctx, const std::function& end_callback) ctx.menu_fade_animation->play(); } -void fade_out(game::context& ctx, const std::function& end_callback) +void fade_out(::context& ctx, const std::function& end_callback) { ctx.menu_fade_animation->set_interpolator(ease::out_cubic); animation_channel* opacity_channel = ctx.menu_fade_animation->get_channel(0); @@ -295,14 +294,14 @@ void fade_out(game::context& ctx, const std::function& end_callback) ctx.menu_fade_animation->play(); } -void fade_in_bg(game::context& ctx) +void fade_in_bg(::context& ctx) { ctx.menu_bg_fade_out_animation->stop(); ctx.menu_bg_fade_in_animation->stop(); ctx.menu_bg_fade_in_animation->play(); } -void fade_out_bg(game::context& ctx) +void fade_out_bg(::context& ctx) { ctx.menu_bg_fade_in_animation->stop(); ctx.menu_bg_fade_out_animation->stop(); @@ -310,4 +309,3 @@ void fade_out_bg(game::context& ctx) } } // namespace menu -} // namespace game diff --git a/src/game/menu.hpp b/src/game/menu.hpp index 3c83e68..09e58a7 100644 --- a/src/game/menu.hpp +++ b/src/game/menu.hpp @@ -22,33 +22,31 @@ #include "game/context.hpp" -namespace game { namespace menu { -void init_menu_item_index(game::context& ctx, const std::string& menu_name); -void setup_animations(game::context& ctx); +void init_menu_item_index(::context& ctx, const std::string& menu_name); +void setup_animations(::context& ctx); -void clear_callbacks(game::context& ctx); -void remove_text_from_ui(game::context& ctx); -void delete_text(game::context& ctx); -void delete_animations(game::context& ctx); +void clear_callbacks(::context& ctx); +void remove_text_from_ui(::context& ctx); +void delete_text(::context& ctx); +void delete_animations(::context& ctx); -void fade_in(game::context& ctx, const std::function& end_callback); -void fade_out(game::context& ctx, const std::function& end_callback); +void fade_in(::context& ctx, const std::function& end_callback); +void fade_out(::context& ctx, const std::function& end_callback); -void fade_in_bg(game::context& ctx); -void fade_out_bg(game::context& ctx); +void fade_in_bg(::context& ctx); +void fade_out_bg(::context& ctx); -void update_text_color(game::context& ctx); -void update_text_font(game::context& ctx); -void update_text_tweens(game::context& ctx); -void align_text(game::context& ctx, bool center = false, bool has_back = true, float anchor_y = 0.0f); -void refresh_text(game::context& ctx); -void add_text_to_ui(game::context& ctx); +void update_text_color(::context& ctx); +void update_text_font(::context& ctx); +void update_text_tweens(::context& ctx); +void align_text(::context& ctx, bool center = false, bool has_back = true, float anchor_y = 0.0f); +void refresh_text(::context& ctx); +void add_text_to_ui(::context& ctx); } // namespace menu -} // namespace game #endif // ANTKEEPER_GAME_MENU_HPP diff --git a/src/platform/windows/dpi-aware.manifest b/src/game/platform/windows/dpi-aware.manifest similarity index 100% rename from src/platform/windows/dpi-aware.manifest rename to src/game/platform/windows/dpi-aware.manifest diff --git a/src/platform/windows/icon.rc.in b/src/game/platform/windows/icon.rc.in similarity index 100% rename from src/platform/windows/icon.rc.in rename to src/game/platform/windows/icon.rc.in diff --git a/src/platform/windows/nvidia.cpp b/src/game/platform/windows/nvidia.cpp similarity index 100% rename from src/platform/windows/nvidia.cpp rename to src/game/platform/windows/nvidia.cpp diff --git a/src/platform/windows/version.rc.in b/src/game/platform/windows/version.rc.in similarity index 100% rename from src/platform/windows/version.rc.in rename to src/game/platform/windows/version.rc.in diff --git a/src/game/settings.hpp b/src/game/settings.hpp index 8e441e6..e0da517 100644 --- a/src/game/settings.hpp +++ b/src/game/settings.hpp @@ -21,9 +21,8 @@ #define ANTKEEPER_GAME_SETTINGS_HPP #include "game/context.hpp" -#include "debug/log.hpp" +#include -namespace game { /** * Reads a setting if found, inserts a setting if not found, and overwrites a setting if a type mismatch occurs. @@ -37,7 +36,7 @@ namespace game { * @return `true` if the setting was read, `false` if the setting was written. */ template -bool read_or_write_setting(game::context& ctx, std::uint32_t key, T& value) +bool read_or_write_setting(::context& ctx, std::uint32_t key, T& value) { if (auto i = ctx.settings->find(key); i != ctx.settings->end()) { @@ -62,6 +61,5 @@ bool read_or_write_setting(game::context& ctx, std::uint32_t key, T& value) return true; } -} // namespace game #endif // ANTKEEPER_GAME_SETTINGS_HPP diff --git a/src/game/spawn.cpp b/src/game/spawn.cpp index a54ca93..b790987 100644 --- a/src/game/spawn.cpp +++ b/src/game/spawn.cpp @@ -18,60 +18,58 @@ */ #include "game/spawn.hpp" -#include "game/component/transform.hpp" -#include "game/component/model.hpp" +#include "game/components/transform-component.hpp" +#include "game/components/model-component.hpp" -namespace game { -entity::id spawn_ant_egg(game::context& ctx, const ant::genome& genome, bool fertilized, const float3& position) +entity::id spawn_ant_egg(::context& ctx, const ant::genome& genome, bool fertilized, const float3& position) { // Create entity entity::id egg_eid = ctx.entity_registry->create(); // Construct transform component - component::transform transform_component; + transform_component transform_component; transform_component.local = math::transform::identity; transform_component.local.translation = position; transform_component.world = transform_component.local; transform_component.warp = true; - ctx.entity_registry->emplace(egg_eid, transform_component); + ctx.entity_registry->emplace<::transform_component>(egg_eid, transform_component); // Construct model component - component::model model_component; + model_component model_component; model_component.render_model = genome.egg->phene.model; model_component.instance_count = 0; model_component.layers = ~0; - ctx.entity_registry->emplace(egg_eid, model_component); + ctx.entity_registry->emplace<::model_component>(egg_eid, model_component); return egg_eid; } -entity::id spawn_ant_larva(game::context& ctx, const ant::genome& genome, const float3& position) +entity::id spawn_ant_larva(::context& ctx, const ant::genome& genome, const float3& position) { // Create entity entity::id larva_eid = ctx.entity_registry->create(); // Construct transform component - component::transform transform_component; + transform_component transform_component; transform_component.local = math::transform::identity; transform_component.local.translation = position; transform_component.world = transform_component.local; transform_component.warp = true; - ctx.entity_registry->emplace(larva_eid, transform_component); + ctx.entity_registry->emplace<::transform_component>(larva_eid, transform_component); // Construct model component - component::model model_component; + model_component model_component; model_component.render_model = genome.larva->phene.model; model_component.instance_count = 0; model_component.layers = ~0; - ctx.entity_registry->emplace(larva_eid, model_component); + ctx.entity_registry->emplace<::model_component>(larva_eid, model_component); return larva_eid; } -entity::id spawn_worker_ant(game::context& ctx, const ant::genome& genome, const float3& position) +entity::id spawn_worker_ant(::context& ctx, const ant::genome& genome, const float3& position) { return entt::null; } -} // namespace game diff --git a/src/game/spawn.hpp b/src/game/spawn.hpp index 6d69ad3..8f7f9bd 100644 --- a/src/game/spawn.hpp +++ b/src/game/spawn.hpp @@ -22,9 +22,8 @@ #include "game/context.hpp" #include "game/ant/genome.hpp" -#include "utility/fundamental-types.hpp" +#include -namespace game { /** * Spawns an ant egg. @@ -36,7 +35,7 @@ namespace game { * * @return Entity ID of the spawned ant egg. */ -entity::id spawn_ant_egg(game::context& ctx, const ant::genome& genome, bool fertilized, const float3& position); +entity::id spawn_ant_egg(::context& ctx, const ant::genome& genome, bool fertilized, const float3& position); /** * Spawns an ant larva. @@ -47,7 +46,7 @@ entity::id spawn_ant_egg(game::context& ctx, const ant::genome& genome, bool fer * * @return Entity ID of the spawned ant larva. */ -entity::id spawn_ant_larva(game::context& ctx, const ant::genome& genome, const float3& position); +entity::id spawn_ant_larva(::context& ctx, const ant::genome& genome, const float3& position); /** * Spawns a worker ant. @@ -58,8 +57,7 @@ entity::id spawn_ant_larva(game::context& ctx, const ant::genome& genome, const * * @return Entity ID of the spawned worker ant. */ -entity::id spawn_worker_ant(game::context& ctx, const ant::genome& genome, const float3& position); +entity::id spawn_worker_ant(::context& ctx, const ant::genome& genome, const float3& position); -} // namespace game #endif // ANTKEEPER_GAME_SPAWN_HPP diff --git a/src/game/state/base.cpp b/src/game/state/base.cpp index 35d6a22..56da769 100644 --- a/src/game/state/base.cpp +++ b/src/game/state/base.cpp @@ -19,10 +19,9 @@ #include "game/state/base.hpp" -namespace game { namespace state { -base::base(game::context& ctx): +base::base(::context& ctx): ctx(ctx) {} @@ -30,4 +29,3 @@ base::~base() {} } // namespace state -} // namespace game diff --git a/src/game/state/base.hpp b/src/game/state/base.hpp index 6327d3e..eb3754c 100644 --- a/src/game/state/base.hpp +++ b/src/game/state/base.hpp @@ -20,7 +20,6 @@ #ifndef ANTKEEPER_GAME_STATE_BASE_HPP #define ANTKEEPER_GAME_STATE_BASE_HPP -namespace game { struct context; @@ -37,7 +36,7 @@ public: * * @param ctx Reference to the game context on which this state will operate. */ - base(game::context& ctx); + base(::context& ctx); /** * Destructs a game state. @@ -45,10 +44,9 @@ public: virtual ~base() = 0; protected: - game::context& ctx; + ::context& ctx; }; } // namespace state -} // namespace game #endif // ANTKEEPER_GAME_STATE_BASE_HPP diff --git a/src/game/state/boot.cpp b/src/game/state/boot.cpp index eff825b..09c9a7c 100644 --- a/src/game/state/boot.cpp +++ b/src/game/state/boot.cpp @@ -17,16 +17,16 @@ * along with Antkeeper source code. If not, see . */ -#include "animation/animation.hpp" -#include "animation/animator.hpp" -#include "animation/ease.hpp" -#include "animation/screen-transition.hpp" -#include "animation/timeline.hpp" -#include "color/color.hpp" -#include "config.hpp" -#include "debug/cli.hpp" -#include "debug/log.hpp" -#include "entity/commands.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "game/commands/commands.hpp" #include "game/context.hpp" #include "game/controls.hpp" #include "game/control-profile.hpp" @@ -37,60 +37,59 @@ #include "game/state/boot.hpp" #include "game/state/splash.hpp" #include "game/state/main-menu.hpp" -#include "game/system/astronomy.hpp" -#include "game/system/atmosphere.hpp" -#include "game/system/behavior.hpp" -#include "game/system/blackbody.hpp" -#include "game/system/camera.hpp" -#include "game/system/collision.hpp" -#include "game/system/constraint.hpp" -#include "game/system/locomotion.hpp" -#include "game/system/orbit.hpp" -#include "game/system/render.hpp" -#include "game/system/spatial.hpp" -#include "game/system/spring.hpp" -#include "game/system/steering.hpp" -#include "game/system/subterrain.hpp" -#include "game/system/terrain.hpp" -#include "game/system/vegetation.hpp" +#include "game/systems/astronomy-system.hpp" +#include "game/systems/atmosphere-system.hpp" +#include "game/systems/behavior-system.hpp" +#include "game/systems/blackbody-system.hpp" +#include "game/systems/camera-system.hpp" +#include "game/systems/collision-system.hpp" +#include "game/systems/constraint-system.hpp" +#include "game/systems/locomotion-system.hpp" +#include "game/systems/orbit-system.hpp" +#include "game/systems/render-system.hpp" +#include "game/systems/spatial-system.hpp" +#include "game/systems/spring-system.hpp" +#include "game/systems/steering-system.hpp" +#include "game/systems/subterrain-system.hpp" +#include "game/systems/terrain-system.hpp" #include "game/settings.hpp" -#include "gl/framebuffer.hpp" -#include "gl/pixel-format.hpp" -#include "gl/pixel-type.hpp" -#include "gl/rasterizer.hpp" -#include "gl/texture-2d.hpp" -#include "gl/texture-filter.hpp" -#include "gl/texture-wrapping.hpp" -#include "gl/vertex-array.hpp" -#include "gl/vertex-attribute.hpp" -#include "gl/vertex-buffer.hpp" -#include "input/gamepad.hpp" -#include "input/keyboard.hpp" -#include "input/mapper.hpp" -#include "input/mouse.hpp" -#include "input/scancode.hpp" -#include "render/compositor.hpp" -#include "render/material-flags.hpp" -#include "render/material-property.hpp" -#include "render/passes/bloom-pass.hpp" -#include "render/passes/clear-pass.hpp" -#include "render/passes/final-pass.hpp" -#include "render/passes/fxaa-pass.hpp" -#include "render/passes/ground-pass.hpp" -#include "render/passes/material-pass.hpp" -#include "render/passes/outline-pass.hpp" -#include "render/passes/resample-pass.hpp" -#include "render/passes/shadow-map-pass.hpp" -#include "render/passes/sky-pass.hpp" -#include "render/renderer.hpp" -#include "render/vertex-attribute.hpp" -#include "resources/file-buffer.hpp" -#include "resources/resource-manager.hpp" -#include "scene/scene.hpp" -#include "utility/paths.hpp" -#include "utility/dict.hpp" -#include "utility/hash/fnv1a.hpp" -#include "input/application-events.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include @@ -106,11 +105,10 @@ using namespace hash::literals; -namespace game { namespace state { -boot::boot(game::context& ctx, int argc, const char* const* argv): - game::state::base(ctx) +boot::boot(::context& ctx, int argc, const char* const* argv): + ::state::base(ctx) { // Boot process debug::log::trace("Booting up..."); @@ -137,7 +135,7 @@ boot::boot(game::context& ctx, int argc, const char* const* argv): debug::log::trace("Boot up complete"); // Push next state - ctx.state_machine.emplace(new game::state::main_menu(ctx, true)); + ctx.state_machine.emplace(new ::state::main_menu(ctx, true)); // Enter main loop debug::log::trace("Entered main loop"); @@ -588,7 +586,7 @@ void boot::setup_rendering() read_or_write_setting(ctx, "shadow_map_resolution"_fnv1a32, ctx.shadow_map_resolution); // Create framebuffers - game::graphics::create_framebuffers(ctx); + ::graphics::create_framebuffers(ctx); // Load blue noise texture gl::texture_2d* blue_noise_map = ctx.resource_manager->load("blue-noise.tex"); @@ -601,7 +599,7 @@ void boot::setup_rendering() // Construct bloom pass ctx.bloom_pass = new render::bloom_pass(ctx.window->get_rasterizer(), ctx.resource_manager); ctx.bloom_pass->set_source_texture(ctx.hdr_color_texture); - ctx.bloom_pass->set_mip_chain_length(0); + ctx.bloom_pass->set_mip_chain_length(5); ctx.bloom_pass->set_filter_radius(0.005f); ctx.common_final_pass = new render::final_pass(ctx.window->get_rasterizer(), ctx.ldr_framebuffer_a, ctx.resource_manager); @@ -886,8 +884,8 @@ void boot::setup_scenes() ctx.menu_bg_billboard = new scene::billboard(); ctx.menu_bg_billboard->set_active(false); ctx.menu_bg_billboard->set_material(menu_bg_material); - ctx.menu_bg_billboard->set_scale({(float)viewport_size[0] * 0.5f, (float)viewport_size[1] * 0.5f, 1.0f}); - ctx.menu_bg_billboard->set_translation({0.0f, 0.0f, -100.0f}); + ctx.menu_bg_billboard->set_scale({std::ceil(viewport_size.x() * 0.5f), std::ceil(viewport_size.y() * 0.5f), 1.0f}); + ctx.menu_bg_billboard->set_translation({std::floor(viewport_size.x() * 0.5f), std::floor(viewport_size.y() * 0.5f), -100.0f}); ctx.menu_bg_billboard->update_tweens(); // Create camera flash billboard @@ -1046,70 +1044,62 @@ void boot::setup_systems() float4 viewport = {0.0f, 0.0f, static_cast(viewport_size[0]), static_cast(viewport_size[1])}; // Setup terrain system - ctx.terrain_system = new game::system::terrain(*ctx.entity_registry); + ctx.terrain_system = new ::terrain_system(*ctx.entity_registry); ctx.terrain_system->set_patch_side_length(31.0f); ctx.terrain_system->set_patch_subdivisions(31); ctx.terrain_system->set_scene_collection(ctx.surface_scene); - // Setup vegetation system - //ctx.vegetation_system = new game::system::vegetation(*ctx.entity_registry); - //ctx.vegetation_system->set_terrain_patch_size(TERRAIN_PATCH_SIZE); - //ctx.vegetation_system->set_vegetation_patch_resolution(VEGETATION_PATCH_RESOLUTION); - //ctx.vegetation_system->set_vegetation_density(1.0f); - //ctx.vegetation_system->set_vegetation_model(ctx.resource_manager->load("grass-tuft.mdl")); - //ctx.vegetation_system->set_scene(ctx.surface_scene); - // Setup camera system - ctx.camera_system = new game::system::camera(*ctx.entity_registry); + ctx.camera_system = new ::camera_system(*ctx.entity_registry); ctx.camera_system->set_viewport(viewport); // Setup subterrain system - ctx.subterrain_system = new game::system::subterrain(*ctx.entity_registry, ctx.resource_manager); + ctx.subterrain_system = new ::subterrain_system(*ctx.entity_registry, ctx.resource_manager); ctx.subterrain_system->set_scene(ctx.underground_scene); // Setup collision system - ctx.collision_system = new game::system::collision(*ctx.entity_registry); + ctx.collision_system = new ::collision_system(*ctx.entity_registry); // Setup behavior system - ctx.behavior_system = new game::system::behavior(*ctx.entity_registry); + ctx.behavior_system = new ::behavior_system(*ctx.entity_registry); // Setup locomotion system - ctx.locomotion_system = new game::system::locomotion(*ctx.entity_registry); + ctx.locomotion_system = new ::locomotion_system(*ctx.entity_registry); // Setup steering system - ctx.steering_system = new game::system::steering(*ctx.entity_registry); + ctx.steering_system = new ::steering_system(*ctx.entity_registry); // Setup spring system - ctx.spring_system = new game::system::spring(*ctx.entity_registry); + ctx.spring_system = new ::spring_system(*ctx.entity_registry); // Setup spatial system - ctx.spatial_system = new game::system::spatial(*ctx.entity_registry); + ctx.spatial_system = new ::spatial_system(*ctx.entity_registry); // Setup constraint system - ctx.constraint_system = new game::system::constraint(*ctx.entity_registry); + ctx.constraint_system = new ::constraint_system(*ctx.entity_registry); // Setup orbit system - ctx.orbit_system = new game::system::orbit(*ctx.entity_registry); + ctx.orbit_system = new ::orbit_system(*ctx.entity_registry); // Setup blackbody system - ctx.blackbody_system = new game::system::blackbody(*ctx.entity_registry); + ctx.blackbody_system = new ::blackbody_system(*ctx.entity_registry); ctx.blackbody_system->set_illuminant(color::illuminant::deg2::d55); // RGB wavelengths for atmospheric scatteering ctx.rgb_wavelengths = {680, 550, 440}; // Setup atmosphere system - ctx.atmosphere_system = new game::system::atmosphere(*ctx.entity_registry); + ctx.atmosphere_system = new ::atmosphere_system(*ctx.entity_registry); ctx.atmosphere_system->set_rgb_wavelengths(ctx.rgb_wavelengths * 1e-9); ctx.atmosphere_system->set_sky_pass(ctx.sky_pass); // Setup astronomy system - ctx.astronomy_system = new game::system::astronomy(*ctx.entity_registry); + ctx.astronomy_system = new ::astronomy_system(*ctx.entity_registry); ctx.astronomy_system->set_transmittance_samples(16); ctx.astronomy_system->set_sky_pass(ctx.sky_pass); // Setup render system - ctx.render_system = new game::system::render(*ctx.entity_registry); + ctx.render_system = new ::render_system(*ctx.entity_registry); //ctx.render_system->add_layer(ctx.underground_scene); ctx.render_system->add_layer(ctx.surface_scene); ctx.render_system->add_layer(ctx.ui_scene); @@ -1135,6 +1125,13 @@ void boot::setup_controls() // ctx.resource_manager->unload("gamecontrollerdb.txt"); // } + // Pass input event queue to action maps + event::queue* input_event_queue = &ctx.input_manager->get_event_queue(); + ctx.window_action_map.set_event_queue(input_event_queue); + ctx.menu_action_map.set_event_queue(input_event_queue); + ctx.movement_action_map.set_event_queue(input_event_queue); + ctx.nuptial_flight_action_map.set_event_queue(input_event_queue); + // Default control profile settings ctx.control_profile_filename = "controls.cfg"; ctx.control_profile = nullptr; @@ -1143,17 +1140,17 @@ void boot::setup_controls() if (read_or_write_setting(ctx, "control_profile"_fnv1a32, ctx.control_profile_filename)) { // Load control profile - //ctx.control_profile = ctx.resource_manager->load(ctx.controls_path / ctx.control_profile_filename); - ctx.control_profile = ctx.resource_manager->load(ctx.control_profile_filename); + //ctx.control_profile = ctx.resource_manager->load<::control_profile>(ctx.controls_path / ctx.control_profile_filename); + ctx.control_profile = ctx.resource_manager->load<::control_profile>(ctx.control_profile_filename); } if (!ctx.control_profile) { // Allocate control profile - ctx.control_profile = new game::control_profile(); + ctx.control_profile = new ::control_profile(); // Reset control profile to default settings. - game::reset_control_profile(*ctx.control_profile); + ::reset_control_profile(*ctx.control_profile); // Save control profile ctx.resource_manager->set_write_dir(ctx.controls_path); @@ -1161,7 +1158,7 @@ void boot::setup_controls() } // Apply control profile - game::apply_control_profile(ctx, *ctx.control_profile); + ::apply_control_profile(ctx, *ctx.control_profile); // Setup action callbacks setup_window_controls(ctx); @@ -1194,7 +1191,7 @@ void boot::setup_ui() debug::log::trace("Loading fonts..."); try { - game::load_fonts(ctx); + ::load_fonts(ctx); debug::log::trace("Loaded fonts"); } catch (...) @@ -1211,7 +1208,7 @@ void boot::setup_ui() const float viewport_aspect_ratio = static_cast(viewport_size.x()) / static_cast(viewport_size.y()); // Resize framebuffers - game::graphics::change_render_resolution(ctx, ctx.render_scale); + ::graphics::change_render_resolution(ctx, ctx.render_scale); // Update camera projection matrix ctx.surface_camera->set_perspective @@ -1233,12 +1230,16 @@ void boot::setup_ui() ctx.ui_camera->get_clip_far() ); + // Resize menu BG billboard + ctx.menu_bg_billboard->set_scale({std::ceil(viewport_size.x() * 0.5f), std::ceil(viewport_size.y() * 0.5f), 1.0f}); + ctx.menu_bg_billboard->set_translation({std::floor(viewport_size.x() * 0.5f), std::floor(viewport_size.y() * 0.5f), -100.0f}); + // Re-align debug text ctx.frame_time_text->set_translation({std::round(0.0f), std::round(viewport_size.y() - ctx.debug_font.get_font_metrics().size), 99.0f}); ctx.frame_time_text->update_tweens(); // Re-align menu text - game::menu::align_text(ctx); + ::menu::align_text(ctx); } ); } @@ -1309,9 +1310,8 @@ void boot::setup_loop() ctx.timeline->advance(dt); // Update entity systems - ctx.terrain_system->update(t, dt); - //ctx.vegetation_system->update(t, dt); - ctx.subterrain_system->update(t, dt); + //ctx.terrain_system->update(t, dt); + //ctx.subterrain_system->update(t, dt); ctx.collision_system->update(t, dt); ctx.behavior_system->update(t, dt); ctx.steering_system->update(t, dt); @@ -1382,4 +1382,3 @@ void boot::shutdown_audio() } } // namespace state -} // namespace game diff --git a/src/game/state/boot.hpp b/src/game/state/boot.hpp index 1e336bc..c4d0247 100644 --- a/src/game/state/boot.hpp +++ b/src/game/state/boot.hpp @@ -24,13 +24,12 @@ #include "game/context.hpp" #include -namespace game { namespace state { /** * Boots the game up on construction, and down on destruction. */ -class boot: public game::state::base +class boot: public ::state::base { public: /** @@ -40,7 +39,7 @@ public: * @param argc Command line argument count. * @param argv Command line argument vector. */ - boot(game::context& ctx, int argc, const char* const* argv); + boot(::context& ctx, int argc, const char* const* argv); /** * Boots down the game. @@ -71,6 +70,5 @@ private: }; } // namespace state -} // namespace game #endif // ANTKEEPER_GAME_STATE_BOOT_HPP diff --git a/src/game/state/collection-menu.cpp b/src/game/state/collection-menu.cpp index 3b7cf43..c674587 100644 --- a/src/game/state/collection-menu.cpp +++ b/src/game/state/collection-menu.cpp @@ -20,23 +20,22 @@ #include "game/state/collection-menu.hpp" #include "game/state/main-menu.hpp" #include "game/controls.hpp" -#include "scene/text.hpp" -#include "debug/log.hpp" +#include +#include #include "game/menu.hpp" #include "game/strings.hpp" -#include "utility/hash/fnv1a.hpp" -#include "resources/resource-manager.hpp" -#include "animation/screen-transition.hpp" -#include "animation/ease.hpp" -#include "render/passes/clear-pass.hpp" +#include +#include +#include +#include +#include using namespace hash::literals; -namespace game { namespace state { -collection_menu::collection_menu(game::context& ctx): - game::state::base(ctx) +collection_menu::collection_menu(::context& ctx): + ::state::base(ctx) { debug::log::trace("Entering collection menu state..."); @@ -125,7 +124,7 @@ collection_menu::collection_menu(game::context& ctx): ); // Queue enable menu controls - //ctx.function_queue.push(std::bind(game::enable_menu_controls, std::ref(ctx))); + //ctx.function_queue.push(std::bind(::enable_menu_controls, std::ref(ctx))); // Fade in from black ctx.fade_transition->transition(config::title_fade_in_duration, true, ease::out_cubic); @@ -138,7 +137,7 @@ collection_menu::~collection_menu() debug::log::trace("Exiting collection menu state..."); // Destruct menu - //game::disable_menu_controls(ctx); + //::disable_menu_controls(ctx); debug::log::trace("Exited collection menu state"); } @@ -178,4 +177,3 @@ void collection_menu::resize_box() } } // namespace state -} // namespace game diff --git a/src/game/state/collection-menu.hpp b/src/game/state/collection-menu.hpp index ed7985d..9eb3251 100644 --- a/src/game/state/collection-menu.hpp +++ b/src/game/state/collection-menu.hpp @@ -21,20 +21,19 @@ #define ANTKEEPER_GAME_STATE_COLLECTION_MENU_HPP #include "game/state/base.hpp" -#include "event/subscription.hpp" -#include "render/material.hpp" -#include "scene/billboard.hpp" -#include "animation/animation.hpp" -#include "geom/primitive/rectangle.hpp" +#include +#include +#include +#include +#include #include -namespace game { namespace state { -class collection_menu: public game::state::base +class collection_menu: public ::state::base { public: - collection_menu(game::context& ctx); + collection_menu(::context& ctx); virtual ~collection_menu(); private: @@ -60,6 +59,5 @@ private: }; } // namespace state -} // namespace game #endif // ANTKEEPER_GAME_STATE_COLLECTION_MENU_HPP diff --git a/src/game/state/controls-menu.cpp b/src/game/state/controls-menu.cpp index 66f663e..1aa429e 100644 --- a/src/game/state/controls-menu.cpp +++ b/src/game/state/controls-menu.cpp @@ -22,19 +22,18 @@ #include "game/state/gamepad-config-menu.hpp" #include "game/state/options-menu.hpp" #include "game/controls.hpp" -#include "scene/text.hpp" -#include "debug/log.hpp" +#include +#include #include "game/menu.hpp" #include "game/strings.hpp" -#include "utility/hash/fnv1a.hpp" +#include using namespace hash::literals; -namespace game { namespace state { -controls_menu::controls_menu(game::context& ctx): - game::state::base(ctx) +controls_menu::controls_menu(::context& ctx): + ::state::base(ctx) { debug::log::trace("Entering controls menu state..."); @@ -54,22 +53,22 @@ controls_menu::controls_menu(game::context& ctx): back_text->set_content(get_string(ctx, "back"_fnv1a32)); // Init menu item index - game::menu::init_menu_item_index(ctx, "controls"); + ::menu::init_menu_item_index(ctx, "controls"); - game::menu::update_text_color(ctx); - game::menu::update_text_font(ctx); - game::menu::align_text(ctx, true); - game::menu::update_text_tweens(ctx); - game::menu::add_text_to_ui(ctx); - game::menu::setup_animations(ctx); + ::menu::update_text_color(ctx); + ::menu::update_text_font(ctx); + ::menu::align_text(ctx, true); + ::menu::update_text_tweens(ctx); + ::menu::add_text_to_ui(ctx); + ::menu::setup_animations(ctx); // Construct menu item callbacks auto select_keyboard_callback = [&ctx]() { // Disable menu controls - ctx.function_queue.push(std::bind(game::disable_menu_controls, std::ref(ctx))); + ctx.function_queue.push(std::bind(::disable_menu_controls, std::ref(ctx))); - game::menu::fade_out + ::menu::fade_out ( ctx, [&ctx]() @@ -80,7 +79,7 @@ controls_menu::controls_menu(game::context& ctx): [&ctx]() { ctx.state_machine.pop(); - ctx.state_machine.emplace(new game::state::keyboard_config_menu(ctx)); + ctx.state_machine.emplace(new ::state::keyboard_config_menu(ctx)); } ); } @@ -89,9 +88,9 @@ controls_menu::controls_menu(game::context& ctx): auto select_gamepad_callback = [&ctx]() { // Disable menu controls - ctx.function_queue.push(std::bind(game::disable_menu_controls, std::ref(ctx))); + ctx.function_queue.push(std::bind(::disable_menu_controls, std::ref(ctx))); - game::menu::fade_out + ::menu::fade_out ( ctx, [&ctx]() @@ -102,7 +101,7 @@ controls_menu::controls_menu(game::context& ctx): [&ctx]() { ctx.state_machine.pop(); - ctx.state_machine.emplace(new game::state::gamepad_config_menu(ctx)); + ctx.state_machine.emplace(new ::state::gamepad_config_menu(ctx)); } ); } @@ -111,9 +110,9 @@ controls_menu::controls_menu(game::context& ctx): auto select_back_callback = [&ctx]() { // Disable menu controls - ctx.function_queue.push(std::bind(game::disable_menu_controls, std::ref(ctx))); + ctx.function_queue.push(std::bind(::disable_menu_controls, std::ref(ctx))); - game::menu::fade_out + ::menu::fade_out ( ctx, [&ctx]() @@ -124,7 +123,7 @@ controls_menu::controls_menu(game::context& ctx): [&ctx]() { ctx.state_machine.pop(); - ctx.state_machine.emplace(new game::state::options_menu(ctx)); + ctx.state_machine.emplace(new ::state::options_menu(ctx)); } ); } @@ -150,10 +149,10 @@ controls_menu::controls_menu(game::context& ctx): ctx.menu_back_callback = select_back_callback; // Queue menu control setup - ctx.function_queue.push(std::bind(game::enable_menu_controls, std::ref(ctx))); + ctx.function_queue.push(std::bind(::enable_menu_controls, std::ref(ctx))); // Fade in menu - game::menu::fade_in(ctx, nullptr); + ::menu::fade_in(ctx, nullptr); debug::log::trace("Entered controls menu state"); } @@ -163,14 +162,13 @@ controls_menu::~controls_menu() debug::log::trace("Exiting options menu state..."); // Destruct menu - game::disable_menu_controls(ctx); - game::menu::clear_callbacks(ctx); - game::menu::delete_animations(ctx); - game::menu::remove_text_from_ui(ctx); - game::menu::delete_text(ctx); + ::disable_menu_controls(ctx); + ::menu::clear_callbacks(ctx); + ::menu::delete_animations(ctx); + ::menu::remove_text_from_ui(ctx); + ::menu::delete_text(ctx); debug::log::trace("Exited controls menu state"); } } // namespace state -} // namespace game diff --git a/src/game/state/controls-menu.hpp b/src/game/state/controls-menu.hpp index 9f5a280..d3c2f50 100644 --- a/src/game/state/controls-menu.hpp +++ b/src/game/state/controls-menu.hpp @@ -22,17 +22,15 @@ #include "game/state/base.hpp" -namespace game { namespace state { -class controls_menu: public game::state::base +class controls_menu: public ::state::base { public: - controls_menu(game::context& ctx); + controls_menu(::context& ctx); virtual ~controls_menu(); }; } // namespace state -} // namespace game #endif // ANTKEEPER_GAME_STATE_CONTROLS_MENU_HPP diff --git a/src/game/state/credits.cpp b/src/game/state/credits.cpp index 63bbc05..74f862c 100644 --- a/src/game/state/credits.cpp +++ b/src/game/state/credits.cpp @@ -20,23 +20,22 @@ #include "game/state/credits.hpp" #include "game/state/extras-menu.hpp" #include "game/context.hpp" -#include "animation/ease.hpp" -#include "animation/animation.hpp" -#include "animation/animator.hpp" -#include "scene/text.hpp" -#include "debug/log.hpp" +#include +#include +#include +#include +#include #include "game/strings.hpp" -#include "utility/hash/fnv1a.hpp" -#include "math/glsl.hpp" +#include +#include using namespace hash::literals; using namespace math::glsl; -namespace game { namespace state { -credits::credits(game::context& ctx): - game::state::base(ctx) +credits::credits(::context& ctx): + ::state::base(ctx) { debug::log::trace("Entering credits state..."); @@ -102,7 +101,7 @@ credits::credits(game::context& ctx): { // Change to extras menu state ctx.state_machine.pop(); - ctx.state_machine.emplace(new game::state::extras_menu(ctx)); + ctx.state_machine.emplace(new ::state::extras_menu(ctx)); } ); }; @@ -162,4 +161,3 @@ credits::~credits() } } // namespace state -} // namespace game diff --git a/src/game/state/credits.hpp b/src/game/state/credits.hpp index 02e330c..b307114 100644 --- a/src/game/state/credits.hpp +++ b/src/game/state/credits.hpp @@ -21,18 +21,17 @@ #define ANTKEEPER_GAME_STATE_CREDITS_HPP #include "game/state/base.hpp" -#include "scene/text.hpp" -#include "animation/animation.hpp" -#include "event/subscription.hpp" +#include +#include +#include #include -namespace game { namespace state { -class credits: public game::state::base +class credits: public ::state::base { public: - credits(game::context& ctx); + credits(::context& ctx); virtual ~credits(); private: @@ -44,6 +43,5 @@ private: }; } // namespace state -} // namespace game #endif // ANTKEEPER_GAME_STATE_CREDITS_HPP diff --git a/src/game/state/extras-menu.cpp b/src/game/state/extras-menu.cpp index 18d8727..ca2402f 100644 --- a/src/game/state/extras-menu.cpp +++ b/src/game/state/extras-menu.cpp @@ -21,20 +21,19 @@ #include "game/state/main-menu.hpp" #include "game/state/credits.hpp" #include "game/controls.hpp" -#include "scene/text.hpp" -#include "debug/log.hpp" +#include +#include #include "game/fonts.hpp" #include "game/menu.hpp" #include "game/strings.hpp" -#include "utility/hash/fnv1a.hpp" +#include using namespace hash::literals; -namespace game { namespace state { -extras_menu::extras_menu(game::context& ctx): - game::state::base(ctx) +extras_menu::extras_menu(::context& ctx): + ::state::base(ctx) { debug::log::trace("Entering extras menu state..."); @@ -51,22 +50,22 @@ extras_menu::extras_menu(game::context& ctx): back_text->set_content(get_string(ctx, "back"_fnv1a32)); // Init menu item index - game::menu::init_menu_item_index(ctx, "extras"); + ::menu::init_menu_item_index(ctx, "extras"); - game::menu::update_text_color(ctx); - game::menu::update_text_font(ctx); - game::menu::align_text(ctx); - game::menu::update_text_tweens(ctx); - game::menu::add_text_to_ui(ctx); - game::menu::setup_animations(ctx); + ::menu::update_text_color(ctx); + ::menu::update_text_font(ctx); + ::menu::align_text(ctx); + ::menu::update_text_tweens(ctx); + ::menu::add_text_to_ui(ctx); + ::menu::setup_animations(ctx); // Construct menu item callbacks auto select_credits_callback = [&ctx]() { // Disable menu controls - ctx.function_queue.push(std::bind(game::disable_menu_controls, std::ref(ctx))); + ctx.function_queue.push(std::bind(::disable_menu_controls, std::ref(ctx))); - game::menu::fade_out + ::menu::fade_out ( ctx, [&ctx]() @@ -77,7 +76,7 @@ extras_menu::extras_menu(game::context& ctx): [&ctx]() { ctx.state_machine.pop(); - ctx.state_machine.emplace(new game::state::credits(ctx)); + ctx.state_machine.emplace(new ::state::credits(ctx)); } ); } @@ -86,9 +85,9 @@ extras_menu::extras_menu(game::context& ctx): auto select_back_callback = [&ctx]() { // Disable menu controls - ctx.function_queue.push(std::bind(game::disable_menu_controls, std::ref(ctx))); + ctx.function_queue.push(std::bind(::disable_menu_controls, std::ref(ctx))); - game::menu::fade_out + ::menu::fade_out ( ctx, [&ctx]() @@ -99,7 +98,7 @@ extras_menu::extras_menu(game::context& ctx): [&ctx]() { ctx.state_machine.pop(); - ctx.state_machine.emplace(new game::state::main_menu(ctx, false)); + ctx.state_machine.emplace(new ::state::main_menu(ctx, false)); } ); } @@ -122,10 +121,10 @@ extras_menu::extras_menu(game::context& ctx): ctx.menu_back_callback = select_back_callback; // Fade in menu - game::menu::fade_in(ctx, nullptr); + ::menu::fade_in(ctx, nullptr); // Queue enable menu controls - ctx.function_queue.push(std::bind(game::enable_menu_controls, std::ref(ctx))); + ctx.function_queue.push(std::bind(::enable_menu_controls, std::ref(ctx))); debug::log::trace("Entered extras menu state"); } @@ -135,14 +134,13 @@ extras_menu::~extras_menu() debug::log::trace("Exiting extras menu state..."); // Destruct menu - game::disable_menu_controls(ctx); - game::menu::clear_callbacks(ctx); - game::menu::delete_animations(ctx); - game::menu::remove_text_from_ui(ctx); - game::menu::delete_text(ctx); + ::disable_menu_controls(ctx); + ::menu::clear_callbacks(ctx); + ::menu::delete_animations(ctx); + ::menu::remove_text_from_ui(ctx); + ::menu::delete_text(ctx); debug::log::trace("Exited extras menu state"); } } // namespace state -} // namespace game diff --git a/src/game/state/extras-menu.hpp b/src/game/state/extras-menu.hpp index 46983f0..1e66ca2 100644 --- a/src/game/state/extras-menu.hpp +++ b/src/game/state/extras-menu.hpp @@ -22,17 +22,15 @@ #include "game/state/base.hpp" -namespace game { namespace state { -class extras_menu: public game::state::base +class extras_menu: public ::state::base { public: - extras_menu(game::context& ctx); + extras_menu(::context& ctx); virtual ~extras_menu(); }; } // namespace state -} // namespace game #endif // ANTKEEPER_GAME_STATE_EXTRAS_MENU_HPP diff --git a/src/game/state/gamepad-config-menu.cpp b/src/game/state/gamepad-config-menu.cpp index 05edf7a..1ae3eb1 100644 --- a/src/game/state/gamepad-config-menu.cpp +++ b/src/game/state/gamepad-config-menu.cpp @@ -21,33 +21,32 @@ #include "game/state/controls-menu.hpp" #include "game/controls.hpp" #include "game/context.hpp" -#include "scene/text.hpp" -#include "debug/log.hpp" -#include "resources/resource-manager.hpp" +#include +#include +#include #include "game/menu.hpp" #include "game/controls.hpp" #include "game/strings.hpp" -#include "utility/hash/fnv1a.hpp" +#include using namespace hash::literals; -namespace game { namespace state { -gamepad_config_menu::gamepad_config_menu(game::context& ctx): - game::state::base(ctx), +gamepad_config_menu::gamepad_config_menu(::context& ctx): + ::state::base(ctx), action_remapped(false) { debug::log::trace("Entering gamepad config menu state..."); // Add control menu items - add_control_item(ctx.movement_actions, ctx.move_forward_action, "control_move_forward"_fnv1a32); - add_control_item(ctx.movement_actions, ctx.move_back_action, "control_move_back"_fnv1a32); - add_control_item(ctx.movement_actions, ctx.move_left_action, "control_move_left"_fnv1a32); - add_control_item(ctx.movement_actions, ctx.move_right_action, "control_move_right"_fnv1a32); - add_control_item(ctx.movement_actions, ctx.move_up_action, "control_move_up"_fnv1a32); - add_control_item(ctx.movement_actions, ctx.move_down_action, "control_move_down"_fnv1a32); - add_control_item(ctx.movement_actions, ctx.pause_action, "control_pause"_fnv1a32); + add_control_item(ctx.movement_action_map, ctx.move_forward_action, "control_move_forward"_fnv1a32); + add_control_item(ctx.movement_action_map, ctx.move_back_action, "control_move_back"_fnv1a32); + add_control_item(ctx.movement_action_map, ctx.move_left_action, "control_move_left"_fnv1a32); + add_control_item(ctx.movement_action_map, ctx.move_right_action, "control_move_right"_fnv1a32); + add_control_item(ctx.movement_action_map, ctx.move_up_action, "control_move_up"_fnv1a32); + add_control_item(ctx.movement_action_map, ctx.move_down_action, "control_move_down"_fnv1a32); + add_control_item(ctx.movement_action_map, ctx.pause_action, "control_pause"_fnv1a32); // Construct menu item texts scene::text* back_text = new scene::text(); @@ -59,22 +58,22 @@ gamepad_config_menu::gamepad_config_menu(game::context& ctx): back_text->set_content(get_string(ctx, "back"_fnv1a32)); // Init menu item index - game::menu::init_menu_item_index(ctx, "gamepad_config"); + ::menu::init_menu_item_index(ctx, "gamepad_config"); - game::menu::update_text_color(ctx); - game::menu::update_text_font(ctx); - game::menu::align_text(ctx); - game::menu::update_text_tweens(ctx); - game::menu::add_text_to_ui(ctx); - game::menu::setup_animations(ctx); + ::menu::update_text_color(ctx); + ::menu::update_text_font(ctx); + ::menu::align_text(ctx); + ::menu::update_text_tweens(ctx); + ::menu::add_text_to_ui(ctx); + ::menu::setup_animations(ctx); // Construct menu item callbacks auto select_back_callback = [&ctx]() { // Disable menu controls - ctx.function_queue.push(std::bind(game::disable_menu_controls, std::ref(ctx))); + ctx.function_queue.push(std::bind(::disable_menu_controls, std::ref(ctx))); - game::menu::fade_out + ::menu::fade_out ( ctx, [&ctx]() @@ -85,7 +84,7 @@ gamepad_config_menu::gamepad_config_menu(game::context& ctx): [&ctx]() { ctx.state_machine.pop(); - ctx.state_machine.emplace(new game::state::controls_menu(ctx)); + ctx.state_machine.emplace(new ::state::controls_menu(ctx)); } ); } @@ -105,10 +104,10 @@ gamepad_config_menu::gamepad_config_menu(game::context& ctx): ctx.menu_back_callback = select_back_callback; // Queue menu control setup - ctx.function_queue.push(std::bind(game::enable_menu_controls, std::ref(ctx))); + ctx.function_queue.push(std::bind(::enable_menu_controls, std::ref(ctx))); // Fade in menu - game::menu::fade_in(ctx, nullptr); + ::menu::fade_in(ctx, nullptr); debug::log::trace("Entered gamepad config menu state"); } @@ -118,16 +117,16 @@ gamepad_config_menu::~gamepad_config_menu() debug::log::trace("Exiting gamepad config menu state..."); // Destruct menu - game::disable_menu_controls(ctx); - game::menu::clear_callbacks(ctx); - game::menu::delete_animations(ctx); - game::menu::remove_text_from_ui(ctx); - game::menu::delete_text(ctx); + ::disable_menu_controls(ctx); + ::menu::clear_callbacks(ctx); + ::menu::delete_animations(ctx); + ::menu::remove_text_from_ui(ctx); + ::menu::delete_text(ctx); if (action_remapped) { // Update control profile - game::update_control_profile(ctx, *ctx.control_profile); + ::update_control_profile(ctx, *ctx.control_profile); // Save control profile ctx.resource_manager->set_write_dir(ctx.controls_path); @@ -318,8 +317,8 @@ void gamepad_config_menu::add_control_item(input::action_map& action_map, input: // Update control mapping text value_text->set_content(this->get_mapping_string(*action_map, *control)); - game::menu::align_text(ctx); - game::menu::update_text_tweens(ctx); + ::menu::align_text(ctx); + ::menu::update_text_tweens(ctx); // Queue disabling of input mapper re-enabling of menu controls ctx.function_queue.push @@ -327,7 +326,7 @@ void gamepad_config_menu::add_control_item(input::action_map& action_map, input: [&ctx]() { ctx.input_mapper.disconnect(); - game::enable_menu_controls(ctx); + ::enable_menu_controls(ctx); } ); }; @@ -337,8 +336,8 @@ void gamepad_config_menu::add_control_item(input::action_map& action_map, input: { // Set control mapping text to "..." value_text->set_content(get_string(ctx, "control_mapping"_fnv1a32)); - game::menu::align_text(ctx); - game::menu::update_text_tweens(ctx); + ::menu::align_text(ctx); + ::menu::update_text_tweens(ctx); // Setup input mapped callbacks gamepad_axis_mapped_subscription = ctx.input_mapper.get_gamepad_axis_mapped_channel().subscribe @@ -359,7 +358,7 @@ void gamepad_config_menu::add_control_item(input::action_map& action_map, input: ( [&]() { - game::disable_menu_controls(ctx); + ::disable_menu_controls(ctx); ctx.input_mapper.connect(ctx.input_manager->get_event_queue()); } ); @@ -372,4 +371,3 @@ void gamepad_config_menu::add_control_item(input::action_map& action_map, input: } } // namespace state -} // namespace game diff --git a/src/game/state/gamepad-config-menu.hpp b/src/game/state/gamepad-config-menu.hpp index 7846f3e..24c5001 100644 --- a/src/game/state/gamepad-config-menu.hpp +++ b/src/game/state/gamepad-config-menu.hpp @@ -21,19 +21,18 @@ #define ANTKEEPER_GAME_STATE_GAMEPAD_CONFIG_MENU_HPP #include "game/state/base.hpp" -#include "input/action.hpp" -#include "input/action-map.hpp" -#include "event/subscription.hpp" +#include +#include +#include #include #include -namespace game { namespace state { -class gamepad_config_menu: public game::state::base +class gamepad_config_menu: public ::state::base { public: - gamepad_config_menu(game::context& ctx); + gamepad_config_menu(::context& ctx); virtual ~gamepad_config_menu(); private: @@ -48,6 +47,5 @@ private: }; } // namespace state -} // namespace game #endif // ANTKEEPER_GAME_STATE_GAMEPAD_CONFIG_MENU_HPP diff --git a/src/game/state/graphics-menu.cpp b/src/game/state/graphics-menu.cpp index ec33a1c..3ca20eb 100644 --- a/src/game/state/graphics-menu.cpp +++ b/src/game/state/graphics-menu.cpp @@ -20,24 +20,23 @@ #include "game/state/graphics-menu.hpp" #include "game/state/options-menu.hpp" #include "game/controls.hpp" -#include "scene/text.hpp" -#include "debug/log.hpp" +#include +#include #include "game/fonts.hpp" #include "game/menu.hpp" #include "game/graphics.hpp" -#include "animation/timeline.hpp" +#include #include "game/strings.hpp" -#include "utility/hash/fnv1a.hpp" +#include using namespace hash::literals; -namespace game { namespace state { -static void update_value_text_content(game::context* ctx); +static void update_value_text_content(::context* ctx); -graphics_menu::graphics_menu(game::context& ctx): - game::state::base(ctx) +graphics_menu::graphics_menu(::context& ctx): + ::state::base(ctx) { debug::log::trace("Entering graphics menu state..."); @@ -76,14 +75,14 @@ graphics_menu::graphics_menu(game::context& ctx): update_value_text_content(); // Init menu item index - game::menu::init_menu_item_index(ctx, "graphics"); + ::menu::init_menu_item_index(ctx, "graphics"); - game::menu::update_text_color(ctx); - game::menu::update_text_font(ctx); - game::menu::align_text(ctx); - game::menu::update_text_tweens(ctx); - game::menu::add_text_to_ui(ctx); - game::menu::setup_animations(ctx); + ::menu::update_text_color(ctx); + ::menu::update_text_font(ctx); + ::menu::align_text(ctx); + ::menu::update_text_tweens(ctx); + ::menu::add_text_to_ui(ctx); + ::menu::setup_animations(ctx); // Construct menu item callbacks auto toggle_fullscreen_callback = [this, &ctx]() @@ -93,8 +92,8 @@ graphics_menu::graphics_menu(game::context& ctx): ctx.window->set_fullscreen(fullscreen); this->update_value_text_content(); - game::menu::align_text(ctx); - game::menu::update_text_tweens(ctx); + ::menu::align_text(ctx); + ::menu::update_text_tweens(ctx); // Update fullscreen settings (*ctx.settings)["fullscreen"_fnv1a32] = fullscreen; @@ -116,12 +115,12 @@ graphics_menu::graphics_menu(game::context& ctx): (*ctx.settings)["render_scale"_fnv1a32] = ctx.render_scale; // Resize framebuffers - game::graphics::change_render_resolution(ctx, ctx.render_scale); + ::graphics::change_render_resolution(ctx, ctx.render_scale); // Update text this->update_value_text_content(); - game::menu::align_text(ctx); - game::menu::update_text_tweens(ctx); + ::menu::align_text(ctx); + ::menu::update_text_tweens(ctx); }; auto decrease_resolution_callback = [this, &ctx]() @@ -140,12 +139,12 @@ graphics_menu::graphics_menu(game::context& ctx): (*ctx.settings)["render_scale"_fnv1a32] = ctx.render_scale; // Resize framebuffers - game::graphics::change_render_resolution(ctx, ctx.render_scale); + ::graphics::change_render_resolution(ctx, ctx.render_scale); // Update text this->update_value_text_content(); - game::menu::align_text(ctx); - game::menu::update_text_tweens(ctx); + ::menu::align_text(ctx); + ::menu::update_text_tweens(ctx); }; auto toggle_v_sync_callback = [this, &ctx]() @@ -158,8 +157,8 @@ graphics_menu::graphics_menu(game::context& ctx): ctx.window->set_v_sync(v_sync); this->update_value_text_content(); - game::menu::align_text(ctx); - game::menu::update_text_tweens(ctx); + ::menu::align_text(ctx); + ::menu::update_text_tweens(ctx); }; auto next_aa_method_callback = [this, &ctx]() @@ -178,15 +177,15 @@ graphics_menu::graphics_menu(game::context& ctx): // Update anti-aliasing method setting (*ctx.settings)["anti_aliasing_method"_fnv1a32] = std::to_underlying(ctx.anti_aliasing_method); - game::graphics::select_anti_aliasing_method(ctx, ctx.anti_aliasing_method); + ::graphics::select_anti_aliasing_method(ctx, ctx.anti_aliasing_method); // Update value text this->update_value_text_content(); // Refresh and realign text - game::menu::refresh_text(ctx); - game::menu::align_text(ctx); - game::menu::update_text_tweens(ctx); + ::menu::refresh_text(ctx); + ::menu::align_text(ctx); + ::menu::update_text_tweens(ctx); }; auto previous_aa_method_callback = [this, &ctx]() @@ -205,15 +204,15 @@ graphics_menu::graphics_menu(game::context& ctx): // Update anti-aliasing method setting (*ctx.settings)["anti_aliasing_method"_fnv1a32] = std::to_underlying(ctx.anti_aliasing_method); - game::graphics::select_anti_aliasing_method(ctx, ctx.anti_aliasing_method); + ::graphics::select_anti_aliasing_method(ctx, ctx.anti_aliasing_method); // Update value text this->update_value_text_content(); // Refresh and realign text - game::menu::refresh_text(ctx); - game::menu::align_text(ctx); - game::menu::update_text_tweens(ctx); + ::menu::refresh_text(ctx); + ::menu::align_text(ctx); + ::menu::update_text_tweens(ctx); }; auto increase_font_scale_callback = [this, &ctx]() @@ -236,13 +235,13 @@ graphics_menu::graphics_menu(game::context& ctx): // Reload fonts debug::log::trace("Reloading fonts..."); - game::load_fonts(ctx); + ::load_fonts(ctx); debug::log::trace("Reloaded fonts"); // Refresh and realign text - game::menu::refresh_text(ctx); - game::menu::align_text(ctx); - game::menu::update_text_tweens(ctx); + ::menu::refresh_text(ctx); + ::menu::align_text(ctx); + ::menu::update_text_tweens(ctx); }; auto decrease_font_scale_callback = [this, &ctx]() @@ -265,13 +264,13 @@ graphics_menu::graphics_menu(game::context& ctx): // Reload fonts debug::log::trace("Reloading fonts..."); - game::load_fonts(ctx); + ::load_fonts(ctx); debug::log::trace("Reloaded fonts"); // Refresh and realign text - game::menu::refresh_text(ctx); - game::menu::align_text(ctx); - game::menu::update_text_tweens(ctx); + ::menu::refresh_text(ctx); + ::menu::align_text(ctx); + ::menu::update_text_tweens(ctx); }; auto toggle_dyslexia_font_callback = [this, &ctx]() @@ -286,20 +285,20 @@ graphics_menu::graphics_menu(game::context& ctx): // Reload fonts debug::log::trace("Reloading fonts..."); - game::load_fonts(ctx); + ::load_fonts(ctx); debug::log::trace("Reloaded fonts"); // Refresh and realign text - game::menu::refresh_text(ctx); - game::menu::align_text(ctx); - game::menu::update_text_tweens(ctx); + ::menu::refresh_text(ctx); + ::menu::align_text(ctx); + ::menu::update_text_tweens(ctx); }; auto select_back_callback = [&ctx]() { // Disable menu controls - ctx.function_queue.push(std::bind(game::disable_menu_controls, std::ref(ctx))); + ctx.function_queue.push(std::bind(::disable_menu_controls, std::ref(ctx))); - game::menu::fade_out + ::menu::fade_out ( ctx, [&ctx]() @@ -310,7 +309,7 @@ graphics_menu::graphics_menu(game::context& ctx): [&ctx]() { ctx.state_machine.pop(); - ctx.state_machine.emplace(new game::state::options_menu(ctx)); + ctx.state_machine.emplace(new ::state::options_menu(ctx)); } ); } @@ -348,10 +347,10 @@ graphics_menu::graphics_menu(game::context& ctx): ctx.menu_back_callback = select_back_callback; // Enable menu controls next frame - ctx.function_queue.push(std::bind(game::enable_menu_controls, std::ref(ctx))); + ctx.function_queue.push(std::bind(::enable_menu_controls, std::ref(ctx))); // Fade in menu - game::menu::fade_in(ctx, nullptr); + ::menu::fade_in(ctx, nullptr); debug::log::trace("Entered graphics menu state"); } @@ -361,11 +360,11 @@ graphics_menu::~graphics_menu() debug::log::trace("Exiting graphics menu state..."); // Destruct menu - game::disable_menu_controls(ctx); - game::menu::clear_callbacks(ctx); - game::menu::delete_animations(ctx); - game::menu::remove_text_from_ui(ctx); - game::menu::delete_text(ctx); + ::disable_menu_controls(ctx); + ::menu::clear_callbacks(ctx); + ::menu::delete_animations(ctx); + ::menu::remove_text_from_ui(ctx); + ::menu::delete_text(ctx); debug::log::trace("Exited graphics menu state"); } @@ -407,4 +406,3 @@ void graphics_menu::update_value_text_content() } } // namespace state -} // namespace game diff --git a/src/game/state/graphics-menu.hpp b/src/game/state/graphics-menu.hpp index a1b5d06..dfb8915 100644 --- a/src/game/state/graphics-menu.hpp +++ b/src/game/state/graphics-menu.hpp @@ -22,13 +22,12 @@ #include "game/state/base.hpp" -namespace game { namespace state { -class graphics_menu: public game::state::base +class graphics_menu: public ::state::base { public: - graphics_menu(game::context& ctx); + graphics_menu(::context& ctx); virtual ~graphics_menu(); private: @@ -36,6 +35,5 @@ private: }; } // namespace state -} // namespace game #endif // ANTKEEPER_GAME_STATE_GRAPHICS_MENU_HPP diff --git a/src/game/state/keyboard-config-menu.cpp b/src/game/state/keyboard-config-menu.cpp index caf96eb..1d28c7d 100644 --- a/src/game/state/keyboard-config-menu.cpp +++ b/src/game/state/keyboard-config-menu.cpp @@ -20,35 +20,34 @@ #include "game/state/keyboard-config-menu.hpp" #include "game/state/controls-menu.hpp" #include "game/controls.hpp" -#include "scene/text.hpp" -#include "debug/log.hpp" -#include "resources/resource-manager.hpp" +#include +#include +#include #include "game/menu.hpp" #include "game/controls.hpp" #include "game/strings.hpp" -#include "utility/hash/fnv1a.hpp" +#include #include #include using namespace hash::literals; -namespace game { namespace state { -keyboard_config_menu::keyboard_config_menu(game::context& ctx): - game::state::base(ctx), +keyboard_config_menu::keyboard_config_menu(::context& ctx): + ::state::base(ctx), action_remapped(false) { debug::log::trace("Entering keyboard config menu state..."); // Add control menu items - add_control_item(ctx.movement_actions, ctx.move_forward_action, "control_move_forward"_fnv1a32); - add_control_item(ctx.movement_actions, ctx.move_back_action, "control_move_back"_fnv1a32); - add_control_item(ctx.movement_actions, ctx.move_left_action, "control_move_left"_fnv1a32); - add_control_item(ctx.movement_actions, ctx.move_right_action, "control_move_right"_fnv1a32); - add_control_item(ctx.movement_actions, ctx.move_up_action, "control_move_up"_fnv1a32); - add_control_item(ctx.movement_actions, ctx.move_down_action, "control_move_down"_fnv1a32); - add_control_item(ctx.movement_actions, ctx.pause_action, "control_pause"_fnv1a32); + add_control_item(ctx.movement_action_map, ctx.move_forward_action, "control_move_forward"_fnv1a32); + add_control_item(ctx.movement_action_map, ctx.move_back_action, "control_move_back"_fnv1a32); + add_control_item(ctx.movement_action_map, ctx.move_left_action, "control_move_left"_fnv1a32); + add_control_item(ctx.movement_action_map, ctx.move_right_action, "control_move_right"_fnv1a32); + add_control_item(ctx.movement_action_map, ctx.move_up_action, "control_move_up"_fnv1a32); + add_control_item(ctx.movement_action_map, ctx.move_down_action, "control_move_down"_fnv1a32); + add_control_item(ctx.movement_action_map, ctx.pause_action, "control_pause"_fnv1a32); // Construct menu item texts scene::text* back_text = new scene::text(); @@ -60,22 +59,22 @@ keyboard_config_menu::keyboard_config_menu(game::context& ctx): back_text->set_content(get_string(ctx, "back"_fnv1a32)); // Init menu item index - game::menu::init_menu_item_index(ctx, "keyboard_config"); + ::menu::init_menu_item_index(ctx, "keyboard_config"); - game::menu::update_text_color(ctx); - game::menu::update_text_font(ctx); - game::menu::align_text(ctx); - game::menu::update_text_tweens(ctx); - game::menu::add_text_to_ui(ctx); - game::menu::setup_animations(ctx); + ::menu::update_text_color(ctx); + ::menu::update_text_font(ctx); + ::menu::align_text(ctx); + ::menu::update_text_tweens(ctx); + ::menu::add_text_to_ui(ctx); + ::menu::setup_animations(ctx); // Construct menu item callbacks auto select_back_callback = [&ctx]() { // Disable menu controls - ctx.function_queue.push(std::bind(game::disable_menu_controls, std::ref(ctx))); + ctx.function_queue.push(std::bind(::disable_menu_controls, std::ref(ctx))); - game::menu::fade_out + ::menu::fade_out ( ctx, [&ctx]() @@ -86,7 +85,7 @@ keyboard_config_menu::keyboard_config_menu(game::context& ctx): [&ctx]() { ctx.state_machine.pop(); - ctx.state_machine.emplace(new game::state::controls_menu(ctx)); + ctx.state_machine.emplace(new ::state::controls_menu(ctx)); } ); } @@ -106,10 +105,10 @@ keyboard_config_menu::keyboard_config_menu(game::context& ctx): ctx.menu_back_callback = select_back_callback; // Enable menu controls next frame - ctx.function_queue.push(std::bind(game::enable_menu_controls, std::ref(ctx))); + ctx.function_queue.push(std::bind(::enable_menu_controls, std::ref(ctx))); // Fade in menu - game::menu::fade_in(ctx, nullptr); + ::menu::fade_in(ctx, nullptr); debug::log::trace("Entered keyboard config menu state"); } @@ -119,16 +118,16 @@ keyboard_config_menu::~keyboard_config_menu() debug::log::trace("Exiting keyboard config menu state..."); // Destruct menu - game::disable_menu_controls(ctx); - game::menu::clear_callbacks(ctx); - game::menu::delete_animations(ctx); - game::menu::remove_text_from_ui(ctx); - game::menu::delete_text(ctx); + ::disable_menu_controls(ctx); + ::menu::clear_callbacks(ctx); + ::menu::delete_animations(ctx); + ::menu::remove_text_from_ui(ctx); + ::menu::delete_text(ctx); if (action_remapped) { // Update control profile - game::update_control_profile(ctx, *ctx.control_profile); + ::update_control_profile(ctx, *ctx.control_profile); // Save control profile ctx.resource_manager->set_write_dir(ctx.controls_path); @@ -243,8 +242,8 @@ void keyboard_config_menu::add_control_item(input::action_map& action_map, input // Update control mapping text value_text->set_content(this->get_mapping_string(*action_map, *control)); - game::menu::align_text(ctx); - game::menu::update_text_tweens(ctx); + ::menu::align_text(ctx); + ::menu::update_text_tweens(ctx); // Queue disabling of input mapper re-enabling of menu controls ctx.function_queue.push @@ -252,7 +251,7 @@ void keyboard_config_menu::add_control_item(input::action_map& action_map, input [&ctx]() { ctx.input_mapper.disconnect(); - game::enable_menu_controls(ctx); + ::enable_menu_controls(ctx); } ); }; @@ -262,8 +261,8 @@ void keyboard_config_menu::add_control_item(input::action_map& action_map, input { // Set control mapping text to "..." value_text->set_content(get_string(ctx, "control_mapping"_fnv1a32)); - game::menu::align_text(ctx); - game::menu::update_text_tweens(ctx); + ::menu::align_text(ctx); + ::menu::update_text_tweens(ctx); // Setup input mapped callbacks key_mapped_subscription = ctx.input_mapper.get_key_mapped_channel().subscribe @@ -284,7 +283,7 @@ void keyboard_config_menu::add_control_item(input::action_map& action_map, input ( [&]() { - game::disable_menu_controls(ctx); + ::disable_menu_controls(ctx); ctx.input_mapper.connect(ctx.input_manager->get_event_queue()); } ); @@ -297,4 +296,3 @@ void keyboard_config_menu::add_control_item(input::action_map& action_map, input } } // namespace state -} // namespace game diff --git a/src/game/state/keyboard-config-menu.hpp b/src/game/state/keyboard-config-menu.hpp index 9ea0df2..4cda283 100644 --- a/src/game/state/keyboard-config-menu.hpp +++ b/src/game/state/keyboard-config-menu.hpp @@ -21,19 +21,18 @@ #define ANTKEEPER_GAME_STATE_KEYBOARD_CONFIG_MENU_HPP #include "game/state/base.hpp" -#include "input/action.hpp" -#include "input/action-map.hpp" -#include "event/subscription.hpp" +#include +#include +#include #include #include -namespace game { namespace state { -class keyboard_config_menu: public game::state::base +class keyboard_config_menu: public ::state::base { public: - keyboard_config_menu(game::context& ctx); + keyboard_config_menu(::context& ctx); virtual ~keyboard_config_menu(); private: @@ -48,6 +47,5 @@ private: }; } // namespace state -} // namespace game #endif // ANTKEEPER_GAME_STATE_KEYBOARD_CONFIG_MENU_HPP diff --git a/src/game/state/language-menu.cpp b/src/game/state/language-menu.cpp index c4496b6..e768863 100644 --- a/src/game/state/language-menu.cpp +++ b/src/game/state/language-menu.cpp @@ -20,23 +20,22 @@ #include "game/state/language-menu.hpp" #include "game/state/options-menu.hpp" #include "game/controls.hpp" -#include "scene/text.hpp" -#include "debug/log.hpp" +#include +#include #include "game/fonts.hpp" #include "game/menu.hpp" #include "game/strings.hpp" -#include "utility/hash/fnv1a.hpp" -#include "resources/resource-manager.hpp" +#include +#include #include #include using namespace hash::literals; -namespace game { namespace state { -language_menu::language_menu(game::context& ctx): - game::state::base(ctx) +language_menu::language_menu(::context& ctx): + ::state::base(ctx) { debug::log::trace("Entering language menu state..."); @@ -72,14 +71,14 @@ language_menu::language_menu(game::context& ctx): update_text_content(); // Init menu item index - game::menu::init_menu_item_index(ctx, "language"); + ::menu::init_menu_item_index(ctx, "language"); - game::menu::update_text_color(ctx); - game::menu::update_text_font(ctx); - game::menu::align_text(ctx); - game::menu::update_text_tweens(ctx); - game::menu::add_text_to_ui(ctx); - game::menu::setup_animations(ctx); + ::menu::update_text_color(ctx); + ::menu::update_text_font(ctx); + ::menu::align_text(ctx); + ::menu::update_text_tweens(ctx); + ::menu::add_text_to_ui(ctx); + ::menu::setup_animations(ctx); auto change_language = [this, &ctx]() { @@ -110,15 +109,15 @@ language_menu::language_menu(game::context& ctx): // Reload fonts debug::log::trace("Reloading fonts..."); - game::load_fonts(ctx); + ::load_fonts(ctx); debug::log::trace("Reloaded fonts"); // Update menus - game::menu::update_text_font(ctx); + ::menu::update_text_font(ctx); this->update_text_content(); - game::menu::refresh_text(ctx); - game::menu::align_text(ctx); - game::menu::update_text_tweens(ctx); + ::menu::refresh_text(ctx); + ::menu::align_text(ctx); + ::menu::update_text_tweens(ctx); }; // Construct menu item callbacks @@ -143,9 +142,9 @@ language_menu::language_menu(game::context& ctx): auto select_back_callback = [&ctx]() { // Disable menu controls - ctx.function_queue.push(std::bind(game::disable_menu_controls, std::ref(ctx))); + ctx.function_queue.push(std::bind(::disable_menu_controls, std::ref(ctx))); - game::menu::fade_out + ::menu::fade_out ( ctx, [&ctx]() @@ -156,7 +155,7 @@ language_menu::language_menu(game::context& ctx): [&ctx]() { ctx.state_machine.pop(); - ctx.state_machine.emplace(new game::state::options_menu(ctx)); + ctx.state_machine.emplace(new ::state::options_menu(ctx)); } ); } @@ -179,10 +178,10 @@ language_menu::language_menu(game::context& ctx): ctx.menu_back_callback = select_back_callback; // Enable menu controls next frame - ctx.function_queue.push(std::bind(game::enable_menu_controls, std::ref(ctx))); + ctx.function_queue.push(std::bind(::enable_menu_controls, std::ref(ctx))); // Fade in menu - game::menu::fade_in(ctx, nullptr); + ::menu::fade_in(ctx, nullptr); debug::log::trace("Entered language menu state"); } @@ -192,11 +191,11 @@ language_menu::~language_menu() debug::log::trace("Exiting language menu state..."); // Destruct menu - game::disable_menu_controls(ctx); - game::menu::clear_callbacks(ctx); - game::menu::delete_animations(ctx); - game::menu::remove_text_from_ui(ctx); - game::menu::delete_text(ctx); + ::disable_menu_controls(ctx); + ::menu::clear_callbacks(ctx); + ::menu::delete_animations(ctx); + ::menu::remove_text_from_ui(ctx); + ::menu::delete_text(ctx); debug::log::trace("Exited language menu state"); } @@ -212,4 +211,3 @@ void language_menu::update_text_content() } } // namespace state -} // namespace game diff --git a/src/game/state/language-menu.hpp b/src/game/state/language-menu.hpp index d8840d7..66a76ea 100644 --- a/src/game/state/language-menu.hpp +++ b/src/game/state/language-menu.hpp @@ -24,13 +24,12 @@ #include #include -namespace game { namespace state { -class language_menu: public game::state::base +class language_menu: public ::state::base { public: - language_menu(game::context& ctx); + language_menu(::context& ctx); virtual ~language_menu(); private: @@ -42,6 +41,5 @@ private: } // namespace state -} // namespace game #endif // ANTKEEPER_GAME_STATE_LANGUAGE_MENU_HPP diff --git a/src/game/state/main-menu.cpp b/src/game/state/main-menu.cpp index 8969eb5..41b7d8d 100644 --- a/src/game/state/main-menu.cpp +++ b/src/game/state/main-menu.cpp @@ -18,14 +18,14 @@ */ #include "game/state/main-menu.hpp" -#include "animation/animation.hpp" -#include "animation/animator.hpp" -#include "animation/ease.hpp" -#include "animation/screen-transition.hpp" -#include "config.hpp" -#include "game/component/model.hpp" -#include "game/component/steering.hpp" -#include "game/component/transform.hpp" +#include +#include +#include +#include +#include +#include "game/components/model-component.hpp" +#include "game/components/steering-component.hpp" +#include "game/components/transform-component.hpp" #include "game/controls.hpp" #include "game/ecoregion.hpp" #include "game/menu.hpp" @@ -36,26 +36,25 @@ #include "game/state/options-menu.hpp" #include "game/strings.hpp" #include "game/world.hpp" -#include "math/glsl.hpp" -#include "math/projection.hpp" -#include "physics/light/exposure.hpp" -#include "render/model.hpp" -#include "render/passes/clear-pass.hpp" -#include "render/passes/ground-pass.hpp" -#include "render/passes/sky-pass.hpp" -#include "resources/resource-manager.hpp" -#include "utility/hash/fnv1a.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include using namespace hash::literals; using namespace math::glsl; -namespace game { namespace state { -main_menu::main_menu(game::context& ctx, bool fade_in): - game::state::base(ctx) +main_menu::main_menu(::context& ctx, bool fade_in): + ::state::base(ctx) { debug::log::trace("Entering main menu state..."); @@ -111,19 +110,19 @@ main_menu::main_menu(game::context& ctx, bool fade_in): quit_text->set_content(get_string(ctx, "main_menu_quit"_fnv1a32)); // Init menu item index - game::menu::init_menu_item_index(ctx, "main"); + ::menu::init_menu_item_index(ctx, "main"); - game::menu::update_text_color(ctx); - game::menu::update_text_font(ctx); - game::menu::align_text(ctx, true, false, (-viewport_size.y() / 3.0f) / 2.0f); - game::menu::update_text_tweens(ctx); - game::menu::add_text_to_ui(ctx); - game::menu::setup_animations(ctx); + ::menu::update_text_color(ctx); + ::menu::update_text_font(ctx); + ::menu::align_text(ctx, true, false, (-viewport_size.y() / 3.0f) / 2.0f); + ::menu::update_text_tweens(ctx); + ::menu::add_text_to_ui(ctx); + ::menu::setup_animations(ctx); auto select_start_callback = [this, &ctx]() { // Disable menu controls - ctx.function_queue.push(std::bind(game::disable_menu_controls, std::ref(ctx))); + ctx.function_queue.push(std::bind(::disable_menu_controls, std::ref(ctx))); // Create change state function auto change_state = [&ctx]() @@ -134,9 +133,9 @@ main_menu::main_menu(game::context& ctx, bool fade_in): [&ctx]() { ctx.state_machine.pop(); - ctx.state_machine.emplace(new game::state::nuptial_flight(ctx)); - //ctx.state_machine.emplace(new game::state::collection_menu(ctx)); - //ctx.state_machine.emplace(new game::state::nest_selection(ctx)); + ctx.state_machine.emplace(new ::state::nuptial_flight(ctx)); + //ctx.state_machine.emplace(new ::state::collection_menu(ctx)); + //ctx.state_machine.emplace(new ::state::nest_selection(ctx)); } ); }; @@ -145,7 +144,7 @@ main_menu::main_menu(game::context& ctx, bool fade_in): this->fade_out_title(); // Fade out menu - game::menu::fade_out(ctx, nullptr); + ::menu::fade_out(ctx, nullptr); // Start fade out to white //ctx.fade_transition_color->set_value({1, 1, 1}); @@ -155,13 +154,13 @@ main_menu::main_menu(game::context& ctx, bool fade_in): auto select_options_callback = [this, &ctx]() { // Disable menu controls - ctx.function_queue.push(std::bind(game::disable_menu_controls, std::ref(ctx))); + ctx.function_queue.push(std::bind(::disable_menu_controls, std::ref(ctx))); // Fade out title this->fade_out_title(); // Fade out menu - game::menu::fade_out + ::menu::fade_out ( ctx, [&ctx]() @@ -172,7 +171,7 @@ main_menu::main_menu(game::context& ctx, bool fade_in): [&ctx]() { ctx.state_machine.pop(); - ctx.state_machine.emplace(new game::state::options_menu(ctx)); + ctx.state_machine.emplace(new ::state::options_menu(ctx)); } ); } @@ -181,13 +180,13 @@ main_menu::main_menu(game::context& ctx, bool fade_in): auto select_extras_callback = [this, &ctx]() { // Disable menu controls - ctx.function_queue.push(std::bind(game::disable_menu_controls, std::ref(ctx))); + ctx.function_queue.push(std::bind(::disable_menu_controls, std::ref(ctx))); // Fade out title this->fade_out_title(); // Fade out menu - game::menu::fade_out + ::menu::fade_out ( ctx, [&ctx]() @@ -198,7 +197,7 @@ main_menu::main_menu(game::context& ctx, bool fade_in): [&ctx]() { ctx.state_machine.pop(); - ctx.state_machine.emplace(new game::state::extras_menu(ctx)); + ctx.state_machine.emplace(new ::state::extras_menu(ctx)); } ); } @@ -207,13 +206,13 @@ main_menu::main_menu(game::context& ctx, bool fade_in): auto select_quit_callback = [this, &ctx]() { // Disable menu controls - ctx.function_queue.push(std::bind(game::disable_menu_controls, std::ref(ctx))); + ctx.function_queue.push(std::bind(::disable_menu_controls, std::ref(ctx))); // Fade out title this->fade_out_title(); // Fade out menu - game::menu::fade_out(ctx, nullptr); + ::menu::fade_out(ctx, nullptr); // Fade to black then quit ctx.fade_transition->transition(config::quit_fade_out_duration, false, ease::out_cubic, false, [&ctx](){ctx.closed=true;}); @@ -252,21 +251,21 @@ main_menu::main_menu(game::context& ctx, bool fade_in): { // Fade in text fade_in_title(); - game::menu::fade_in(ctx, nullptr); + ::menu::fade_in(ctx, nullptr); } if (ctx.entities.find("earth") == ctx.entities.end()) { - game::world::cosmogenesis(ctx); - game::world::create_observer(ctx); - //game::world::enter_ecoregion(ctx, *ctx.resource_manager->load("seedy-scrub.eco")); + ::world::cosmogenesis(ctx); + ::world::create_observer(ctx); + //::world::enter_ecoregion(ctx, *ctx.resource_manager->load<::ecoregion>("seedy-scrub.eco")); } // Set world time - game::world::set_time(ctx, 2022, 6, 21, 12, 0, 0.0); + ::world::set_time(ctx, 2022, 6, 21, 12, 0, 0.0); // Set world time scale - game::world::set_time_scale(ctx, 0.0); + ::world::set_time_scale(ctx, 0.0); ctx.surface_camera->set_active(true); const float ev100_sunny16 = physics::light::ev::from_settings(16.0f, 1.0f / 100.0f, 100.0f); @@ -286,9 +285,6 @@ main_menu::main_menu(game::context& ctx, bool fade_in): // Disable UI color clear ctx.ui_clear_pass->set_cleared_buffers(false, true, false); - //if (!ctx.menu_bg_billboard->is_active()) - // game::menu::fade_in_bg(ctx); - // Setup window resized callback window_resized_subscription = ctx.window->get_resized_channel().subscribe ( @@ -304,12 +300,12 @@ main_menu::main_menu(game::context& ctx, bool fade_in): title_text.set_translation({std::round(viewport_center.x() - title_w * 0.5f), std::round(viewport_center.y() - title_h * 0.5f + (viewport_size.y() / 3.0f) / 2.0f), 0.0f}); title_text.update_tweens(); - game::menu::align_text(ctx, true, false, (-viewport_size.y() / 3.0f) / 2.0f); + ::menu::align_text(ctx, true, false, (-viewport_size.y() / 3.0f) / 2.0f); } ); // Enable menu controls - ctx.function_queue.push(std::bind(game::enable_menu_controls, std::ref(ctx))); + ctx.function_queue.push(std::bind(::enable_menu_controls, std::ref(ctx))); debug::log::trace("Entered main menu state"); } @@ -319,11 +315,11 @@ main_menu::~main_menu() debug::log::trace("Exiting main menu state..."); // Destruct menu - game::disable_menu_controls(ctx); - game::menu::clear_callbacks(ctx); - game::menu::delete_animations(ctx); - game::menu::remove_text_from_ui(ctx); - game::menu::delete_text(ctx); + ::disable_menu_controls(ctx); + ::menu::clear_callbacks(ctx); + ::menu::delete_animations(ctx); + ::menu::remove_text_from_ui(ctx); + ::menu::delete_text(ctx); // Hide menu BG ctx.menu_bg_billboard->set_active(false); @@ -358,4 +354,3 @@ void main_menu::fade_out_title() } } // namespace state -} // namespace game diff --git a/src/game/state/main-menu.hpp b/src/game/state/main-menu.hpp index 3b01d4d..adc58b1 100644 --- a/src/game/state/main-menu.hpp +++ b/src/game/state/main-menu.hpp @@ -21,19 +21,18 @@ #define ANTKEEPER_GAME_STATE_MAIN_MENU_HPP #include "game/state/base.hpp" -#include "scene/text.hpp" -#include "animation/animation.hpp" -#include "entity/id.hpp" -#include "event/subscription.hpp" +#include +#include +#include +#include #include -namespace game { namespace state { -class main_menu: public game::state::base +class main_menu: public ::state::base { public: - main_menu(game::context& ctx, bool fade_in); + main_menu(::context& ctx, bool fade_in); virtual ~main_menu(); private: @@ -47,6 +46,5 @@ private: }; } // namespace state -} // namespace game #endif // ANTKEEPER_GAME_STATE_MAIN_MENU_HPP diff --git a/src/game/state/nest-selection.cpp b/src/game/state/nest-selection.cpp index d702944..54f1652 100644 --- a/src/game/state/nest-selection.cpp +++ b/src/game/state/nest-selection.cpp @@ -17,54 +17,74 @@ * along with Antkeeper source code. If not, see . */ -#include "game/state/nest-selection.hpp" -#include "game/state/pause-menu.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/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" -#include "resources/resource-manager.hpp" -#include "game/world.hpp" -#include "render/passes/clear-pass.hpp" -#include "render/passes/ground-pass.hpp" -#include "utility/state-machine.hpp" -#include "config.hpp" -#include "math/interpolation.hpp" -#include "physics/light/exposure.hpp" -#include "input/mouse.hpp" -#include "math/projection.hpp" - +#include "game/ant/cladogenesis.hpp" +#include "game/ant/genome.hpp" #include "game/ant/morphogenesis.hpp" #include "game/ant/phenome.hpp" -#include "game/ant/genome.hpp" -#include "game/ant/cladogenesis.hpp" +#include "game/commands/commands.hpp" +#include "game/components/camera-component.hpp" +#include "game/components/constraint-stack-component.hpp" +#include "game/components/locomotion-component.hpp" +#include "game/components/model-component.hpp" +#include "game/components/picking-component.hpp" +#include "game/components/spring-component.hpp" +#include "game/components/steering-component.hpp" +#include "game/components/terrain-component.hpp" +#include "game/components/transform-component.hpp" +#include "game/constraints/child-of-constraint.hpp" +#include "game/constraints/copy-rotation-constraint.hpp" +#include "game/constraints/copy-scale-constraint.hpp" +#include "game/constraints/copy-transform-constraint.hpp" +#include "game/constraints/copy-translation-constraint.hpp" +#include "game/constraints/ease-to-constraint.hpp" +#include "game/constraints/pivot-constraint.hpp" +#include "game/constraints/spring-rotation-constraint.hpp" +#include "game/constraints/spring-to-constraint.hpp" +#include "game/constraints/spring-translation-constraint.hpp" +#include "game/constraints/three-dof-constraint.hpp" +#include "game/constraints/track-to-constraint.hpp" +#include "game/controls.hpp" #include "game/spawn.hpp" +#include "game/state/nest-selection.hpp" +#include "game/state/pause-menu.hpp" +#include "game/systems/astronomy-system.hpp" +#include "game/systems/atmosphere-system.hpp" +#include "game/systems/camera-system.hpp" +#include "game/systems/collision-system.hpp" +#include "game/world.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include -using namespace game::ant; +using namespace ::ant; -namespace game { namespace state { -nest_selection::nest_selection(game::context& ctx): - game::state::base(ctx) +nest_selection::nest_selection(::context& ctx): + ::state::base(ctx) { debug::log::trace("Entering nest selection state..."); + // Create world if not yet created + if (ctx.entities.find("earth") == ctx.entities.end()) + { + // Create cosmos + ::world::cosmogenesis(ctx); + + // Create observer + ::world::create_observer(ctx); + } + ::world::enter_ecoregion(ctx, *ctx.resource_manager->load<::ecoregion>("seedy-scrub.eco")); + debug::log::trace("Generating genome..."); std::random_device rng; ant::genome* genome = ant::cladogenesis(ctx.active_ecoregion->gene_pools[0], rng); @@ -80,37 +100,30 @@ nest_selection::nest_selection(game::context& ctx): // Create worker entity(s) entity::id worker_eid = ctx.entity_registry->create(); - component::transform worker_transform_component; + transform_component worker_transform_component; worker_transform_component.local = math::transform::identity; worker_transform_component.local.translation = {0, 0, -20}; worker_transform_component.world = worker_transform_component.local; worker_transform_component.warp = true; - ctx.entity_registry->emplace(worker_eid, worker_transform_component); + ctx.entity_registry->emplace(worker_eid, worker_transform_component); - component::model worker_model_component; + model_component worker_model_component; worker_model_component.render_model = worker_model; worker_model_component.instance_count = 0; worker_model_component.layers = ~0; - ctx.entity_registry->emplace(worker_eid, worker_model_component); + ctx.entity_registry->emplace(worker_eid, worker_model_component); // Disable UI color clear ctx.ui_clear_pass->set_cleared_buffers(false, true, false); - // Create world if not yet created - if (ctx.entities.find("earth") == ctx.entities.end()) - { - // Create cosmos - game::world::cosmogenesis(ctx); - - // Create observer - game::world::create_observer(ctx); - } + // Set world time + ::world::set_time(ctx, 2022, 6, 21, 12, 0, 0.0); // Init time scale double time_scale = 60.0; // Set time scale - game::world::set_time_scale(ctx, time_scale); + ::world::set_time_scale(ctx, time_scale); // Setup and enable sky and ground passes ctx.sky_pass->set_enabled(true); @@ -152,34 +165,44 @@ nest_selection::nest_selection(game::context& ctx): // ruler_archetype->create(*ctx.entity_registry); auto yucca_archetype = ctx.resource_manager->load("yucca-plant-l.ent"); auto yucca_eid = yucca_archetype->create(*ctx.entity_registry); - entity::command::warp_to(*ctx.entity_registry, yucca_eid, {0, 4, 30}); + ::command::warp_to(*ctx.entity_registry, yucca_eid, {0, 4, 30}); yucca_archetype = ctx.resource_manager->load("yucca-plant-m.ent"); yucca_eid = yucca_archetype->create(*ctx.entity_registry); - entity::command::warp_to(*ctx.entity_registry, yucca_eid, {400, 0, 200}); + ::command::warp_to(*ctx.entity_registry, yucca_eid, {400, 0, 200}); yucca_archetype = ctx.resource_manager->load("yucca-plant-s.ent"); yucca_eid = yucca_archetype->create(*ctx.entity_registry); - entity::command::warp_to(*ctx.entity_registry, yucca_eid, {-300, 3, -300}); + ::command::warp_to(*ctx.entity_registry, yucca_eid, {-300, 3, -300}); auto cactus_plant_archetype = ctx.resource_manager->load("barrel-cactus-plant-l.ent"); auto cactus_plant_eid = cactus_plant_archetype->create(*ctx.entity_registry); - entity::command::warp_to(*ctx.entity_registry, cactus_plant_eid, {-100, 0, -200}); + ::command::warp_to(*ctx.entity_registry, cactus_plant_eid, {-100, 0, -200}); cactus_plant_archetype = ctx.resource_manager->load("barrel-cactus-plant-m.ent"); cactus_plant_eid = cactus_plant_archetype->create(*ctx.entity_registry); - entity::command::warp_to(*ctx.entity_registry, cactus_plant_eid, {100, -2, -70}); + ::command::warp_to(*ctx.entity_registry, cactus_plant_eid, {100, -2, -70}); cactus_plant_archetype = ctx.resource_manager->load("barrel-cactus-plant-s.ent"); cactus_plant_eid = cactus_plant_archetype->create(*ctx.entity_registry); - entity::command::warp_to(*ctx.entity_registry, cactus_plant_eid, {50, 2, 80}); + ::command::warp_to(*ctx.entity_registry, cactus_plant_eid, {50, 2, 80}); auto cactus_seed_archetype = ctx.resource_manager->load("barrel-cactus-seed.ent"); auto cactus_seed_eid = cactus_seed_archetype->create(*ctx.entity_registry); - entity::command::warp_to(*ctx.entity_registry, cactus_seed_eid, {10, 5, 10}); + ::command::warp_to(*ctx.entity_registry, cactus_seed_eid, {10, 5, 10}); + + // Queue enable game controls + ctx.function_queue.push + ( + [&ctx]() + { + ::enable_game_controls(ctx); + } + ); - // Queue control setup - ctx.function_queue.push(std::bind(&nest_selection::enable_controls, this)); + // Queue fade in + ctx.fade_transition_color->set_value({0, 0, 0}); + ctx.function_queue.push(std::bind(&screen_transition::transition, ctx.fade_transition, 1.0f, true, ease::out_sine, true, nullptr)); debug::log::trace("Entered nest selection state"); } @@ -188,6 +211,9 @@ nest_selection::~nest_selection() { debug::log::trace("Exiting nest selection state..."); + // Disable game controls + ::disable_game_controls(ctx); + destroy_first_person_camera_rig(); debug::log::trace("Exited nest selection state"); @@ -196,7 +222,7 @@ nest_selection::~nest_selection() 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; + spring_rotation_constraint first_person_camera_rig_spring_rotation; first_person_camera_rig_spring_rotation.spring = { {0.0f, 0.0f, 0.0f}, @@ -205,16 +231,16 @@ void nest_selection::create_first_person_camera_rig() 1.0f, first_person_camera_rig_rotation_spring_angular_frequency }; - component::constraint_stack_node first_person_camera_rig_spring_rotation_node; + constraint_stack_node_component first_person_camera_rig_spring_rotation_node; first_person_camera_rig_spring_rotation_node.active = true; first_person_camera_rig_spring_rotation_node.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); + 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; + spring_translation_constraint first_person_camera_rig_spring_translation; first_person_camera_rig_spring_translation.spring = { {0.0f, 0.0f, 0.0f}, @@ -223,37 +249,37 @@ void nest_selection::create_first_person_camera_rig() 1.0f, first_person_camera_rig_translation_spring_angular_frequency }; - component::constraint_stack_node first_person_camera_rig_spring_translation_node; + constraint_stack_node_component first_person_camera_rig_spring_translation_node; first_person_camera_rig_spring_translation_node.active = true; first_person_camera_rig_spring_translation_node.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); + 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; + constraint_stack_component 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; + transform_component 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; + camera_component 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); + 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; + spring1_component first_person_camera_rig_fov_spring; first_person_camera_rig_fov_spring.spring = { 0.0f, @@ -269,7 +295,7 @@ void nest_selection::create_first_person_camera_rig() // 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); + 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); } @@ -288,7 +314,7 @@ void nest_selection::set_first_person_camera_rig_pedestal(float 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); - ctx.entity_registry->patch + ctx.entity_registry->patch ( first_person_camera_rig_spring_translation_eid, [&](auto& component) @@ -297,7 +323,7 @@ void nest_selection::set_first_person_camera_rig_pedestal(float pedestal) } ); - ctx.entity_registry->patch + ctx.entity_registry->patch ( first_person_camera_rig_fov_spring_eid, [&](auto& component) @@ -311,13 +337,13 @@ void nest_selection::move_first_person_camera_rig(const float2& direction, float { 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 spring_rotation_constraint& first_person_camera_rig_spring_rotation = ctx.entity_registry->get(first_person_camera_rig_spring_rotation_eid); const math::quaternion yaw_rotation = math::angle_axis(first_person_camera_rig_spring_rotation.spring.x0[0], float3{0.0f, 1.0f, 0.0f}); const float3 rotated_direction = math::normalize(yaw_rotation * float3{direction[0], 0.0f, direction[1]}); const float3 velocity = rotated_direction * speed; - ctx.entity_registry->patch + ctx.entity_registry->patch ( first_person_camera_rig_spring_translation_eid, [&](auto& component) @@ -330,7 +356,7 @@ void nest_selection::move_first_person_camera_rig(const float2& direction, float void nest_selection::satisfy_first_person_camera_rig_constraints() { // Satisfy first person camera rig spring translation constraint - ctx.entity_registry->patch + ctx.entity_registry->patch ( first_person_camera_rig_spring_translation_eid, [&](auto& component) @@ -341,7 +367,7 @@ void nest_selection::satisfy_first_person_camera_rig_constraints() ); // Satisfy first person camera rig spring rotation constraint - ctx.entity_registry->patch + ctx.entity_registry->patch ( first_person_camera_rig_spring_rotation_eid, [&](auto& component) @@ -352,7 +378,7 @@ void nest_selection::satisfy_first_person_camera_rig_constraints() ); // Satisfy first person camera rig fov spring - ctx.entity_registry->patch + ctx.entity_registry->patch ( first_person_camera_rig_fov_spring_eid, [&](auto& component) @@ -442,7 +468,7 @@ void nest_selection::enable_controls() if (!mouse_look) return; - ctx.entity_registry->patch + ctx.entity_registry->patch ( first_person_camera_rig_spring_rotation_eid, [&, mouse_pan_factor](auto& component) @@ -456,7 +482,7 @@ void nest_selection::enable_controls() ( [&, gamepad_pan_factor](float value) { - ctx.entity_registry->patch + ctx.entity_registry->patch ( first_person_camera_rig_spring_rotation_eid, [&, gamepad_pan_factor](auto& component) @@ -475,7 +501,7 @@ void nest_selection::enable_controls() if (!mouse_look) return; - ctx.entity_registry->patch + ctx.entity_registry->patch ( first_person_camera_rig_spring_rotation_eid, [&, mouse_pan_factor](auto& component) @@ -489,7 +515,7 @@ void nest_selection::enable_controls() ( [&, gamepad_pan_factor](float value) { - ctx.entity_registry->patch + ctx.entity_registry->patch ( first_person_camera_rig_spring_rotation_eid, [&, gamepad_pan_factor](auto& component) @@ -508,7 +534,7 @@ void nest_selection::enable_controls() if (!mouse_look) return; - ctx.entity_registry->patch + ctx.entity_registry->patch ( first_person_camera_rig_spring_rotation_eid, [&, mouse_tilt_factor](auto& component) @@ -523,7 +549,7 @@ void nest_selection::enable_controls() ( [&, gamepad_tilt_factor](float value) { - ctx.entity_registry->patch + ctx.entity_registry->patch ( first_person_camera_rig_spring_rotation_eid, [&, gamepad_tilt_factor](auto& component) @@ -543,7 +569,7 @@ void nest_selection::enable_controls() if (!mouse_look) return; - ctx.entity_registry->patch + ctx.entity_registry->patch ( first_person_camera_rig_spring_rotation_eid, [&, mouse_tilt_factor](auto& component) @@ -558,7 +584,7 @@ void nest_selection::enable_controls() ( [&, gamepad_tilt_factor](float value) { - ctx.entity_registry->patch + ctx.entity_registry->patch ( first_person_camera_rig_spring_rotation_eid, [&, gamepad_tilt_factor](auto& component) @@ -647,28 +673,28 @@ void nest_selection::enable_controls() ( [&ctx = this->ctx, ff_time_scale]() { - game::world::set_time_scale(ctx, ff_time_scale); + ::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); + ::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); + ::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); + ::world::set_time_scale(ctx, time_scale); } ); @@ -688,7 +714,7 @@ void nest_selection::enable_controls() }; // Push pause menu state - ctx.state_machine.emplace(new game::state::pause_menu(ctx)); + ctx.state_machine.emplace(new ::state::pause_menu(ctx)); } ); @@ -752,4 +778,3 @@ void nest_selection::disable_controls() } } // namespace state -} // namespace game diff --git a/src/game/state/nest-selection.hpp b/src/game/state/nest-selection.hpp index abbd5f4..5387d86 100644 --- a/src/game/state/nest-selection.hpp +++ b/src/game/state/nest-selection.hpp @@ -21,16 +21,15 @@ #define ANTKEEPER_GAME_STATE_NEST_SELECTION_HPP #include "game/state/base.hpp" -#include "entity/id.hpp" -#include "utility/fundamental-types.hpp" +#include +#include -namespace game { namespace state { -class nest_selection: public game::state::base +class nest_selection: public ::state::base { public: - nest_selection(game::context& ctx); + nest_selection(::context& ctx); virtual ~nest_selection(); private: @@ -61,6 +60,5 @@ private: }; } // namespace state -} // namespace game #endif // ANTKEEPER_GAME_STATE_NEST_SELECTION_HPP diff --git a/src/game/state/nuptial-flight.cpp b/src/game/state/nuptial-flight.cpp index e439716..2a6cceb 100644 --- a/src/game/state/nuptial-flight.cpp +++ b/src/game/state/nuptial-flight.cpp @@ -21,42 +21,57 @@ #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" -#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/constraint.hpp" -#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 +#include "game/systems/camera-system.hpp" +#include "game/systems/astronomy-system.hpp" +#include "game/systems/atmosphere-system.hpp" +#include "game/systems/collision-system.hpp" +#include "game/components/caste-component.hpp" +#include "game/components/locomotion-component.hpp" +#include "game/components/transform-component.hpp" +#include "game/components/terrain-component.hpp" +#include "game/components/camera-component.hpp" +#include "game/components/model-component.hpp" +#include "game/components/constraint-stack-component.hpp" +#include "game/components/steering-component.hpp" +#include "game/components/picking-component.hpp" +#include "game/components/spring-component.hpp" +#include "game/constraints/child-of-constraint.hpp" +#include "game/constraints/copy-rotation-constraint.hpp" +#include "game/constraints/copy-scale-constraint.hpp" +#include "game/constraints/copy-transform-constraint.hpp" +#include "game/constraints/copy-translation-constraint.hpp" +#include "game/constraints/ease-to-constraint.hpp" +#include "game/constraints/pivot-constraint.hpp" +#include "game/constraints/spring-rotation-constraint.hpp" +#include "game/constraints/spring-to-constraint.hpp" +#include "game/constraints/spring-translation-constraint.hpp" +#include "game/constraints/three-dof-constraint.hpp" +#include "game/constraints/track-to-constraint.hpp" +#include #include "game/controls.hpp" -#include "entity/commands.hpp" -#include "animation/screen-transition.hpp" -#include "animation/ease.hpp" -#include "resources/resource-manager.hpp" +#include "game/commands/commands.hpp" +#include +#include +#include #include "game/world.hpp" -#include "render/passes/clear-pass.hpp" -#include "render/passes/ground-pass.hpp" -#include "utility/state-machine.hpp" -#include "config.hpp" -#include "math/interpolation.hpp" -#include "physics/light/exposure.hpp" -#include "color/color.hpp" -#include "input/mouse.hpp" +#include "game/strings.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace hash::literals; -namespace game { namespace state { -nuptial_flight::nuptial_flight(game::context& ctx): - game::state::base(ctx) +nuptial_flight::nuptial_flight(::context& ctx): + ::state::base(ctx) { debug::log::trace("Entering nuptial flight state..."); @@ -71,25 +86,25 @@ nuptial_flight::nuptial_flight(game::context& ctx): if (ctx.entities.find("earth") == ctx.entities.end()) { // Create cosmos - game::world::cosmogenesis(ctx); + ::world::cosmogenesis(ctx); // Create observer - game::world::create_observer(ctx); + ::world::create_observer(ctx); } - game::world::enter_ecoregion(ctx, *ctx.resource_manager->load("seedy-scrub.eco")); + ::world::enter_ecoregion(ctx, *ctx.resource_manager->load<::ecoregion>("seedy-scrub.eco")); // Set world time - game::world::set_time(ctx, 2022, 6, 21, 12, 0, 0.0); + ::world::set_time(ctx, 2022, 6, 21, 12, 0, 0.0); // Set world time scale - game::world::set_time_scale(ctx, 0.0); + ::world::set_time_scale(ctx, 0.0); // Setup and enable sky and ground passes ctx.sky_pass->set_enabled(true); ctx.ground_pass->set_enabled(true); // Create mating swarm - swarm_eid = game::ant::create_swarm(ctx); + swarm_eid = ::ant::create_swarm(ctx); // Switch to surface camera ctx.underground_camera->set_active(false); @@ -117,7 +132,7 @@ nuptial_flight::nuptial_flight(game::context& ctx): create_camera_rig(); // Select random alate - ctx.entity_registry->view().each + ctx.entity_registry->view().each ( [&](entity::id alate_eid, auto& transform, auto& steering) { @@ -128,19 +143,39 @@ nuptial_flight::nuptial_flight(game::context& ctx): // Satisfy camera rig constraints satisfy_camera_rig_constraints(); - // Queue fade in - ctx.fade_transition_color->set_value({0, 0, 0}); - ctx.function_queue.push(std::bind(&screen_transition::transition, ctx.fade_transition, 1.0f, true, ease::out_sine, true, nullptr)); - - // Queue enable game controls + // Construct selection text + selection_text.set_material(&ctx.menu_font_material); + selection_text.set_color({1.0f, 1.0f, 1.0f, 1.0f}); + selection_text.set_font(&ctx.menu_font); + //selection_text.set_content(get_string(ctx, "title_antkeeper"_fnv1a32)); + selection_text.set_content("Hello, World!"); + const auto& text_aabb = static_cast&>(selection_text.get_local_bounds()); + float text_w = text_aabb.max_point.x() - text_aabb.min_point.x(); + float text_h = text_aabb.max_point.y() - text_aabb.min_point.y(); + selection_text.set_translation({std::round(viewport_size.x() * 0.5f - text_w * 0.5f), std::round(0.0f), 0.0f}); + selection_text.update_tweens(); + + // Add text to UI + ctx.ui_scene->add_object(&selection_text); + + // Setup controls + setup_controls(); + + // Queue enable controls ctx.function_queue.push ( [&ctx]() { - game::enable_game_controls(ctx); + + ::enable_nuptial_flight_controls(ctx); + ::enable_game_controls(ctx); } ); + // Queue fade in + ctx.fade_transition_color->set_value({0, 0, 0}); + ctx.function_queue.push(std::bind(&screen_transition::transition, ctx.fade_transition, 1.0f, true, ease::out_sine, true, nullptr)); + debug::log::trace("Entered nuptial flight state"); } @@ -149,13 +184,18 @@ nuptial_flight::~nuptial_flight() debug::log::trace("Exiting nuptial flight state..."); // Disable game controls - game::disable_game_controls(ctx); + ::disable_nuptial_flight_controls(ctx); + ::disable_game_controls(ctx); + + // Remove text from UI + ctx.ui_scene->remove_object(&selection_text); // Deselect selected entity select_entity(entt::null); + destroy_camera_rig(); - game::ant::destroy_swarm(ctx, swarm_eid); + ::ant::destroy_swarm(ctx, swarm_eid); debug::log::trace("Exited nuptial flight state"); } @@ -163,50 +203,50 @@ nuptial_flight::~nuptial_flight() void nuptial_flight::create_camera_rig() { // Construct camera rig focus ease to constraint - component::constraint::ease_to camera_rig_focus_ease_to; + ease_to_constraint 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 = 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; + constraint_stack_node_component camera_rig_focus_ease_to_node; camera_rig_focus_ease_to_node.active = true; camera_rig_focus_ease_to_node.weight = 1.0f; camera_rig_focus_ease_to_node.next = entt::null; camera_rig_focus_ease_to_eid = ctx.entity_registry->create(); - ctx.entity_registry->emplace(camera_rig_focus_ease_to_eid, camera_rig_focus_ease_to); - ctx.entity_registry->emplace(camera_rig_focus_ease_to_eid, camera_rig_focus_ease_to_node); + ctx.entity_registry->emplace(camera_rig_focus_ease_to_eid, camera_rig_focus_ease_to); + ctx.entity_registry->emplace(camera_rig_focus_ease_to_eid, camera_rig_focus_ease_to_node); // Construct camera rig focus constraint stack - component::constraint_stack camera_rig_focus_constraint_stack; + constraint_stack_component camera_rig_focus_constraint_stack; camera_rig_focus_constraint_stack.priority = 1; camera_rig_focus_constraint_stack.head = camera_rig_focus_ease_to_eid; // Construct camera rig focus transform component - component::transform camera_rig_focus_transform; + transform_component camera_rig_focus_transform; camera_rig_focus_transform.local = math::transform::identity; camera_rig_focus_transform.world = camera_rig_focus_transform.local; camera_rig_focus_transform.warp = true; // Construct camera rig focus entity camera_rig_focus_eid = ctx.entity_registry->create(); - ctx.entity_registry->emplace(camera_rig_focus_eid, camera_rig_focus_transform); - ctx.entity_registry->emplace(camera_rig_focus_eid, camera_rig_focus_constraint_stack); + ctx.entity_registry->emplace(camera_rig_focus_eid, camera_rig_focus_transform); + ctx.entity_registry->emplace(camera_rig_focus_eid, camera_rig_focus_constraint_stack); // Construct camera rig pivot constraint - component::constraint::pivot camera_rig_pivot; + pivot_constraint camera_rig_pivot; camera_rig_pivot.target = camera_rig_focus_eid; camera_rig_pivot.offset = {0, 0, 0}; - component::constraint_stack_node camera_rig_pivot_node; + constraint_stack_node_component camera_rig_pivot_node; camera_rig_pivot_node.active = true; camera_rig_pivot_node.weight = 1.0f; camera_rig_pivot_node.next = entt::null; camera_rig_pivot_eid = ctx.entity_registry->create(); - ctx.entity_registry->emplace(camera_rig_pivot_eid, camera_rig_pivot); - ctx.entity_registry->emplace(camera_rig_pivot_eid, camera_rig_pivot_node); + ctx.entity_registry->emplace(camera_rig_pivot_eid, camera_rig_pivot); + ctx.entity_registry->emplace(camera_rig_pivot_eid, camera_rig_pivot_node); // Construct camera rig copy translation constraint - component::constraint::copy_translation camera_rig_copy_translation; + copy_translation_constraint camera_rig_copy_translation; camera_rig_copy_translation.target = camera_rig_focus_eid; camera_rig_copy_translation.copy_x = true; camera_rig_copy_translation.copy_y = true; @@ -215,16 +255,16 @@ void nuptial_flight::create_camera_rig() camera_rig_copy_translation.invert_y = false; camera_rig_copy_translation.invert_z = false; camera_rig_copy_translation.offset = true; - component::constraint_stack_node camera_rig_copy_translation_node; + constraint_stack_node_component camera_rig_copy_translation_node; camera_rig_copy_translation_node.active = true; camera_rig_copy_translation_node.weight = 1.0f; camera_rig_copy_translation_node.next = camera_rig_pivot_eid; camera_rig_copy_translation_eid = ctx.entity_registry->create(); - ctx.entity_registry->emplace(camera_rig_copy_translation_eid, camera_rig_copy_translation); - ctx.entity_registry->emplace(camera_rig_copy_translation_eid, camera_rig_copy_translation_node); + ctx.entity_registry->emplace(camera_rig_copy_translation_eid, camera_rig_copy_translation); + ctx.entity_registry->emplace(camera_rig_copy_translation_eid, camera_rig_copy_translation_node); // Construct camera rig spring rotation constraint - component::constraint::spring_rotation camera_rig_spring_rotation; + spring_rotation_constraint camera_rig_spring_rotation; camera_rig_spring_rotation.spring = { {0.0f, 0.0f, 0.0f}, @@ -233,16 +273,16 @@ void nuptial_flight::create_camera_rig() 1.0f, camera_rig_rotation_spring_angular_frequency }; - component::constraint_stack_node camera_rig_spring_rotation_node; + constraint_stack_node_component camera_rig_spring_rotation_node; camera_rig_spring_rotation_node.active = true; camera_rig_spring_rotation_node.weight = 1.0f; camera_rig_spring_rotation_node.next = camera_rig_copy_translation_eid; camera_rig_spring_rotation_eid = ctx.entity_registry->create(); - ctx.entity_registry->emplace(camera_rig_spring_rotation_eid, camera_rig_spring_rotation); - ctx.entity_registry->emplace(camera_rig_spring_rotation_eid, camera_rig_spring_rotation_node); + ctx.entity_registry->emplace(camera_rig_spring_rotation_eid, camera_rig_spring_rotation); + ctx.entity_registry->emplace(camera_rig_spring_rotation_eid, camera_rig_spring_rotation_node); // Construct camera rig spring translation constraint - component::constraint::spring_translation camera_rig_spring_translation; + spring_translation_constraint camera_rig_spring_translation; camera_rig_spring_translation.spring = { {0.0f, 0.0f, 0.0f}, @@ -251,37 +291,37 @@ void nuptial_flight::create_camera_rig() 1.0f, camera_rig_translation_spring_angular_frequency }; - component::constraint_stack_node camera_rig_spring_translation_node; + constraint_stack_node_component camera_rig_spring_translation_node; camera_rig_spring_translation_node.active = true; camera_rig_spring_translation_node.weight = 1.0f; camera_rig_spring_translation_node.next = camera_rig_spring_rotation_eid; camera_rig_spring_translation_eid = ctx.entity_registry->create(); - ctx.entity_registry->emplace(camera_rig_spring_translation_eid, camera_rig_spring_translation); - ctx.entity_registry->emplace(camera_rig_spring_translation_eid, camera_rig_spring_translation_node); + ctx.entity_registry->emplace(camera_rig_spring_translation_eid, camera_rig_spring_translation); + ctx.entity_registry->emplace(camera_rig_spring_translation_eid, camera_rig_spring_translation_node); // Construct camera rig constraint stack - component::constraint_stack camera_rig_constraint_stack; + constraint_stack_component camera_rig_constraint_stack; camera_rig_constraint_stack.priority = 2; camera_rig_constraint_stack.head = camera_rig_spring_translation_eid; // Construct camera rig transform component - component::transform camera_rig_transform; + transform_component camera_rig_transform; camera_rig_transform.local = math::transform::identity; camera_rig_transform.world = camera_rig_transform.local; camera_rig_transform.warp = true; // Construct camera rig camera component - component::camera camera_rig_camera; + camera_component camera_rig_camera; camera_rig_camera.object = ctx.surface_camera; // Construct camera rig entity camera_rig_eid = ctx.entity_registry->create(); - ctx.entity_registry->emplace(camera_rig_eid, camera_rig_camera); - ctx.entity_registry->emplace(camera_rig_eid, camera_rig_transform); - ctx.entity_registry->emplace(camera_rig_eid, camera_rig_constraint_stack); + ctx.entity_registry->emplace(camera_rig_eid, camera_rig_camera); + ctx.entity_registry->emplace(camera_rig_eid, camera_rig_transform); + ctx.entity_registry->emplace(camera_rig_eid, camera_rig_constraint_stack); // Construct camera rig fov spring - component::spring1 camera_rig_fov_spring; + spring1_component camera_rig_fov_spring; camera_rig_fov_spring.spring = { 0.0f, @@ -297,7 +337,7 @@ void nuptial_flight::create_camera_rig() // 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); + ctx.entity_registry->emplace(camera_rig_fov_spring_eid, camera_rig_fov_spring); set_camera_rig_zoom(0.25f); } @@ -321,7 +361,7 @@ void nuptial_flight::set_camera_rig_zoom(float zoom) camera_rig_zoom = zoom; const float distance = math::log_lerp(camera_rig_far_distance, camera_rig_near_distance, camera_rig_zoom); - ctx.entity_registry->patch + ctx.entity_registry->patch ( camera_rig_spring_translation_eid, [&](auto& component) @@ -331,7 +371,7 @@ void nuptial_flight::set_camera_rig_zoom(float zoom) ); const float fov = math::log_lerp(camera_rig_far_fov, camera_rig_near_fov, camera_rig_zoom); - ctx.entity_registry->patch + ctx.entity_registry->patch ( camera_rig_fov_spring_eid, [&](auto& component) @@ -344,7 +384,7 @@ void nuptial_flight::set_camera_rig_zoom(float zoom) void nuptial_flight::satisfy_camera_rig_constraints() { // Satisfy camera rig focus ease to constraint - ctx.entity_registry->patch + ctx.entity_registry->patch ( camera_rig_focus_ease_to_eid, [&](auto& component) @@ -354,7 +394,7 @@ void nuptial_flight::satisfy_camera_rig_constraints() ); // Satisfy camera rig spring translation constraint - ctx.entity_registry->patch + ctx.entity_registry->patch ( camera_rig_spring_translation_eid, [&](auto& component) @@ -365,7 +405,7 @@ void nuptial_flight::satisfy_camera_rig_constraints() ); // Satisfy camera rig spring rotation constraint - ctx.entity_registry->patch + ctx.entity_registry->patch ( camera_rig_spring_rotation_eid, [&](auto& component) @@ -376,7 +416,7 @@ void nuptial_flight::satisfy_camera_rig_constraints() ); // Satisfycamera rig fov spring - ctx.entity_registry->patch + ctx.entity_registry->patch ( camera_rig_fov_spring_eid, [&](auto& component) @@ -387,6 +427,41 @@ void nuptial_flight::satisfy_camera_rig_constraints() ); } +void nuptial_flight::setup_controls() +{ + action_subscriptions.emplace_back + ( + ctx.pick_mate_action.get_activated_channel().subscribe + ( + [&](const auto& event) + { + // Get window-space mouse coordinates + const auto& mouse_position = (*ctx.input_manager->get_mice().begin())->get_position(); + + // Get window viewport size + const auto& viewport_size = ctx.window->get_viewport_size(); + + // Transform mouse coordinates from window space to NDC space + const float2 mouse_ndc = + { + static_cast(mouse_position.x()) / static_cast(viewport_size.x() - 1) * 2.0f - 1.0f, + (1.0f - static_cast(mouse_position.y()) / static_cast(viewport_size.y() - 1)) * 2.0f - 1.0f + }; + + // Get picking ray from camera + const geom::primitive::ray ray = ctx.surface_camera->pick(mouse_ndc); + + // Pick entity + entity::id picked_eid = ctx.collision_system->pick_nearest(ray, ~selected_picking_flag); + if (picked_eid != entt::null) + { + select_entity(picked_eid); + } + } + ) + ); +} + void nuptial_flight::enable_controls() { /* @@ -466,7 +541,7 @@ void nuptial_flight::enable_controls() if (!mouse_look) return; - ctx.entity_registry->patch + ctx.entity_registry->patch ( camera_rig_spring_rotation_eid, [&, mouse_pan_factor](auto& component) @@ -480,7 +555,7 @@ void nuptial_flight::enable_controls() ( [&, gamepad_pan_factor](float value) { - ctx.entity_registry->patch + ctx.entity_registry->patch ( camera_rig_spring_rotation_eid, [&, gamepad_pan_factor](auto& component) @@ -499,7 +574,7 @@ void nuptial_flight::enable_controls() if (!mouse_look) return; - ctx.entity_registry->patch + ctx.entity_registry->patch ( camera_rig_spring_rotation_eid, [&, mouse_pan_factor](auto& component) @@ -513,7 +588,7 @@ void nuptial_flight::enable_controls() ( [&, gamepad_pan_factor](float value) { - ctx.entity_registry->patch + ctx.entity_registry->patch ( camera_rig_spring_rotation_eid, [&, gamepad_pan_factor](auto& component) @@ -532,7 +607,7 @@ void nuptial_flight::enable_controls() if (!mouse_look) return; - ctx.entity_registry->patch + ctx.entity_registry->patch ( camera_rig_spring_rotation_eid, [&, mouse_tilt_factor](auto& component) @@ -547,7 +622,7 @@ void nuptial_flight::enable_controls() ( [&, gamepad_tilt_factor](float value) { - ctx.entity_registry->patch + ctx.entity_registry->patch ( camera_rig_spring_rotation_eid, [&, gamepad_tilt_factor](auto& component) @@ -567,7 +642,7 @@ void nuptial_flight::enable_controls() if (!mouse_look) return; - ctx.entity_registry->patch + ctx.entity_registry->patch ( camera_rig_spring_rotation_eid, [&, mouse_tilt_factor](auto& component) @@ -582,7 +657,7 @@ void nuptial_flight::enable_controls() ( [&, gamepad_tilt_factor](float value) { - ctx.entity_registry->patch + ctx.entity_registry->patch ( camera_rig_spring_rotation_eid, [&, gamepad_tilt_factor](auto& component) @@ -688,7 +763,7 @@ void nuptial_flight::enable_controls() // Change to nest selection state ctx.state_machine.pop(); - ctx.state_machine.emplace(new game::state::nest_selection(ctx)); + ctx.state_machine.emplace(new ::state::nest_selection(ctx)); } ); @@ -697,28 +772,28 @@ void nuptial_flight::enable_controls() ( [&ctx = this->ctx, ff_time_scale]() { - game::world::set_time_scale(ctx, ff_time_scale); + ::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); + ::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); + ::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); + ::world::set_time_scale(ctx, time_scale); } ); @@ -738,7 +813,7 @@ void nuptial_flight::enable_controls() }; // Push pause menu state - ctx.state_machine.emplace(new game::state::pause_menu(ctx)); + ctx.state_machine.emplace(new ::state::pause_menu(ctx)); } ); @@ -805,10 +880,10 @@ void nuptial_flight::select_entity(entity::id entity_id) { if (entity_id != selected_eid) { - if (ctx.entity_registry->valid(selected_eid) && ctx.entity_registry->all_of(selected_eid)) + if (ctx.entity_registry->valid(selected_eid) && ctx.entity_registry->all_of(selected_eid)) { // Unset selected bit on picking flags of previously selected entity - ctx.entity_registry->patch + ctx.entity_registry->patch ( selected_eid, [&](auto& component) @@ -821,10 +896,10 @@ void nuptial_flight::select_entity(entity::id entity_id) selected_eid = entity_id; - if (ctx.entity_registry->valid(selected_eid) && ctx.entity_registry->all_of(selected_eid)) + if (ctx.entity_registry->valid(selected_eid) && ctx.entity_registry->all_of(selected_eid)) { // Set selected bit on picking flags of current selected entity - ctx.entity_registry->patch + ctx.entity_registry->patch ( selected_eid, [&](auto& component) @@ -835,7 +910,7 @@ void nuptial_flight::select_entity(entity::id entity_id) } // Update camera rig focus ease to target - ctx.entity_registry->patch + ctx.entity_registry->patch ( camera_rig_focus_ease_to_eid, [&](auto& component) @@ -843,11 +918,47 @@ void nuptial_flight::select_entity(entity::id entity_id) component.target = selected_eid; component.t = 0.0f; - const component::transform* transform = ctx.entity_registry->try_get(camera_rig_focus_eid); + const transform_component* transform = ctx.entity_registry->try_get(camera_rig_focus_eid); if (transform) component.start = transform->world.translation; } ); + + // Update selection text + if (ctx.entity_registry->valid(selected_eid) && ctx.entity_registry->all_of<::caste_component>(selected_eid)) + { + std::string format_string = "{}"; + + const auto& caste = ctx.entity_registry->get<::caste_component>(selected_eid); + switch (caste.type) + { + case ::ant::caste::queen: + format_string = ::get_string(ctx, "ant_label_numbered_queen_format"_fnv1a32); + break; + + case ::ant::caste::worker: + format_string = ::get_string(ctx, "ant_label_numbered_worker_format"_fnv1a32); + break; + + case ::ant::caste::soldier: + format_string = ::get_string(ctx, "ant_label_numbered_soldier_format"_fnv1a32); + break; + + case ::ant::caste::male: + format_string = ::get_string(ctx, "ant_label_numbered_male_format"_fnv1a32); + break; + + default: + //std::unreachable(); + break; + } + + selection_text.set_content(std::vformat(format_string, std::make_format_args(std::to_underlying(selected_eid)))); + } + else + { + + } } } @@ -856,7 +967,7 @@ void nuptial_flight::select_nearest_entity(const float3& direction) if (!ctx.entity_registry->valid(selected_eid)) return; - const component::transform* selected_eid_transform = ctx.entity_registry->try_get(selected_eid); + const transform_component* selected_eid_transform = ctx.entity_registry->try_get(selected_eid); if (!selected_eid_transform) return; @@ -873,4 +984,3 @@ void nuptial_flight::select_nearest_entity(const float3& direction) } } // namespace state -} // namespace game diff --git a/src/game/state/nuptial-flight.hpp b/src/game/state/nuptial-flight.hpp index 5f7f14e..4a08507 100644 --- a/src/game/state/nuptial-flight.hpp +++ b/src/game/state/nuptial-flight.hpp @@ -21,16 +21,18 @@ #define ANTKEEPER_GAME_STATE_NUPTIAL_FLIGHT_HPP #include "game/state/base.hpp" -#include "entity/id.hpp" -#include "utility/fundamental-types.hpp" +#include +#include +#include +#include +#include -namespace game { namespace state { -class nuptial_flight: public game::state::base +class nuptial_flight: public ::state::base { public: - nuptial_flight(game::context& ctx); + nuptial_flight(::context& ctx); virtual ~nuptial_flight(); private: @@ -40,15 +42,16 @@ private: void set_camera_rig_zoom(float zoom); void satisfy_camera_rig_constraints(); + void setup_controls(); void enable_controls(); void disable_controls(); void select_entity(entity::id entity_id); void select_nearest_entity(const float3& direction); + // Camera entity::id camera_rig_focus_eid; entity::id camera_rig_focus_ease_to_eid; - entity::id camera_rig_eid; entity::id camera_rig_spring_translation_eid; entity::id camera_rig_spring_rotation_eid; @@ -60,24 +63,27 @@ private: 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; - + // Ants entity::id swarm_eid; + // Picking std::uint32_t selected_picking_flag; entity::id selected_eid; + // UI + scene::text selection_text; + + // Controls bool mouse_look; + std::vector> action_subscriptions; }; } // namespace state -} // namespace game #endif // ANTKEEPER_GAME_STATE_NUPTIAL_FLIGHT_HPP diff --git a/src/game/state/options-menu.cpp b/src/game/state/options-menu.cpp index 0293087..d39a347 100644 --- a/src/game/state/options-menu.cpp +++ b/src/game/state/options-menu.cpp @@ -26,21 +26,20 @@ #include "game/state/pause-menu.hpp" #include "game/menu.hpp" #include "game/controls.hpp" -#include "animation/ease.hpp" -#include "animation/animation.hpp" -#include "animation/animator.hpp" -#include "scene/text.hpp" -#include "debug/log.hpp" +#include +#include +#include +#include +#include #include "game/strings.hpp" -#include "utility/hash/fnv1a.hpp" +#include using namespace hash::literals; -namespace game { namespace state { -options_menu::options_menu(game::context& ctx): - game::state::base(ctx) +options_menu::options_menu(::context& ctx): + ::state::base(ctx) { debug::log::trace("Entering options menu state..."); @@ -66,23 +65,23 @@ options_menu::options_menu(game::context& ctx): ctx.menu_item_texts.push_back({back_text, nullptr}); // Init menu item index - game::menu::init_menu_item_index(ctx, "options"); + ::menu::init_menu_item_index(ctx, "options"); - game::menu::update_text_color(ctx); - game::menu::update_text_font(ctx); - game::menu::align_text(ctx, true); - game::menu::update_text_tweens(ctx); - game::menu::add_text_to_ui(ctx); - game::menu::setup_animations(ctx); + ::menu::update_text_color(ctx); + ::menu::update_text_font(ctx); + ::menu::align_text(ctx, true); + ::menu::update_text_tweens(ctx); + ::menu::add_text_to_ui(ctx); + ::menu::setup_animations(ctx); // Construct menu item callbacks auto select_controls_callback = [&ctx]() { // Disable menu controls - ctx.function_queue.push(std::bind(game::disable_menu_controls, std::ref(ctx))); + ctx.function_queue.push(std::bind(::disable_menu_controls, std::ref(ctx))); // Return to main menu - game::menu::fade_out + ::menu::fade_out ( ctx, [&ctx]() @@ -93,7 +92,7 @@ options_menu::options_menu(game::context& ctx): [&ctx]() { ctx.state_machine.pop(); - ctx.state_machine.emplace(new game::state::controls_menu(ctx)); + ctx.state_machine.emplace(new ::state::controls_menu(ctx)); } ); } @@ -102,10 +101,10 @@ options_menu::options_menu(game::context& ctx): auto select_graphics_callback = [&ctx]() { // Disable menu controls - ctx.function_queue.push(std::bind(game::disable_menu_controls, std::ref(ctx))); + ctx.function_queue.push(std::bind(::disable_menu_controls, std::ref(ctx))); // Return to main menu - game::menu::fade_out + ::menu::fade_out ( ctx, [&ctx]() @@ -116,7 +115,7 @@ options_menu::options_menu(game::context& ctx): [&ctx]() { ctx.state_machine.pop(); - ctx.state_machine.emplace(new game::state::graphics_menu(ctx)); + ctx.state_machine.emplace(new ::state::graphics_menu(ctx)); } ); } @@ -125,10 +124,10 @@ options_menu::options_menu(game::context& ctx): auto select_sound_callback = [&ctx]() { // Disable menu controls - ctx.function_queue.push(std::bind(game::disable_menu_controls, std::ref(ctx))); + ctx.function_queue.push(std::bind(::disable_menu_controls, std::ref(ctx))); // Return to main menu - game::menu::fade_out + ::menu::fade_out ( ctx, [&ctx]() @@ -139,7 +138,7 @@ options_menu::options_menu(game::context& ctx): [&ctx]() { ctx.state_machine.pop(); - ctx.state_machine.emplace(new game::state::sound_menu(ctx)); + ctx.state_machine.emplace(new ::state::sound_menu(ctx)); } ); } @@ -148,10 +147,10 @@ options_menu::options_menu(game::context& ctx): auto select_language_callback = [&ctx]() { // Disable menu controls - ctx.function_queue.push(std::bind(game::disable_menu_controls, std::ref(ctx))); + ctx.function_queue.push(std::bind(::disable_menu_controls, std::ref(ctx))); // Return to main menu - game::menu::fade_out + ::menu::fade_out ( ctx, [&ctx]() @@ -162,7 +161,7 @@ options_menu::options_menu(game::context& ctx): [&ctx]() { ctx.state_machine.pop(); - ctx.state_machine.emplace(new game::state::language_menu(ctx)); + ctx.state_machine.emplace(new ::state::language_menu(ctx)); } ); } @@ -171,12 +170,12 @@ options_menu::options_menu(game::context& ctx): auto select_back_callback = [&ctx]() { // Disable menu controls - ctx.function_queue.push(std::bind(game::disable_menu_controls, std::ref(ctx))); + ctx.function_queue.push(std::bind(::disable_menu_controls, std::ref(ctx))); // Save config - //game::save::config(ctx); + //::save::config(ctx); - game::menu::fade_out + ::menu::fade_out ( ctx, [&ctx]() @@ -188,9 +187,9 @@ options_menu::options_menu(game::context& ctx): { ctx.state_machine.pop(); if (ctx.resume_callback) - ctx.state_machine.emplace(new game::state::pause_menu(ctx)); + ctx.state_machine.emplace(new ::state::pause_menu(ctx)); else - ctx.state_machine.emplace(new game::state::main_menu(ctx, false)); + ctx.state_machine.emplace(new ::state::main_menu(ctx, false)); } ); } @@ -214,10 +213,10 @@ options_menu::options_menu(game::context& ctx): ctx.menu_back_callback = select_back_callback; // Fade in menu - game::menu::fade_in(ctx, nullptr); + ::menu::fade_in(ctx, nullptr); // Queue enable menu controls - ctx.function_queue.push(std::bind(game::enable_menu_controls, std::ref(ctx))); + ctx.function_queue.push(std::bind(::enable_menu_controls, std::ref(ctx))); debug::log::trace("Entered options menu state"); } @@ -227,14 +226,13 @@ options_menu::~options_menu() debug::log::trace("Exiting options menu state..."); // Destruct menu - game::disable_menu_controls(ctx); - game::menu::clear_callbacks(ctx); - game::menu::delete_animations(ctx); - game::menu::remove_text_from_ui(ctx); - game::menu::delete_text(ctx); + ::disable_menu_controls(ctx); + ::menu::clear_callbacks(ctx); + ::menu::delete_animations(ctx); + ::menu::remove_text_from_ui(ctx); + ::menu::delete_text(ctx); debug::log::trace("Exited options menu state"); } } // namespace state -} // namespace game diff --git a/src/game/state/options-menu.hpp b/src/game/state/options-menu.hpp index 9f4a736..fa5d0ff 100644 --- a/src/game/state/options-menu.hpp +++ b/src/game/state/options-menu.hpp @@ -22,17 +22,15 @@ #include "game/state/base.hpp" -namespace game { namespace state { -class options_menu: public game::state::base +class options_menu: public ::state::base { public: - options_menu(game::context& ctx); + options_menu(::context& ctx); virtual ~options_menu(); }; } // namespace state -} // namespace game #endif // ANTKEEPER_GAME_STATE_OPTIONS_MENU_HPP diff --git a/src/game/state/pause-menu.cpp b/src/game/state/pause-menu.cpp index 94e97c9..d55306e 100644 --- a/src/game/state/pause-menu.cpp +++ b/src/game/state/pause-menu.cpp @@ -23,23 +23,22 @@ #include "game/state/nuptial-flight.hpp" #include "game/menu.hpp" #include "game/controls.hpp" -#include "animation/ease.hpp" -#include "animation/animation.hpp" -#include "animation/animator.hpp" -#include "scene/text.hpp" -#include "debug/log.hpp" -#include "animation/screen-transition.hpp" -#include "config.hpp" +#include +#include +#include +#include +#include +#include +#include #include "game/strings.hpp" -#include "utility/hash/fnv1a.hpp" +#include using namespace hash::literals; -namespace game { namespace state { -pause_menu::pause_menu(game::context& ctx): - game::state::base(ctx) +pause_menu::pause_menu(::context& ctx): + ::state::base(ctx) { debug::log::trace("Entering pause menu state..."); @@ -62,14 +61,14 @@ pause_menu::pause_menu(game::context& ctx): ctx.menu_item_texts.push_back({quit_text, nullptr}); // Init menu item index - game::menu::init_menu_item_index(ctx, "pause"); + ::menu::init_menu_item_index(ctx, "pause"); - game::menu::update_text_color(ctx); - game::menu::update_text_font(ctx); - game::menu::align_text(ctx, true, false); - game::menu::update_text_tweens(ctx); - game::menu::add_text_to_ui(ctx); - game::menu::setup_animations(ctx); + ::menu::update_text_color(ctx); + ::menu::update_text_font(ctx); + ::menu::align_text(ctx, true, false); + ::menu::update_text_tweens(ctx); + ::menu::add_text_to_ui(ctx); + ::menu::setup_animations(ctx); // Construct menu item callbacks auto select_resume_callback = [&ctx]() @@ -78,7 +77,7 @@ pause_menu::pause_menu(game::context& ctx): //ctx.controls["pause"]->set_activated_callback(nullptr); // Disable menu controls - ctx.function_queue.push(std::bind(game::disable_menu_controls, std::ref(ctx))); + ctx.function_queue.push(std::bind(::disable_menu_controls, std::ref(ctx))); auto resume_paused_state = [&ctx]() { @@ -93,8 +92,8 @@ pause_menu::pause_menu(game::context& ctx): }; // Fade out pause menu then resume paused state - game::menu::fade_out(ctx, resume_paused_state); - game::menu::fade_out_bg(ctx); + ::menu::fade_out(ctx, resume_paused_state); + ::menu::fade_out_bg(ctx); }; auto select_options_callback = [&ctx]() { @@ -102,10 +101,10 @@ pause_menu::pause_menu(game::context& ctx): //ctx.controls["pause"]->set_activated_callback(nullptr); // Disable menu controls - ctx.function_queue.push(std::bind(game::disable_menu_controls, std::ref(ctx))); + ctx.function_queue.push(std::bind(::disable_menu_controls, std::ref(ctx))); // Fade out pause menu then open options menu - game::menu::fade_out + ::menu::fade_out ( ctx, [&ctx]() @@ -116,7 +115,7 @@ pause_menu::pause_menu(game::context& ctx): [&ctx]() { ctx.state_machine.pop(); - ctx.state_machine.emplace(new game::state::options_menu(ctx)); + ctx.state_machine.emplace(new ::state::options_menu(ctx)); } ); } @@ -128,7 +127,7 @@ pause_menu::pause_menu(game::context& ctx): //ctx.controls["pause"]->set_activated_callback(nullptr); // Disable menu controls - ctx.function_queue.push(std::bind(game::disable_menu_controls, std::ref(ctx))); + ctx.function_queue.push(std::bind(::disable_menu_controls, std::ref(ctx))); // Clear resume callback ctx.resume_callback = nullptr; @@ -143,13 +142,13 @@ pause_menu::pause_menu(game::context& ctx): ctx.menu_bg_billboard->set_active(false); ctx.state_machine.pop(); ctx.state_machine.pop(); - ctx.state_machine.emplace(new game::state::main_menu(ctx, true)); + ctx.state_machine.emplace(new ::state::main_menu(ctx, true)); } ); }; // Fade out pause menu - game::menu::fade_out(ctx, nullptr); + ::menu::fade_out(ctx, nullptr); // Fade out to black then return to main menu ctx.fade_transition_color->set_value({0, 0, 0}); @@ -161,13 +160,13 @@ pause_menu::pause_menu(game::context& ctx): //ctx.controls["pause"]->set_activated_callback(nullptr); // Disable menu controls - ctx.function_queue.push(std::bind(game::disable_menu_controls, std::ref(ctx))); + ctx.function_queue.push(std::bind(::disable_menu_controls, std::ref(ctx))); // Clear paused state //ctx.paused_state.reset(); // Fade out pause menu - game::menu::fade_out(ctx, nullptr); + ::menu::fade_out(ctx, nullptr); // Fade out to black then quit ctx.fade_transition_color->set_value({0, 0, 0}); @@ -198,17 +197,17 @@ pause_menu::pause_menu(game::context& ctx): //ctx.controls["pause"]->set_activated_callback(select_resume_callback); // Enable menu controls - game::enable_menu_controls(ctx); + ::enable_menu_controls(ctx); } ); // Fade in menu and menu BG - game::menu::fade_in(ctx, nullptr); + ::menu::fade_in(ctx, nullptr); if (!ctx.menu_bg_billboard->is_active()) - game::menu::fade_in_bg(ctx); + ::menu::fade_in_bg(ctx); // Save colony - //game::save::colony(ctx); + //::save::colony(ctx); debug::log::trace("Entered pause menu state"); } @@ -218,14 +217,13 @@ pause_menu::~pause_menu() debug::log::trace("Exiting pause menu state..."); // Destruct menu - game::disable_menu_controls(ctx); - game::menu::clear_callbacks(ctx); - game::menu::delete_animations(ctx); - game::menu::remove_text_from_ui(ctx); - game::menu::delete_text(ctx); + ::disable_menu_controls(ctx); + ::menu::clear_callbacks(ctx); + ::menu::delete_animations(ctx); + ::menu::remove_text_from_ui(ctx); + ::menu::delete_text(ctx); debug::log::trace("Exited pause menu state"); } } // namespace state -} // namespace game diff --git a/src/game/state/pause-menu.hpp b/src/game/state/pause-menu.hpp index 67c66d0..50ad923 100644 --- a/src/game/state/pause-menu.hpp +++ b/src/game/state/pause-menu.hpp @@ -22,17 +22,15 @@ #include "game/state/base.hpp" -namespace game { namespace state { -class pause_menu: public game::state::base +class pause_menu: public ::state::base { public: - pause_menu(game::context& ctx); + pause_menu(::context& ctx); virtual ~pause_menu(); }; } // namespace state -} // namespace game #endif // ANTKEEPER_GAME_STATE_PAUSE_MENU_HPP diff --git a/src/game/state/sound-menu.cpp b/src/game/state/sound-menu.cpp index c0e913f..3b17869 100644 --- a/src/game/state/sound-menu.cpp +++ b/src/game/state/sound-menu.cpp @@ -20,19 +20,18 @@ #include "game/state/sound-menu.hpp" #include "game/state/options-menu.hpp" #include "game/controls.hpp" -#include "scene/text.hpp" -#include "debug/log.hpp" +#include +#include #include "game/menu.hpp" #include "game/strings.hpp" -#include "utility/hash/fnv1a.hpp" +#include using namespace hash::literals; -namespace game { namespace state { -sound_menu::sound_menu(game::context& ctx): - game::state::base(ctx) +sound_menu::sound_menu(::context& ctx): + ::state::base(ctx) { debug::log::trace("Entering sound menu state..."); @@ -71,14 +70,14 @@ sound_menu::sound_menu(game::context& ctx): update_value_text_content(); // Init menu item index - game::menu::init_menu_item_index(ctx, "sound"); + ::menu::init_menu_item_index(ctx, "sound"); - game::menu::update_text_color(ctx); - game::menu::update_text_font(ctx); - game::menu::align_text(ctx); - game::menu::update_text_tweens(ctx); - game::menu::add_text_to_ui(ctx); - game::menu::setup_animations(ctx); + ::menu::update_text_color(ctx); + ::menu::update_text_font(ctx); + ::menu::align_text(ctx); + ::menu::update_text_tweens(ctx); + ::menu::add_text_to_ui(ctx); + ::menu::setup_animations(ctx); // Construct menu item callbacks auto increase_volume_callback = [this, &ctx](float* volume) @@ -94,8 +93,8 @@ sound_menu::sound_menu(game::context& ctx): *volume = 1.0f; this->update_value_text_content(); - game::menu::align_text(ctx); - game::menu::update_text_tweens(ctx); + ::menu::align_text(ctx); + ::menu::update_text_tweens(ctx); }; auto decrease_volume_callback = [this, &ctx](float* volume) { @@ -110,8 +109,8 @@ sound_menu::sound_menu(game::context& ctx): *volume = 0.0f; this->update_value_text_content(); - game::menu::align_text(ctx); - game::menu::update_text_tweens(ctx); + ::menu::align_text(ctx); + ::menu::update_text_tweens(ctx); }; auto toggle_mono_audio_callback = [this, &ctx]() @@ -119,8 +118,8 @@ sound_menu::sound_menu(game::context& ctx): ctx.mono_audio = !ctx.mono_audio; this->update_value_text_content(); - game::menu::align_text(ctx); - game::menu::update_text_tweens(ctx); + ::menu::align_text(ctx); + ::menu::update_text_tweens(ctx); }; auto toggle_captions_callback = [this, &ctx]() @@ -128,8 +127,8 @@ sound_menu::sound_menu(game::context& ctx): ctx.captions = !ctx.captions; this->update_value_text_content(); - game::menu::align_text(ctx); - game::menu::update_text_tweens(ctx); + ::menu::align_text(ctx); + ::menu::update_text_tweens(ctx); }; auto increase_captions_size_callback = [this, &ctx]() @@ -145,8 +144,8 @@ sound_menu::sound_menu(game::context& ctx): ctx.captions_size = 2.0f; this->update_value_text_content(); - game::menu::align_text(ctx); - game::menu::update_text_tweens(ctx); + ::menu::align_text(ctx); + ::menu::update_text_tweens(ctx); }; auto decrease_captions_size_callback = [this, &ctx]() @@ -162,15 +161,15 @@ sound_menu::sound_menu(game::context& ctx): ctx.captions_size = 0.1f; this->update_value_text_content(); - game::menu::align_text(ctx); - game::menu::update_text_tweens(ctx); + ::menu::align_text(ctx); + ::menu::update_text_tweens(ctx); }; auto select_back_callback = [&ctx]() { // Disable menu controls - ctx.function_queue.push(std::bind(game::disable_menu_controls, std::ref(ctx))); + ctx.function_queue.push(std::bind(::disable_menu_controls, std::ref(ctx))); - game::menu::fade_out + ::menu::fade_out ( ctx, [&ctx]() @@ -181,7 +180,7 @@ sound_menu::sound_menu(game::context& ctx): [&ctx]() { ctx.state_machine.pop(); - ctx.state_machine.emplace(new game::state::options_menu(ctx)); + ctx.state_machine.emplace(new ::state::options_menu(ctx)); } ); } @@ -219,10 +218,10 @@ sound_menu::sound_menu(game::context& ctx): ctx.menu_back_callback = select_back_callback; // Queue menu control setup - ctx.function_queue.push(std::bind(game::enable_menu_controls, std::ref(ctx))); + ctx.function_queue.push(std::bind(::enable_menu_controls, std::ref(ctx))); // Fade in menu - game::menu::fade_in(ctx, nullptr); + ::menu::fade_in(ctx, nullptr); debug::log::trace("Entered sound menu state"); } @@ -232,11 +231,11 @@ sound_menu::~sound_menu() debug::log::trace("Exiting sound menu state..."); // Destruct menu - game::disable_menu_controls(ctx); - game::menu::clear_callbacks(ctx); - game::menu::delete_animations(ctx); - game::menu::remove_text_from_ui(ctx); - game::menu::delete_text(ctx); + ::disable_menu_controls(ctx); + ::menu::clear_callbacks(ctx); + ::menu::delete_animations(ctx); + ::menu::remove_text_from_ui(ctx); + ::menu::delete_text(ctx); debug::log::trace("Exited sound menu state"); } @@ -255,4 +254,3 @@ void sound_menu::update_value_text_content() } } // namespace state -} // namespace game diff --git a/src/game/state/sound-menu.hpp b/src/game/state/sound-menu.hpp index c5b81e2..010e677 100644 --- a/src/game/state/sound-menu.hpp +++ b/src/game/state/sound-menu.hpp @@ -22,13 +22,12 @@ #include "game/state/base.hpp" -namespace game { namespace state { -class sound_menu: public game::state::base +class sound_menu: public ::state::base { public: - sound_menu(game::context& ctx); + sound_menu(::context& ctx); virtual ~sound_menu(); private: @@ -36,6 +35,5 @@ private: }; } // namespace state -} // namespace game #endif // ANTKEEPER_GAME_STATE_SOUND_MENU_HPP diff --git a/src/game/state/splash.cpp b/src/game/state/splash.cpp index 571eb8f..9717d9b 100644 --- a/src/game/state/splash.cpp +++ b/src/game/state/splash.cpp @@ -1,223 +1,221 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "game/state/splash.hpp" -#include "animation/animation.hpp" -#include "animation/animator.hpp" -#include "animation/ease.hpp" -#include "animation/screen-transition.hpp" -#include "debug/log.hpp" -#include "game/context.hpp" -#include "game/state/main-menu.hpp" -#include "math/linear-algebra.hpp" -#include "render/material-flags.hpp" -#include "render/passes/clear-pass.hpp" -#include "resources/resource-manager.hpp" -#include "math/glsl.hpp" - -using namespace math::glsl; - -namespace game { -namespace state { - -splash::splash(game::context& ctx): - game::state::base(ctx) -{ - debug::log::trace("Entering splash state..."); - - const vec2 viewport_size = vec2(ctx.window->get_viewport_size()); - const vec2 viewport_center = viewport_size * 0.5f; - - // Enable color buffer clearing in UI pass - ctx.ui_clear_pass->set_cleared_buffers(true, true, false); - - // Load splash texture - const gl::texture_2d* splash_texture = ctx.resource_manager->load("splash.tex"); - - // Get splash texture dimensions - auto splash_dimensions = splash_texture->get_dimensions(); - - // Construct splash billboard material - splash_billboard_material.set_blend_mode(render::blend_mode::translucent); - splash_billboard_material.set_shader_program(ctx.resource_manager->load("ui-element-textured.glsl")); - splash_billboard_material.add_property("background")->set_value(splash_texture); - render::material_property* splash_tint = splash_billboard_material.add_property("tint"); - splash_tint->set_value(float4{1, 1, 1, 0}); - splash_billboard_material.update_tweens(); - - // Construct splash billboard - splash_billboard.set_material(&splash_billboard_material); - splash_billboard.set_scale({(float)std::get<0>(splash_dimensions) * 0.5f, (float)std::get<1>(splash_dimensions) * 0.5f, 1.0f}); - splash_billboard.set_translation({std::round(viewport_center.x()), std::round(viewport_center.y()), 0.0f}); - splash_billboard.update_tweens(); - - // Add splash billboard to UI scene - ctx.ui_scene->add_object(&splash_billboard); - - // Load animation timing configuration - const double splash_fade_in_duration = 0.5; - const double splash_duration = 2.0; - const double splash_fade_out_duration = 0.5; - - // Construct splash fade in animation - splash_fade_in_animation.set_interpolator(ease::out_cubic); - animation_channel* splash_fade_in_opacity_channel = splash_fade_in_animation.add_channel(0); - splash_fade_in_opacity_channel->insert_keyframe({0.0, 0.0f}); - splash_fade_in_opacity_channel->insert_keyframe({splash_fade_in_duration, 1.0f}); - splash_fade_in_opacity_channel->insert_keyframe({splash_fade_in_duration + splash_duration, 1.0f}); - - // Build splash fade out animation - splash_fade_out_animation.set_interpolator(ease::out_cubic); - animation_channel* splash_fade_out_opacity_channel = splash_fade_out_animation.add_channel(0); - splash_fade_out_opacity_channel->insert_keyframe({0.0, 1.0f}); - splash_fade_out_opacity_channel->insert_keyframe({splash_fade_out_duration, 0.0f}); - - // Setup animation frame callbacks - auto set_splash_opacity = [splash_tint](int channel, const float& opacity) - { - splash_tint->set_value(float4{1, 1, 1, opacity}); - }; - splash_fade_in_animation.set_frame_callback(set_splash_opacity); - splash_fade_out_animation.set_frame_callback(set_splash_opacity); - - // Trigger splash fade out animation when splash fade in animation ends - splash_fade_in_animation.set_end_callback - ( - [this]() - { - this->splash_fade_out_animation.play(); - } - ); - - // Trigger a state change when the splash fade out animation ends - splash_fade_out_animation.set_end_callback - ( - [&ctx]() - { - // Queue change to main menu state - ctx.function_queue.push - ( - [&ctx]() - { - ctx.state_machine.pop(); - ctx.state_machine.emplace(new game::state::main_menu(ctx, true)); - } - ); - } - ); - - // Add splash fade animations to animator - ctx.animator->add_animation(&splash_fade_in_animation); - ctx.animator->add_animation(&splash_fade_out_animation); - - // Start splash fade in animation - splash_fade_in_animation.play(); - - // Setup window resized callback - window_resized_subscription = ctx.window->get_resized_channel().subscribe - ( - [&](const auto& event) - { - const vec2 viewport_size = vec2(event.window->get_viewport_size()); - const vec2 viewport_center = viewport_size * 0.5f; - splash_billboard.set_translation({std::round(viewport_center.x()), std::round(viewport_center.y()), 0.0f}); - splash_billboard.update_tweens(); - } - ); - - // Construct splash skip function - auto skip = [&](const auto& event) - { - ctx.function_queue.emplace - ( - [&]() - { - // Black out screen - ctx.window->get_rasterizer()->set_clear_color(0.0f, 0.0f, 0.0f, 1.0f); - ctx.window->get_rasterizer()->clear_framebuffer(true, false, false); - ctx.window->swap_buffers(); - - // Change to main menu state - ctx.state_machine.pop(); - ctx.state_machine.emplace(new game::state::main_menu(ctx, true)); - } - ); - }; - - // Set up splash skippers - input_mapped_subscriptions.emplace_back - ( - ctx.input_mapper.get_gamepad_button_mapped_channel().subscribe - ( - skip - ) - ); - input_mapped_subscriptions.emplace_back - ( - ctx.input_mapper.get_key_mapped_channel().subscribe - ( - skip - ) - ); - input_mapped_subscriptions.emplace_back - ( - ctx.input_mapper.get_mouse_button_mapped_channel().subscribe - ( - skip - ) - ); - - // Enable splash skippers next frame - ctx.function_queue.push - ( - [&]() - { - ctx.input_mapper.connect(ctx.input_manager->get_event_queue()); - } - ); - - debug::log::trace("Entered splash state"); -} - -splash::~splash() -{ - debug::log::trace("Exiting splash state..."); - - // Disable splash skippers - ctx.input_mapper.disconnect(); - input_mapped_subscriptions.clear(); - - // Remove splash fade animations from animator - ctx.animator->remove_animation(&splash_fade_in_animation); - ctx.animator->remove_animation(&splash_fade_out_animation); - - // Remove splash billboard from UI scene - ctx.ui_scene->remove_object(&splash_billboard); - - // Unload splash texture - ctx.resource_manager->unload("splash.tex"); - - // Disable color buffer clearing in UI pass - ctx.ui_clear_pass->set_cleared_buffers(false, true, false); - - debug::log::trace("Exited splash state"); -} - -} // namespace state -} // namespace game +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include "game/state/splash.hpp" +#include +#include +#include +#include +#include +#include "game/context.hpp" +#include "game/state/main-menu.hpp" +#include +#include +#include +#include +#include + +using namespace math::glsl; + +namespace state { + +splash::splash(::context& ctx): + ::state::base(ctx) +{ + debug::log::trace("Entering splash state..."); + + const vec2 viewport_size = vec2(ctx.window->get_viewport_size()); + const vec2 viewport_center = viewport_size * 0.5f; + + // Enable color buffer clearing in UI pass + ctx.ui_clear_pass->set_cleared_buffers(true, true, false); + + // Load splash texture + const gl::texture_2d* splash_texture = ctx.resource_manager->load("splash.tex"); + + // Get splash texture dimensions + auto splash_dimensions = splash_texture->get_dimensions(); + + // Construct splash billboard material + splash_billboard_material.set_blend_mode(render::blend_mode::translucent); + splash_billboard_material.set_shader_program(ctx.resource_manager->load("ui-element-textured.glsl")); + splash_billboard_material.add_property("background")->set_value(splash_texture); + render::material_property* splash_tint = splash_billboard_material.add_property("tint"); + splash_tint->set_value(float4{1, 1, 1, 0}); + splash_billboard_material.update_tweens(); + + // Construct splash billboard + splash_billboard.set_material(&splash_billboard_material); + splash_billboard.set_scale({(float)std::get<0>(splash_dimensions) * 0.5f, (float)std::get<1>(splash_dimensions) * 0.5f, 1.0f}); + splash_billboard.set_translation({std::round(viewport_center.x()), std::round(viewport_center.y()), 0.0f}); + splash_billboard.update_tweens(); + + // Add splash billboard to UI scene + ctx.ui_scene->add_object(&splash_billboard); + + // Load animation timing configuration + const double splash_fade_in_duration = 0.5; + const double splash_duration = 2.0; + const double splash_fade_out_duration = 0.5; + + // Construct splash fade in animation + splash_fade_in_animation.set_interpolator(ease::out_cubic); + animation_channel* splash_fade_in_opacity_channel = splash_fade_in_animation.add_channel(0); + splash_fade_in_opacity_channel->insert_keyframe({0.0, 0.0f}); + splash_fade_in_opacity_channel->insert_keyframe({splash_fade_in_duration, 1.0f}); + splash_fade_in_opacity_channel->insert_keyframe({splash_fade_in_duration + splash_duration, 1.0f}); + + // Build splash fade out animation + splash_fade_out_animation.set_interpolator(ease::out_cubic); + animation_channel* splash_fade_out_opacity_channel = splash_fade_out_animation.add_channel(0); + splash_fade_out_opacity_channel->insert_keyframe({0.0, 1.0f}); + splash_fade_out_opacity_channel->insert_keyframe({splash_fade_out_duration, 0.0f}); + + // Setup animation frame callbacks + auto set_splash_opacity = [splash_tint](int channel, const float& opacity) + { + splash_tint->set_value(float4{1, 1, 1, opacity}); + }; + splash_fade_in_animation.set_frame_callback(set_splash_opacity); + splash_fade_out_animation.set_frame_callback(set_splash_opacity); + + // Trigger splash fade out animation when splash fade in animation ends + splash_fade_in_animation.set_end_callback + ( + [this]() + { + this->splash_fade_out_animation.play(); + } + ); + + // Trigger a state change when the splash fade out animation ends + splash_fade_out_animation.set_end_callback + ( + [&ctx]() + { + // Queue change to main menu state + ctx.function_queue.push + ( + [&ctx]() + { + ctx.state_machine.pop(); + ctx.state_machine.emplace(new ::state::main_menu(ctx, true)); + } + ); + } + ); + + // Add splash fade animations to animator + ctx.animator->add_animation(&splash_fade_in_animation); + ctx.animator->add_animation(&splash_fade_out_animation); + + // Start splash fade in animation + splash_fade_in_animation.play(); + + // Setup window resized callback + window_resized_subscription = ctx.window->get_resized_channel().subscribe + ( + [&](const auto& event) + { + const vec2 viewport_size = vec2(event.window->get_viewport_size()); + const vec2 viewport_center = viewport_size * 0.5f; + splash_billboard.set_translation({std::round(viewport_center.x()), std::round(viewport_center.y()), 0.0f}); + splash_billboard.update_tweens(); + } + ); + + // Construct splash skip function + auto skip = [&](const auto& event) + { + ctx.function_queue.emplace + ( + [&]() + { + // Black out screen + ctx.window->get_rasterizer()->set_clear_color(0.0f, 0.0f, 0.0f, 1.0f); + ctx.window->get_rasterizer()->clear_framebuffer(true, false, false); + ctx.window->swap_buffers(); + + // Change to main menu state + ctx.state_machine.pop(); + ctx.state_machine.emplace(new ::state::main_menu(ctx, true)); + } + ); + }; + + // Set up splash skippers + input_mapped_subscriptions.emplace_back + ( + ctx.input_mapper.get_gamepad_button_mapped_channel().subscribe + ( + skip + ) + ); + input_mapped_subscriptions.emplace_back + ( + ctx.input_mapper.get_key_mapped_channel().subscribe + ( + skip + ) + ); + input_mapped_subscriptions.emplace_back + ( + ctx.input_mapper.get_mouse_button_mapped_channel().subscribe + ( + skip + ) + ); + + // Enable splash skippers next frame + ctx.function_queue.push + ( + [&]() + { + ctx.input_mapper.connect(ctx.input_manager->get_event_queue()); + } + ); + + debug::log::trace("Entered splash state"); +} + +splash::~splash() +{ + debug::log::trace("Exiting splash state..."); + + // Disable splash skippers + ctx.input_mapper.disconnect(); + input_mapped_subscriptions.clear(); + + // Remove splash fade animations from animator + ctx.animator->remove_animation(&splash_fade_in_animation); + ctx.animator->remove_animation(&splash_fade_out_animation); + + // Remove splash billboard from UI scene + ctx.ui_scene->remove_object(&splash_billboard); + + // Unload splash texture + ctx.resource_manager->unload("splash.tex"); + + // Disable color buffer clearing in UI pass + ctx.ui_clear_pass->set_cleared_buffers(false, true, false); + + debug::log::trace("Exited splash state"); +} + +} // namespace state diff --git a/src/game/state/splash.hpp b/src/game/state/splash.hpp index 3dff350..d497000 100644 --- a/src/game/state/splash.hpp +++ b/src/game/state/splash.hpp @@ -21,19 +21,18 @@ #define ANTKEEPER_GAME_STATE_SPLASH_HPP #include "game/state/base.hpp" -#include "render/material.hpp" -#include "scene/billboard.hpp" -#include "animation/animation.hpp" -#include "event/subscription.hpp" +#include +#include +#include +#include #include -namespace game { namespace state { -class splash: public game::state::base +class splash: public ::state::base { public: - splash(game::context& ctx); + splash(::context& ctx); virtual ~splash(); private: @@ -46,6 +45,5 @@ private: }; } // namespace state -} // namespace game #endif // ANTKEEPER_GAME_STATE_SPLASH_HPP diff --git a/src/game/strings.cpp b/src/game/strings.cpp index fece625..c75a1e3 100644 --- a/src/game/strings.cpp +++ b/src/game/strings.cpp @@ -20,9 +20,8 @@ #include "game/strings.hpp" #include -namespace game { -std::string get_string(const game::context& ctx, std::uint32_t key) +std::string get_string(const ::context& ctx, std::uint32_t key) { if (auto i = ctx.string_map->find(key); i != ctx.string_map->end()) { @@ -32,4 +31,3 @@ std::string get_string(const game::context& ctx, std::uint32_t key) return std::format("${:x}", key); } -} // namespace game diff --git a/src/game/strings.hpp b/src/game/strings.hpp index ff7bdf5..5238556 100644 --- a/src/game/strings.hpp +++ b/src/game/strings.hpp @@ -24,7 +24,6 @@ #include #include -namespace game { /** * Returns a localized string. @@ -35,8 +34,7 @@ namespace game { * * @return `true` if the string was found, `false` otherwise. */ -[[nodiscard]] std::string get_string(const game::context& ctx, std::uint32_t key); +[[nodiscard]] std::string get_string(const ::context& ctx, std::uint32_t key); -} // namespace game #endif // ANTKEEPER_GAME_STRINGS_HPP diff --git a/src/game/system/astronomy.cpp b/src/game/system/astronomy.cpp deleted file mode 100644 index 6d32eed..0000000 --- a/src/game/system/astronomy.cpp +++ /dev/null @@ -1,627 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "game/system/astronomy.hpp" -#include "astro/apparent-size.hpp" -#include "game/component/blackbody.hpp" -#include "game/component/transform.hpp" -#include "game/component/diffuse-reflector.hpp" -#include "geom/intersection.hpp" -#include "geom/cartesian.hpp" -#include "color/color.hpp" -#include "physics/orbit/orbit.hpp" -#include "physics/time/ut1.hpp" -#include "physics/time/jd.hpp" -#include "physics/light/photometry.hpp" -#include "physics/light/luminosity.hpp" -#include "physics/light/refraction.hpp" -#include "physics/gas/atmosphere.hpp" -#include "geom/cartesian.hpp" -#include "astro/apparent-size.hpp" -#include "geom/solid-angle.hpp" -#include "math/polynomial.hpp" - -namespace game { -namespace system { - -astronomy::astronomy(entity::registry& registry): - updatable(registry), - time_days(0.0), - time_centuries(0.0), - time_scale(1.0), - observer_eid(entt::null), - reference_body_eid(entt::null), - transmittance_samples(0), - sun_light(nullptr), - sky_light(nullptr), - moon_light(nullptr), - bounce_light(nullptr), - bounce_albedo{0, 0, 0}, - sky_pass(nullptr), - starlight_illuminance{0, 0, 0} -{ - // Construct ENU to EUS transformation - enu_to_eus = math::transformation::se3 - { - {0, 0, 0}, - math::quaternion::rotate_x(-math::half_pi) - }; - - registry.on_construct().connect<&astronomy::on_observer_modified>(this); - registry.on_update().connect<&astronomy::on_observer_modified>(this); - registry.on_destroy().connect<&astronomy::on_observer_destroyed>(this); - registry.on_construct().connect<&astronomy::on_celestial_body_modified>(this); - registry.on_update().connect<&astronomy::on_celestial_body_modified>(this); - registry.on_destroy().connect<&astronomy::on_celestial_body_destroyed>(this); - registry.on_construct().connect<&astronomy::on_orbit_modified>(this); - registry.on_update().connect<&astronomy::on_orbit_modified>(this); - registry.on_destroy().connect<&astronomy::on_orbit_destroyed>(this); - registry.on_construct().connect<&astronomy::on_atmosphere_modified>(this); - registry.on_update().connect<&astronomy::on_atmosphere_modified>(this); - registry.on_destroy().connect<&astronomy::on_atmosphere_destroyed>(this); -} - -astronomy::~astronomy() -{ - registry.on_construct().disconnect<&astronomy::on_observer_modified>(this); - registry.on_update().disconnect<&astronomy::on_observer_modified>(this); - registry.on_destroy().disconnect<&astronomy::on_observer_destroyed>(this); - registry.on_construct().disconnect<&astronomy::on_celestial_body_modified>(this); - registry.on_update().disconnect<&astronomy::on_celestial_body_modified>(this); - registry.on_destroy().disconnect<&astronomy::on_celestial_body_destroyed>(this); - registry.on_construct().disconnect<&astronomy::on_orbit_modified>(this); - registry.on_update().disconnect<&astronomy::on_orbit_modified>(this); - registry.on_destroy().disconnect<&astronomy::on_orbit_destroyed>(this); - registry.on_construct().disconnect<&astronomy::on_atmosphere_modified>(this); - registry.on_update().disconnect<&astronomy::on_atmosphere_modified>(this); - registry.on_destroy().disconnect<&astronomy::on_atmosphere_destroyed>(this); -} - -void astronomy::update(double t, double dt) -{ - double3 sky_light_illuminance = {0.0, 0.0, 0.0}; - - // Add scaled timestep to current time - set_time(time_days + dt * time_scale); - - // Abort if no valid observer entity or reference body entity - if (observer_eid == entt::null || reference_body_eid == entt::null) - return; - - // Get pointer to observer component - const auto observer = registry.try_get(observer_eid); - - // Abort if no observer component - if (!observer) - return; - - // Get pointers to reference body components - const auto - [ - reference_body, - reference_orbit, - reference_atmosphere - ] = registry.try_get(reference_body_eid); - - // Abort if no reference body or reference orbit - if (!reference_body || !reference_orbit) - return; - - // Update ICRF to EUS transformation - update_icrf_to_eus(*reference_body, *reference_orbit); - - // Set the transform component translations of orbiting bodies to their topocentric positions - registry.view().each( - [&](entity::id entity_id, const auto& body, const auto& orbit, auto& transform) - { - // Skip reference body entity - if (entity_id == reference_body_eid) - return; - - // Transform orbital Cartesian position (r) from the ICRF frame to the EUS frame - const double3 r_eus = icrf_to_eus * orbit.position; - - // Evaluate body orientation polynomials - const double body_pole_ra = math::polynomial::horner(body.pole_ra.begin(), body.pole_ra.end(), time_centuries); - const double body_pole_dec = math::polynomial::horner(body.pole_dec.begin(), body.pole_dec.end(), time_centuries); - const double body_prime_meridian = math::polynomial::horner(body.prime_meridian.begin(), body.prime_meridian.end(), time_days); - - // Determine body orientation in the ICRF frame - math::quaternion rotation_icrf = physics::orbit::frame::bcbf::to_bci - ( - body_pole_ra, - body_pole_dec, - body_prime_meridian - ).r; - - // Transform body orientation from the ICRF frame to the EUS frame. - math::quaternion rotation_eus = math::normalize(icrf_to_eus.r * rotation_icrf); - - // Update local transform - if (orbit.parent != entt::null) - { - transform.local.translation = math::normalize(float3(r_eus)); - transform.local.rotation = math::quaternion(rotation_eus); - transform.local.scale = {1.0f, 1.0f, 1.0f}; - } - }); - - constexpr double3 bounce_normal = {0, 1, 0}; - double3 bounce_illuminance = {0, 0, 0}; - - // Update blackbody lighting - registry.view().each( - [&, bounce_normal](entity::id entity_id, const auto& blackbody_body, const auto& blackbody_orbit, const auto& blackbody) - { - // Transform blackbody position from ICRF frame to EUS frame - const double3 blackbody_position_eus = icrf_to_eus * blackbody_orbit.position; - - // Measure distance and direction, in EUS frame, from observer to blackbody - const double observer_blackbody_distance = math::length(blackbody_position_eus); - const double3 observer_blackbody_direction_eus = blackbody_position_eus / observer_blackbody_distance; - - // Measure blackbody solid angle as seen by observer - const double observer_blackbody_angular_radius = astro::angular_radius(blackbody_body.radius, observer_blackbody_distance); - const double observer_blackbody_solid_angle = geom::solid_angle::cone(observer_blackbody_angular_radius); - - // Calculate illuminance from blackbody reaching observer - const double3 observer_blackbody_illuminance = blackbody.luminance * observer_blackbody_solid_angle; - - // Calculate illuminance from blackbody reaching observer after atmospheric extinction - double3 observer_blackbody_transmitted_illuminance = observer_blackbody_illuminance; - if (reference_atmosphere) - { - // Construct ray at observer pointing towards the blackbody - const geom::ray ray = {{0, 0, 0}, observer_blackbody_direction_eus}; - - // Integrate atmospheric spectral transmittance factor between observer and blackbody - const double3 transmittance = integrate_transmittance(*observer, *reference_body, *reference_atmosphere, ray); - - // Attenuate illuminance from blackbody reaching observer by spectral transmittance factor - observer_blackbody_transmitted_illuminance *= transmittance; - } - - // Update sun light - if (sun_light != nullptr) - { - const double3 blackbody_up_eus = icrf_to_eus.r * double3{0, 0, 1}; - sun_light->set_rotation - ( - math::look_rotation - ( - float3(-observer_blackbody_direction_eus), - float3(blackbody_up_eus) - ) - ); - - sun_light->set_color(float3(observer_blackbody_transmitted_illuminance)); - - // Bounce sun light - bounce_illuminance += std::max(0.0, math::dot(bounce_normal, -observer_blackbody_direction_eus)) * observer_blackbody_transmitted_illuminance * bounce_albedo; - } - - // Update sky light - if (sky_light != nullptr) - { - // Calculate sky illuminance - double3 blackbody_position_enu_spherical = physics::orbit::frame::enu::spherical(enu_to_eus.inverse() * blackbody_position_eus); - const double sky_illuminance = 25000.0 * std::max(0.0, std::sin(blackbody_position_enu_spherical.y())); - - // Add sky illuminance to sky light illuminance - sky_light_illuminance += {sky_illuminance, sky_illuminance, sky_illuminance}; - - // Add starlight illuminance to sky light illuminance - sky_light_illuminance += starlight_illuminance; - - // Update sky light - sky_light->set_color(float3(sky_light_illuminance)); - - // Bounce sky light - bounce_illuminance += sky_light_illuminance * bounce_albedo; - } - - // Upload blackbody params to sky pass - if (this->sky_pass) - { - this->sky_pass->set_sun_position(float3(blackbody_position_eus)); - this->sky_pass->set_sun_luminance(float3(blackbody.luminance)); - this->sky_pass->set_sun_illuminance(float3(observer_blackbody_illuminance), float3(observer_blackbody_transmitted_illuminance)); - this->sky_pass->set_sun_angular_radius(static_cast(observer_blackbody_angular_radius)); - } - - // Update diffuse reflectors - this->registry.view().each( - [&, bounce_normal](entity::id entity_id, const auto& reflector_body, const auto& reflector_orbit, const auto& reflector, const auto& transform) - { - // Transform reflector position from ICRF frame to EUS frame - const double3 reflector_position_eus = icrf_to_eus * reflector_orbit.position; - - // Measure distance and direction, in EUS frame, from observer to reflector - const double observer_reflector_distance = math::length(reflector_position_eus); - const double3 observer_reflector_direction_eus = reflector_position_eus / observer_reflector_distance; - - // Measure distance and direction, in EUS frame, from reflector to blackbody - double3 reflector_blackbody_direction_eus = blackbody_position_eus - reflector_position_eus; - const double reflector_blackbody_distance = math::length(reflector_blackbody_direction_eus); - reflector_blackbody_direction_eus /= reflector_blackbody_distance; - - // Measure blackbody solid angle as seen by reflector - const double reflector_blackbody_angular_radius = astro::angular_radius(blackbody_body.radius, reflector_blackbody_distance); - const double reflector_blackbody_solid_angle = geom::solid_angle::cone(reflector_blackbody_angular_radius); - - // Calculate blackbody illuminance reaching reflector - const double3 reflector_blackbody_illuminance = blackbody.luminance * reflector_blackbody_solid_angle; - - // Measure reflector solid angle as seen by observer - const double observer_reflector_angular_radius = astro::angular_radius(reflector_body.radius, observer_reflector_distance); - const double observer_reflector_solid_angle = geom::solid_angle::cone(observer_reflector_angular_radius); - - // Determine phase factor of reflector as seen by observer - const double observer_reflector_phase_factor = dot(observer_reflector_direction_eus, -reflector_blackbody_direction_eus) * 0.5 + 0.5; - - // Measure observer reference body solid angle as seen by reflector - const double reflector_observer_angular_radius = astro::angular_radius(reference_body->radius, observer_reflector_distance); - const double reflector_observer_solid_angle = geom::solid_angle::cone(reflector_observer_angular_radius); - - // Determine phase factor of observer reference body as by reflector - const double reflector_observer_phase_factor = dot(-observer_reflector_direction_eus, -observer_blackbody_direction_eus) * 0.5 + 0.5; - - // Calculate spectral transmittance between observer and reflector factor due to atmospheric extinction - double3 observer_reflector_transmittance = {1, 1, 1}; - if (reference_atmosphere) - { - const geom::ray ray = {{0, 0, 0}, observer_reflector_direction_eus}; - observer_reflector_transmittance = integrate_transmittance(*observer, *reference_body, *reference_atmosphere, ray); - } - - // Measure luminance of observer reference body as seen by reflector - const double3 reflector_observer_luminance = observer_blackbody_illuminance * reference_body->albedo * observer_reflector_transmittance * reflector_observer_phase_factor * math::inv_pi; - - // Measure illuminance from observer reference body reaching reflector - const double3 reflector_observer_illuminance = reflector_observer_luminance * reflector_observer_solid_angle; - - // Measure luminance of reflector as seen by observer - const double3 observer_reflector_luminance = (reflector_blackbody_illuminance * observer_reflector_phase_factor + reflector_observer_illuminance) * reflector.albedo * observer_reflector_transmittance * math::inv_pi; - - // Measure illuminance from reflector reaching observer - const double3 observer_reflector_illuminance = observer_reflector_luminance * observer_reflector_solid_angle; - - if (this->sky_pass) - { - this->sky_pass->set_moon_position(transform.local.translation); - this->sky_pass->set_moon_rotation(transform.local.rotation); - this->sky_pass->set_moon_angular_radius(static_cast(observer_reflector_angular_radius)); - this->sky_pass->set_moon_sunlight_direction(float3(-reflector_blackbody_direction_eus)); - this->sky_pass->set_moon_sunlight_illuminance(float3(reflector_blackbody_illuminance * observer_reflector_transmittance)); - this->sky_pass->set_moon_planetlight_direction(float3(observer_reflector_direction_eus)); - this->sky_pass->set_moon_planetlight_illuminance(float3(reflector_observer_illuminance * observer_reflector_transmittance)); - this->sky_pass->set_moon_illuminance(float3(observer_reflector_illuminance / observer_reflector_transmittance), float3(observer_reflector_illuminance)); - } - - if (this->moon_light) - { - const float3 reflector_up_eus = float3(icrf_to_eus.r * double3{0, 0, 1}); - - this->moon_light->set_color(float3(observer_reflector_illuminance)); - this->moon_light->set_rotation - ( - math::look_rotation - ( - float3(-observer_reflector_direction_eus), - reflector_up_eus - ) - ); - - // Bounce moon light - bounce_illuminance += std::max(0.0, math::dot(bounce_normal, -observer_reflector_direction_eus)) * observer_reflector_illuminance * bounce_albedo; - } - }); - }); - - if (bounce_light) - { - bounce_light->set_color(float3(bounce_illuminance)); - } -} - -void astronomy::set_time(double t) -{ - time_days = t; - time_centuries = time_days * physics::time::jd::centuries_per_day; -} - -void astronomy::set_time_scale(double scale) -{ - time_scale = scale; -} - -void astronomy::set_observer(entity::id eid) -{ - if (observer_eid != eid) - { - observer_eid = eid; - - if (observer_eid != entt::null) - observer_modified(); - else - reference_body_eid = entt::null; - } -} - -void astronomy::set_transmittance_samples(std::size_t samples) -{ - transmittance_samples = samples; -} - -void astronomy::set_sun_light(scene::directional_light* light) -{ - sun_light = light; -} - -void astronomy::set_sky_light(scene::ambient_light* light) -{ - sky_light = light; -} - -void astronomy::set_moon_light(scene::directional_light* light) -{ - moon_light = light; -} - -void astronomy::set_bounce_light(scene::directional_light* light) -{ - bounce_light = light; -} - -void astronomy::set_bounce_albedo(const double3& albedo) -{ - bounce_albedo = albedo; -} - -void astronomy::set_starlight_illuminance(const double3& illuminance) -{ - starlight_illuminance = illuminance; -} - -void astronomy::set_sky_pass(::render::sky_pass* pass) -{ - this->sky_pass = pass; - - if (sky_pass) - { - if (observer_eid != entt::null) - { - // Get pointer to observer - const auto observer = registry.try_get(reference_body_eid); - - sky_pass->set_observer_elevation(static_cast(observer->elevation)); - } - - if (reference_body_eid != entt::null) - { - // Get pointer to reference celestial body - const auto reference_body = registry.try_get(reference_body_eid); - - if (reference_body) - sky_pass->set_planet_radius(static_cast(reference_body->radius)); - else - sky_pass->set_planet_radius(0.0f); - } - } -} - -void astronomy::on_observer_modified(entity::registry& registry, entity::id entity_id) -{ - if (entity_id == observer_eid) - observer_modified(); -} - -void astronomy::on_observer_destroyed(entity::registry& registry, entity::id entity_id) -{ - if (entity_id == observer_eid) - observer_modified(); -} - -void astronomy::on_celestial_body_modified(entity::registry& registry, entity::id entity_id) -{ - if (entity_id == reference_body_eid) - reference_body_modified(); -} - -void astronomy::on_celestial_body_destroyed(entity::registry& registry, entity::id entity_id) -{ - if (entity_id == reference_body_eid) - reference_body_modified(); -} - -void astronomy::on_orbit_modified(entity::registry& registry, entity::id entity_id) -{ - if (entity_id == reference_body_eid) - reference_orbit_modified(); -} - -void astronomy::on_orbit_destroyed(entity::registry& registry, entity::id entity_id) -{ - if (entity_id == reference_body_eid) - reference_orbit_modified(); -} - -void astronomy::on_atmosphere_modified(entity::registry& registry, entity::id entity_id) -{ - if (entity_id == reference_body_eid) - reference_atmosphere_modified(); -} - -void astronomy::on_atmosphere_destroyed(entity::registry& registry, entity::id entity_id) -{ - if (entity_id == reference_body_eid) - reference_atmosphere_modified(); -} - -void astronomy::observer_modified() -{ - // Get pointer to observer component - const auto observer = registry.try_get(observer_eid); - - if (observer) - { - if (reference_body_eid != observer->reference_body_eid) - { - // Reference body changed - reference_body_eid = observer->reference_body_eid; - reference_body_modified(); - reference_orbit_modified(); - reference_atmosphere_modified(); - } - - if (reference_body_eid != entt::null) - { - // Get pointer to reference celestial body - const auto reference_body = registry.try_get(reference_body_eid); - - // Update BCBF to EUS transformation - if (reference_body) - update_bcbf_to_eus(*observer, *reference_body); - } - - // Upload observer elevation to sky pass - if (sky_pass) - sky_pass->set_observer_elevation(static_cast(observer->elevation)); - } -} - -void astronomy::reference_body_modified() -{ - // Get pointer to reference celestial body - const auto reference_body = registry.try_get(reference_body_eid); - - if (reference_body) - { - // Get pointer to observer - const auto observer = registry.try_get(observer_eid); - - // Update BCBF to EUS transformation - if (observer) - update_bcbf_to_eus(*observer, *reference_body); - } - - // Update reference celestial body-related sky pass parameters - if (sky_pass) - { - if (reference_body) - sky_pass->set_planet_radius(static_cast(reference_body->radius)); - else - sky_pass->set_planet_radius(0.0f); - } -} - -void astronomy::reference_orbit_modified() -{ - -} - -void astronomy::reference_atmosphere_modified() -{ - -} - -void astronomy::update_bcbf_to_eus(const game::component::observer& observer, const game::component::celestial_body& body) -{ - // Construct BCBF to EUS transformation - bcbf_to_eus = physics::orbit::frame::bcbf::to_enu - ( - body.radius + observer.elevation, - observer.latitude, - observer.longitude - ) * enu_to_eus; -} - -void astronomy::update_icrf_to_eus(const game::component::celestial_body& body, const game::component::orbit& orbit) -{ - // Evaluate reference body orientation polynomials - const double body_pole_ra = math::polynomial::horner(body.pole_ra.begin(), body.pole_ra.end(), time_centuries); - const double body_pole_dec = math::polynomial::horner(body.pole_dec.begin(), body.pole_dec.end(), time_centuries); - const double body_prime_meridian = math::polynomial::horner(body.prime_meridian.begin(), body.prime_meridian.end(), time_days); - - // Construct ICRF frame to BCBF transformation - math::transformation::se3 icrf_to_bcbf = physics::orbit::frame::bci::to_bcbf - ( - body_pole_ra, - body_pole_dec, - body_prime_meridian - ); - icrf_to_bcbf.t = icrf_to_bcbf.r * -orbit.position; - - /// Construct ICRF to EUS transformation - icrf_to_eus = icrf_to_bcbf * bcbf_to_eus; - - // Pass ICRF to EUS transformation to sky pass - if (sky_pass) - { - // Upload topocentric frame to sky pass - sky_pass->set_icrf_to_eus - ( - math::transformation::se3 - { - float3(icrf_to_eus.t), - math::quaternion(icrf_to_eus.r) - } - ); - } -} - -double3 astronomy::integrate_transmittance(const game::component::observer& observer, const game::component::celestial_body& body, const game::component::atmosphere& atmosphere, geom::ray ray) const -{ - double3 transmittance = {1, 1, 1}; - - // Make ray height relative to center of reference body - ray.origin.y() += body.radius + observer.elevation; - - // Construct sphere representing upper limit of the atmosphere - geom::sphere atmosphere_sphere; - atmosphere_sphere.center = {0, 0, 0}; - atmosphere_sphere.radius = body.radius + atmosphere.upper_limit; - - // Check for intersection between the ray and atmosphere - auto intersection = geom::ray_sphere_intersection(ray, atmosphere_sphere); - if (std::get<0>(intersection)) - { - // Get point of intersection - const double3 intersection_point = ray.extrapolate(std::get<2>(intersection)); - - // Integrate optical of Rayleigh, Mie, and ozone particles - const double optical_depth_r = physics::gas::atmosphere::optical_depth_exp(ray.origin, intersection_point, body.radius, atmosphere.rayleigh_scale_height, transmittance_samples); - const double optical_depth_m = physics::gas::atmosphere::optical_depth_exp(ray.origin, intersection_point, body.radius, atmosphere.mie_scale_height, transmittance_samples); - const double optical_depth_o = physics::gas::atmosphere::optical_depth_tri(ray.origin, intersection_point, body.radius, atmosphere.ozone_lower_limit, atmosphere.ozone_upper_limit, atmosphere.ozone_mode, transmittance_samples); - - // Calculate transmittance factor due to scattering and absorption - const double3 extinction_r = atmosphere.rayleigh_scattering * optical_depth_r; - const double extinction_m = atmosphere.mie_extinction * optical_depth_m; - const double3 extinction_o = atmosphere.ozone_absorption * optical_depth_o; - transmittance = extinction_r + double3{extinction_m, extinction_m, extinction_m} + extinction_o; - transmittance.x() = std::exp(-transmittance.x()); - transmittance.y() = std::exp(-transmittance.y()); - transmittance.z() = std::exp(-transmittance.z()); - } - - return transmittance; -} - -} // namespace system -} // namespace game diff --git a/src/game/system/astronomy.hpp b/src/game/system/astronomy.hpp deleted file mode 100644 index 30ac0a0..0000000 --- a/src/game/system/astronomy.hpp +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_SYSTEM_ASTRONOMY_HPP -#define ANTKEEPER_GAME_SYSTEM_ASTRONOMY_HPP - -#include "game/system/updatable.hpp" -#include "entity/id.hpp" -#include "scene/directional-light.hpp" -#include "scene/ambient-light.hpp" -#include "utility/fundamental-types.hpp" -#include "math/se3.hpp" -#include "render/passes/sky-pass.hpp" -#include "game/component/observer.hpp" -#include "game/component/atmosphere.hpp" -#include "game/component/celestial-body.hpp" -#include "game/component/orbit.hpp" -#include "geom/ray.hpp" - -namespace game { -namespace system { - -/** - * Calculates apparent properties of celestial bodies as seen by an observer. - */ -class astronomy: - public updatable -{ -public: - astronomy(entity::registry& registry); - ~astronomy(); - - /** - * Adds the timestep `dt`, scaled by set time scale, to the current time, then calculates apparent properties of celestial bodies as seen by an observer. - * - * @param t Time, in seconds. - * @param dt Delta time, in seconds. - */ - virtual void update(double t, double dt); - - /** - * Sets the current time. - * - * @param t Time since epoch, in days. - */ - void set_time(double t); - - /** - * Sets the factor by which the timestep `dt` will be scaled before being added to the current time. - * - * @param scale Factor by which to scale the timestep. - */ - void set_time_scale(double scale); - - /** - * Sets the observer entity. - * - * @param eid Entity ID of the observer. - */ - void set_observer(entity::id eid); - - /** - * Sets the number of samples to take when integrating atmospheric transmittance. - * - * @param samples Number of integration samples. - */ - void set_transmittance_samples(std::size_t samples); - - void set_sun_light(scene::directional_light* light); - void set_sky_light(scene::ambient_light* light); - void set_moon_light(scene::directional_light* light); - void set_bounce_light(scene::directional_light* light); - void set_bounce_albedo(const double3& albedo); - void set_starlight_illuminance(const double3& illuminance); - void set_sky_pass(::render::sky_pass* pass); - -private: - void on_observer_modified(entity::registry& registry, entity::id entity_id); - void on_observer_destroyed(entity::registry& registry, entity::id entity_id); - void on_celestial_body_modified(entity::registry& registry, entity::id entity_id); - void on_celestial_body_destroyed(entity::registry& registry, entity::id entity_id); - void on_orbit_modified(entity::registry& registry, entity::id entity_id); - void on_orbit_destroyed(entity::registry& registry, entity::id entity_id); - void on_atmosphere_modified(entity::registry& registry, entity::id entity_id); - void on_atmosphere_destroyed(entity::registry& registry, entity::id entity_id); - - /// Called each time the observer is modified. - void observer_modified(); - - /// Called each time the celestial body of the reference body is modified. - void reference_body_modified(); - - /// Called each time the orbit of the reference body is modified. - void reference_orbit_modified(); - - /// Called each time the atmosphere of the reference body is modified. - void reference_atmosphere_modified(); - - /// Updates the BCBF to EUS transformation. - void update_bcbf_to_eus(const game::component::observer& observer, const game::component::celestial_body& body); - - /// Updates the ICRF to EUS transformation. - void update_icrf_to_eus(const game::component::celestial_body& body, const game::component::orbit& orbit); - - /** - * Integrates a transmittance factor due to atmospheric extinction along a ray. - * - * @param ray Ray to cast, in the EUS frame. - * @param samples Number of samples to integrate. - * - * @return Spectral transmittance factor. - */ - double3 integrate_transmittance(const game::component::observer& observer, const game::component::celestial_body& body, const game::component::atmosphere& atmosphere, geom::ray ray) const; - - /// Time since epoch, in days. - double time_days; - - /// Time since epoch, in centuries. - double time_centuries; - - /// Time scale. - double time_scale; - - /// Number of transmittance integration samples. - std::size_t transmittance_samples; - - /// Entity ID of the observer. - entity::id observer_eid; - - /// Entity ID of the reference body. - entity::id reference_body_eid; - - /// ENU to EUS transformation. - math::transformation::se3 enu_to_eus; - - /// BCBF to EUS transformation. - math::transformation::se3 bcbf_to_eus; - - /// ICRF to EUS tranformation. - math::transformation::se3 icrf_to_eus; - - scene::directional_light* sun_light; - scene::ambient_light* sky_light; - scene::directional_light* moon_light; - scene::directional_light* bounce_light; - double3 bounce_albedo; - ::render::sky_pass* sky_pass; - double3 starlight_illuminance; -}; - -} // namespace system -} // namespace game - -#endif // ANTKEEPER_GAME_SYSTEM_ASTRONOMY_HPP diff --git a/src/game/system/atmosphere.cpp b/src/game/system/atmosphere.cpp deleted file mode 100644 index 8bea475..0000000 --- a/src/game/system/atmosphere.cpp +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "game/system/atmosphere.hpp" -#include "physics/gas/atmosphere.hpp" -#include "physics/gas/ozone.hpp" -#include "physics/number-density.hpp" - -namespace game { -namespace system { - -atmosphere::atmosphere(entity::registry& registry): - updatable(registry), - rgb_wavelengths{0, 0, 0}, - rgb_ozone_cross_sections{0, 0, 0}, - active_atmosphere_eid(entt::null), - sky_pass(nullptr) -{ - registry.on_construct().connect<&atmosphere::on_atmosphere_construct>(this); - registry.on_update().connect<&atmosphere::on_atmosphere_update>(this); - registry.on_destroy().connect<&atmosphere::on_atmosphere_destroy>(this); -} - -atmosphere::~atmosphere() -{ - registry.on_construct().disconnect<&atmosphere::on_atmosphere_construct>(this); - registry.on_update().disconnect<&atmosphere::on_atmosphere_update>(this); - registry.on_destroy().disconnect<&atmosphere::on_atmosphere_destroy>(this); -} - -void atmosphere::update(double t, double dt) -{} - -void atmosphere::set_rgb_wavelengths(const double3& wavelengths) -{ - rgb_wavelengths = wavelengths; - - // Update ozone cross sections - rgb_ozone_cross_sections = - { - physics::gas::ozone::cross_section_293k(wavelengths.x() * 1e9), - physics::gas::ozone::cross_section_293k(wavelengths.y() * 1e9), - physics::gas::ozone::cross_section_293k(wavelengths.z() * 1e9) - }; - - // Update atmosphere components - registry.view().each - ( - [&](entity::id entity_id, auto& component) - { - update_atmosphere(entity_id); - } - ); -} - -void atmosphere::set_sky_pass(::render::sky_pass* pass) -{ - sky_pass = pass; - update_sky_pass(); -} - -void atmosphere::set_active_atmosphere(entity::id entity_id) -{ - if (entity_id != active_atmosphere_eid) - { - active_atmosphere_eid = entity_id; - update_sky_pass(); - } -} - -void atmosphere::update_atmosphere(entity::id entity_id) -{ - // Get atmosphere component of the entity - game::component::atmosphere* component = registry.try_get(entity_id); - - // Abort if entity has no atmosphere component - if (!component) - return; - - // Calculate Rayleigh scattering coefficients - const double rayleigh_density = physics::number_density(component->rayleigh_concentration); - const double rayleigh_polarization = physics::gas::atmosphere::polarization(component->index_of_refraction, rayleigh_density); - component->rayleigh_scattering = - { - physics::gas::atmosphere::scattering(rayleigh_density, rayleigh_polarization, rgb_wavelengths.x()), - physics::gas::atmosphere::scattering(rayleigh_density, rayleigh_polarization, rgb_wavelengths.y()), - physics::gas::atmosphere::scattering(rayleigh_density, rayleigh_polarization, rgb_wavelengths.z()) - }; - - // Calculate Mie scattering and extinction coefficients - const double mie_density = physics::number_density(component->mie_concentration); - const double mie_polarization = physics::gas::atmosphere::polarization(component->index_of_refraction, mie_density); - component->mie_scattering = physics::gas::atmosphere::scattering(mie_density, mie_polarization); - component->mie_extinction = physics::gas::atmosphere::extinction(component->mie_scattering, component->mie_albedo); - - // Calculate ozone absorption coefficients - const double ozone_density = physics::number_density(component->ozone_concentration); - component->ozone_absorption = - { - physics::gas::ozone::absorption(rgb_ozone_cross_sections.x(), ozone_density), - physics::gas::ozone::absorption(rgb_ozone_cross_sections.y(), ozone_density), - physics::gas::ozone::absorption(rgb_ozone_cross_sections.z(), ozone_density) - }; - - // Update sky pass parameters - if (entity_id == active_atmosphere_eid) - { - update_sky_pass(); - } -} - -void atmosphere::update_sky_pass() -{ - // Abort if no sky pass set - if (!sky_pass) - return; - - // Abort if active atmosphere entity is not valid - if (!registry.valid(active_atmosphere_eid)) - return; - - // Get atmosphere component of the entity - game::component::atmosphere* component = registry.try_get(active_atmosphere_eid); - - // Abort if entity has no atmosphere component - if (!component) - return; - - sky_pass->set_atmosphere_upper_limit(static_cast(component->upper_limit)); - sky_pass->set_rayleigh_parameters(static_cast(component->rayleigh_scale_height), math::vector(component->rayleigh_scattering)); - sky_pass->set_mie_parameters(static_cast(component->mie_scale_height), static_cast(component->mie_scattering), static_cast(component->mie_extinction), static_cast(component->mie_anisotropy)); - sky_pass->set_ozone_parameters(static_cast(component->ozone_lower_limit), static_cast(component->ozone_upper_limit), static_cast(component->ozone_mode), math::vector(component->ozone_absorption)); - sky_pass->set_airglow_illuminance(math::vector(component->airglow_illuminance)); -} - -void atmosphere::on_atmosphere_construct(entity::registry& registry, entity::id entity_id) -{ - update_atmosphere(entity_id); -} - -void atmosphere::on_atmosphere_update(entity::registry& registry, entity::id entity_id) -{ - update_atmosphere(entity_id); -} - -void atmosphere::on_atmosphere_destroy(entity::registry& registry, entity::id entity_id) -{ - if (entity_id == active_atmosphere_eid) - active_atmosphere_eid = entt::null; -} - -} // namespace system -} // namespace game diff --git a/src/game/system/atmosphere.hpp b/src/game/system/atmosphere.hpp deleted file mode 100644 index f7004d4..0000000 --- a/src/game/system/atmosphere.hpp +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_SYSTEM_ATMOSPHERE_HPP -#define ANTKEEPER_GAME_SYSTEM_ATMOSPHERE_HPP - -#include "game/system/updatable.hpp" -#include "entity/id.hpp" -#include "utility/fundamental-types.hpp" -#include "game/component/atmosphere.hpp" -#include "render/passes/sky-pass.hpp" - -namespace game { -namespace system { - -/** - * Updates variables related to atmospheric scattering. - */ -class atmosphere: - public updatable -{ -public: - atmosphere(entity::registry& registry); - ~atmosphere(); - - virtual void update(double t, double dt); - - /** - * Sets the wavelengths of red, green, and blue light. - * - * @param wavelengths Vector containing the wavelengths of red (x), green (y), and blue (z) light, in meters. - */ - void set_rgb_wavelengths(const double3& wavelengths); - - void set_sky_pass(::render::sky_pass* pass); - - /** - * Sets the entity ID of the active atmosphere. - * - * @param entity_id Entity ID of the active atmosphere. - */ - void set_active_atmosphere(entity::id entity_id); - -private: - void update_atmosphere(entity::id entity_id); - void update_sky_pass(); - - void on_atmosphere_construct(entity::registry& registry, entity::id entity_id); - void on_atmosphere_update(entity::registry& registry, entity::id entity_id); - void on_atmosphere_destroy(entity::registry& registry, entity::id entity_id); - - entity::id active_atmosphere_eid; - double3 rgb_wavelengths; - double3 rgb_ozone_cross_sections; - game::component::atmosphere* atmosphere_component; - ::render::sky_pass* sky_pass; -}; - -} // namespace system -} // namespace game - -#endif // ANTKEEPER_GAME_SYSTEM_ATMOSPHERE_HPP diff --git a/src/game/system/behavior.cpp b/src/game/system/behavior.cpp deleted file mode 100644 index fdf862a..0000000 --- a/src/game/system/behavior.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "game/system/behavior.hpp" -#include "game/component/behavior.hpp" -#include "entity/id.hpp" - -namespace game { -namespace system { - -behavior::behavior(entity::registry& registry): - updatable(registry) -{} - -void behavior::update(double t, double dt) -{ - entity::ebt::context context; - context.registry = ®istry; - - registry.view().each( - [&](entity::id entity_id, auto& behavior) - { - if (behavior.behavior_tree) - { - context.entity_id = entity_id; - behavior.behavior_tree->execute(context); - } - }); -} - -} // namespace system -} // namespace game diff --git a/src/game/system/behavior.hpp b/src/game/system/behavior.hpp deleted file mode 100644 index 69a914e..0000000 --- a/src/game/system/behavior.hpp +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_SYSTEM_BEHAVIOR_HPP -#define ANTKEEPER_GAME_SYSTEM_BEHAVIOR_HPP - -#include "game/system/updatable.hpp" - -namespace game { -namespace system { - -class behavior: - public updatable -{ -public: - behavior(entity::registry& registry); - virtual void update(double t, double dt); -}; - -} // namespace system -} // namespace game - -#endif // ANTKEEPER_GAME_SYSTEM_BEHAVIOR_HPP - diff --git a/src/game/system/blackbody.cpp b/src/game/system/blackbody.cpp deleted file mode 100644 index 8e0c621..0000000 --- a/src/game/system/blackbody.cpp +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "game/system/blackbody.hpp" -#include "color/color.hpp" -#include "physics/light/blackbody.hpp" -#include "physics/light/photometry.hpp" -#include "math/quadrature.hpp" -#include - -namespace game { -namespace system { - -blackbody::blackbody(entity::registry& registry): - updatable(registry), - illuminant(color::illuminant::deg2::d50) -{ - // Construct a range of sample wavelengths in the visible spectrum - visible_wavelengths_nm.resize(780 - 280); - std::iota(visible_wavelengths_nm.begin(), visible_wavelengths_nm.end(), 280); - - registry.on_construct().connect<&blackbody::on_blackbody_construct>(this); - registry.on_update().connect<&blackbody::on_blackbody_update>(this); - registry.on_construct().connect<&blackbody::on_celestial_body_construct>(this); - registry.on_update().connect<&blackbody::on_celestial_body_update>(this); -} - -blackbody::~blackbody() -{ - registry.on_construct().disconnect<&blackbody::on_blackbody_construct>(this); - registry.on_update().disconnect<&blackbody::on_blackbody_update>(this); - registry.on_construct().disconnect<&blackbody::on_celestial_body_construct>(this); - registry.on_update().disconnect<&blackbody::on_celestial_body_update>(this); -} - -void blackbody::update(double t, double dt) -{} - -void blackbody::set_illuminant(const math::vector2& illuminant) -{ - this->illuminant = illuminant; -} - -void blackbody::update_luminance(entity::id entity_id) -{ - // Get blackbody and celestial body components of the entity - auto [blackbody, celestial_body] = registry.try_get(entity_id); - - // Abort if entity is missing a blackbody or celestial body component - if (!blackbody || !celestial_body) - return; - - // Construct chromatic adaptation transform - const double3x3 cat = color::cat::matrix(illuminant, color::aces::white_point); - - // Construct a lambda function which calculates the blackbody's RGB luminance of a given wavelength - auto rgb_luminance = [temperature = blackbody->temperature, cat](double wavelength_nm) -> double3 - { - // Convert wavelength from nanometers to meters - const double wavelength_m = wavelength_nm * 1e-9; - - // Calculate the spectral intensity of the wavelength - const double spectral_radiance = physics::light::blackbody::spectral_radiance(temperature, wavelength_m); - - - // Calculate the ACEScg color of the wavelength using CIE color matching functions - double3 spectral_color = color::aces::ap1.from_xyz * cat * color::xyz::match(wavelength_nm); - - // Scale the spectral color by spectral intensity - return spectral_color * spectral_radiance * 1e-9 * physics::light::max_luminous_efficacy; - }; - - // Integrate the blackbody RGB luminance over wavelengths in the visible spectrum - blackbody->luminance = math::quadrature::simpson(rgb_luminance, visible_wavelengths_nm.begin(), visible_wavelengths_nm.end()); -} - -void blackbody::on_blackbody_construct(entity::registry& registry, entity::id entity_id) -{ - update_luminance(entity_id); -} - -void blackbody::on_blackbody_update(entity::registry& registry, entity::id entity_id) -{ - update_luminance(entity_id); -} - -void blackbody::on_celestial_body_construct(entity::registry& registry, entity::id entity_id) -{ - update_luminance(entity_id); -} - -void blackbody::on_celestial_body_update(entity::registry& registry, entity::id entity_id) -{ - update_luminance(entity_id); -} - -} // namespace system -} // namespace game diff --git a/src/game/system/blackbody.hpp b/src/game/system/blackbody.hpp deleted file mode 100644 index 222d04e..0000000 --- a/src/game/system/blackbody.hpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_SYSTEM_BLACKBODY_HPP -#define ANTKEEPER_GAME_SYSTEM_BLACKBODY_HPP - -#include "game/system/updatable.hpp" -#include "entity/id.hpp" -#include "utility/fundamental-types.hpp" -#include "game/component/blackbody.hpp" -#include "game/component/celestial-body.hpp" -#include - -namespace game { -namespace system { - -/** - * Calculates the RGB luminous intensity of blackbody radiators. - */ -class blackbody: - public updatable -{ -public: - blackbody(entity::registry& registry); - ~blackbody(); - - virtual void update(double t, double dt); - - /** - * Sets the blackbody illuminant. - * - * @param illuminant CIE chromaticity coordinates of an illuminant. - */ - void set_illuminant(const math::vector2& illuminant); - -private: - void update_luminance(entity::id entity_id); - - void on_blackbody_construct(entity::registry& registry, entity::id entity_id); - void on_blackbody_update(entity::registry& registry, entity::id entity_id); - - void on_celestial_body_construct(entity::registry& registry, entity::id entity_id); - void on_celestial_body_update(entity::registry& registry, entity::id entity_id); - - math::vector2 illuminant; - std::vector visible_wavelengths_nm; -}; - -} // namespace system -} // namespace game - -#endif // ANTKEEPER_GAME_SYSTEM_BLACKBODY_HPP diff --git a/src/game/system/camera.cpp b/src/game/system/camera.cpp deleted file mode 100644 index 4282e13..0000000 --- a/src/game/system/camera.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "camera.hpp" - -namespace game { -namespace system { - -camera::camera(entity::registry& registry): - updatable(registry), - viewport{0, 0, 0, 0} -{} - -void camera::update(double t, double dt) -{ - -} - -void camera::set_viewport(const float4& viewport) -{ - this->viewport = viewport; -} - -} // namespace system -} // namespace game diff --git a/src/game/system/camera.hpp b/src/game/system/camera.hpp deleted file mode 100644 index ba8badf..0000000 --- a/src/game/system/camera.hpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_SYSTEM_CAMERA_HPP -#define ANTKEEPER_GAME_SYSTEM_CAMERA_HPP - -#include "game/system/updatable.hpp" -#include "utility/fundamental-types.hpp" - -namespace game { -namespace system { - -class camera: public updatable -{ -public: - camera(entity::registry& registry); - virtual void update(double t, double dt); - - void set_viewport(const float4& viewport); - -private: - float4 viewport; -}; - -} // namespace system -} // namespace game - -#endif // ANTKEEPER_GAME_SYSTEM_CAMERA_HPP - diff --git a/src/game/system/collision.cpp b/src/game/system/collision.cpp deleted file mode 100644 index 3ae3b9a..0000000 --- a/src/game/system/collision.cpp +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "collision.hpp" -#include "game/component/transform.hpp" -#include "game/component/picking.hpp" -#include "geom/primitive/intersection.hpp" -#include "geom/primitive/plane.hpp" -#include "math/transform-operators.hpp" -#include - -namespace game { -namespace system { - -collision::collision(entity::registry& registry): - updatable(registry) -{ - registry.on_construct().connect<&collision::on_collision_construct>(this); - registry.on_update().connect<&collision::on_collision_update>(this); - registry.on_destroy().connect<&collision::on_collision_destroy>(this); -} - -void collision::update(double t, double dt) -{ - registry.on_construct().disconnect<&collision::on_collision_construct>(this); - registry.on_update().disconnect<&collision::on_collision_update>(this); - registry.on_destroy().disconnect<&collision::on_collision_destroy>(this); -} - -entity::id collision::pick_nearest(const geom::primitive::ray& ray, std::uint32_t flags) const -{ - entity::id nearest_eid = entt::null; - float nearest_distance = std::numeric_limits::infinity(); - - // For each entity with picking and transform components - registry.view().each - ( - [&](entity::id entity_id, const auto& picking, const auto& transform) - { - // Skip entity if picking flags don't match - if (!~(flags | picking.flags)) - return; - - // Transform picking sphere - const geom::primitive::sphere sphere = - { - transform.world * picking.sphere.center, - picking.sphere.radius * math::max(transform.world.scale) - }; - - // Test for intersection between ray and sphere - auto result = geom::primitive::intersection(ray, sphere); - if (result) - { - float t0 = std::get<0>(*result); - float t1 = std::get<1>(*result); - - if (t0 < nearest_distance) - { - nearest_eid = entity_id; - nearest_distance = t0; - } - } - } - ); - - return nearest_eid; -} - -entity::id collision::pick_nearest(const float3& origin, const float3& normal, std::uint32_t flags) const -{ - entity::id nearest_eid = entt::null; - float nearest_sqr_distance = std::numeric_limits::infinity(); - - // Construct picking plane - const geom::primitive::plane picking_plane = geom::primitive::plane(origin, normal); - - // For each entity with picking and transform components - registry.view().each - ( - [&](entity::id entity_id, const auto& picking, const auto& transform) - { - // Skip entity if picking flags don't match - if (!~(flags | picking.flags)) - return; - - // Transform picking sphere center - float3 picking_sphere_center = transform.world * picking.sphere.center; - - // Skip entity if picking sphere center has negative distance from picking plane - if (picking_plane.distance(picking_sphere_center) < 0.0f) - return; - - // Measure distance from picking plane origin to picking sphere center - const float sqr_distance = math::sqr_distance(picking_sphere_center, origin); - - // Check if entity is nearer than the current nearest entity - if (sqr_distance < nearest_sqr_distance) - { - nearest_eid = entity_id; - nearest_sqr_distance = sqr_distance; - } - } - ); - - return nearest_eid; -} - -void collision::on_collision_construct(entity::registry& registry, entity::id entity_id) -{} - -void collision::on_collision_update(entity::registry& registry, entity::id entity_id) -{} - -void collision::on_collision_destroy(entity::registry& registry, entity::id entity_id) -{} - -} // namespace system -} // namespace game diff --git a/src/game/system/collision.hpp b/src/game/system/collision.hpp deleted file mode 100644 index ef37ae9..0000000 --- a/src/game/system/collision.hpp +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_SYSTEM_COLLISION_HPP -#define ANTKEEPER_GAME_SYSTEM_COLLISION_HPP - -#include "game/system/updatable.hpp" -#include "entity/id.hpp" -#include "game/component/collision.hpp" -#include "geom/primitive/ray.hpp" - -namespace game { -namespace system { - -/** - * Maintains a spatially partitioned set of collision meshes. - */ -class collision: public updatable -{ -public: - collision(entity::registry& registry); - virtual void update(double t, double dt); - - /** - * Picks the nearest entity with the specified picking flags that intersects a ray. - * - * @param ray Picking ray. - * @param flags Picking flags. - * - * @return ID of the picked entity, or `entt::null` if no entity was picked. - */ - entity::id pick_nearest(const geom::primitive::ray& ray, std::uint32_t flags) const; - - /** - * Picks the nearest entity with the specified picking flags that has a non-negative distance from a plane. - * - * @param origin Origin of the picking plane. - * @param normal Picking plane normal direction. - * @param flags Picking flags. - * - * @return ID of the picked entity, or `entt::null` if no entity was picked. - */ - entity::id pick_nearest(const float3& origin, const float3& normal, std::uint32_t flags) const; - -private: - void on_collision_construct(entity::registry& registry, entity::id entity_id); - void on_collision_update(entity::registry& registry, entity::id entity_id); - void on_collision_destroy(entity::registry& registry, entity::id entity_id); -}; - -} // namespace system -} // namespace game - -#endif // ANTKEEPER_GAME_SYSTEM_COLLISION_HPP - diff --git a/src/game/system/constraint.cpp b/src/game/system/constraint.cpp deleted file mode 100644 index de7c865..0000000 --- a/src/game/system/constraint.cpp +++ /dev/null @@ -1,356 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "game/system/constraint.hpp" -#include "game/component/constraint-stack.hpp" -#include "math/quaternion.hpp" -#include "math/transform-operators.hpp" - -namespace game { -namespace system { - -constraint::constraint(entity::registry& registry): - updatable(registry) -{ - registry.on_construct().connect<&constraint::on_constraint_stack_update>(this); - registry.on_update().connect<&constraint::on_constraint_stack_update>(this); - registry.on_destroy().connect<&constraint::on_constraint_stack_update>(this); -} - -constraint::~constraint() -{ - registry.on_construct().disconnect<&constraint::on_constraint_stack_update>(this); - registry.on_update().disconnect<&constraint::on_constraint_stack_update>(this); - registry.on_destroy().disconnect<&constraint::on_constraint_stack_update>(this); -} - -void constraint::update(double t, double dt) -{ - // For each entity with transform and constraint stack components - registry.view().each - ( - [&](entity::id transform_eid, auto& transform, auto& stack) - { - // Init world-space transform - transform.world = transform.local; - - // Get entity ID of first constraint - entity::id constraint_eid = stack.head; - - // Consecutively apply constraints - while (registry.valid(constraint_eid)) - { - // Get constraint stack node of the constraint - const component::constraint_stack_node* node = registry.try_get(constraint_eid); - - // Abort if constraint is missing a constraint stack node - if (!node) - break; - - // Apply constraint if enabled - if (node->active) - handle_constraint(transform, constraint_eid, static_cast(dt)); - - // Get entity ID of next constraint in the stack - constraint_eid = node->next; - } - } - ); -} - -void constraint::evaluate(entity::id entity_id) -{ - if (!registry.valid(entity_id)) - return; - - // Get transform and constraint stack components of the entity - const auto [transform, stack] = registry.try_get(entity_id); - - if (!transform || !stack) - return; - - // Init world-space transform - transform->world = transform->local; - - // Get entity ID of first constraint - entity::id constraint_eid = stack->head; - - // Consecutively apply constraints - while (registry.valid(constraint_eid)) - { - // Get constraint stack node of the constraint - const component::constraint_stack_node* node = registry.try_get(constraint_eid); - - // Abort if constraint is missing a constraint stack node - if (!node) - break; - - // Apply constraint if enabled - if (node->active) - handle_constraint(*transform, constraint_eid, 0.0f); - - // Get entity ID of next constraint in the stack - constraint_eid = node->next; - } -} - -void constraint::on_constraint_stack_update(entity::registry& registry, entity::id constraint_stack_eid) -{ - registry.sort - ( - [](const auto& lhs, const auto& rhs) - { - return lhs.priority < rhs.priority; - } - ); -} - -void constraint::handle_constraint(component::transform& transform, entity::id constraint_eid, float dt) -{ - if (auto constraint = registry.try_get(constraint_eid); constraint) - handle_copy_translation_constraint(transform, *constraint); - else if (auto constraint = registry.try_get(constraint_eid); constraint) - handle_copy_rotation_constraint(transform, *constraint); - else if (auto constraint = registry.try_get(constraint_eid); constraint) - handle_copy_scale_constraint(transform, *constraint); - else if (auto constraint = registry.try_get(constraint_eid); constraint) - handle_copy_transform_constraint(transform, *constraint); - else if (auto constraint = registry.try_get(constraint_eid); constraint) - handle_track_to_constraint(transform, *constraint); - else if (auto constraint = registry.try_get(constraint_eid); constraint) - handle_three_dof_constraint(transform, *constraint); - else if (auto constraint = registry.try_get(constraint_eid); constraint) - handle_pivot_constraint(transform, *constraint); - else if (auto constraint = registry.try_get(constraint_eid); constraint) - handle_child_of_constraint(transform, *constraint); - else if (auto constraint = registry.try_get(constraint_eid); constraint) - handle_spring_to_constraint(transform, *constraint, dt); - else if (auto constraint = registry.try_get(constraint_eid); constraint) - handle_spring_translation_constraint(transform, *constraint, dt); - else if (auto constraint = registry.try_get(constraint_eid); constraint) - handle_spring_rotation_constraint(transform, *constraint, dt); - else if (auto constraint = registry.try_get(constraint_eid); constraint) - handle_ease_to_constraint(transform, *constraint, dt); -} - -void constraint::handle_child_of_constraint(component::transform& transform, const component::constraint::child_of& constraint) -{ - if (registry.valid(constraint.target)) - { - const component::transform* target_transform = registry.try_get(constraint.target); - if (target_transform) - { - transform.world = target_transform->world * transform.world; - } - } -} - -void constraint::handle_copy_rotation_constraint(component::transform& transform, const component::constraint::copy_rotation& constraint) -{ - if (registry.valid(constraint.target)) - { - const component::transform* target_transform = registry.try_get(constraint.target); - if (target_transform) - { - transform.world.rotation = target_transform->world.rotation; - } - } -} - -void constraint::handle_copy_scale_constraint(component::transform& transform, const component::constraint::copy_scale& constraint) -{ - if (registry.valid(constraint.target)) - { - const component::transform* target_transform = registry.try_get(constraint.target); - if (target_transform) - { - const auto& target_scale = target_transform->world.scale; - - if (constraint.copy_x) - transform.world.scale.x() = target_scale.x(); - if (constraint.copy_y) - transform.world.scale.y() = target_scale.y(); - if (constraint.copy_z) - transform.world.scale.z() = target_scale.z(); - } - } -} - -void constraint::handle_copy_transform_constraint(component::transform& transform, const component::constraint::copy_transform& constraint) -{ - if (registry.valid(constraint.target)) - { - const component::transform* target_transform = registry.try_get(constraint.target); - if (target_transform) - { - transform.world = target_transform->world; - } - } -} - -void constraint::handle_copy_translation_constraint(component::transform& transform, const component::constraint::copy_translation& constraint) -{ - if (registry.valid(constraint.target)) - { - const component::transform* target_transform = registry.try_get(constraint.target); - if (target_transform) - { - const auto& target_translation = target_transform->world.translation; - - if (constraint.offset) - { - if (constraint.copy_x) - transform.world.translation.x() += (constraint.invert_x) ? -target_translation.x() : target_translation.x(); - if (constraint.copy_y) - transform.world.translation.y() += (constraint.invert_y) ? -target_translation.y() : target_translation.y(); - if (constraint.copy_z) - transform.world.translation.z() += (constraint.invert_z) ? -target_translation.z() : target_translation.z(); - } - else - { - if (constraint.copy_x) - transform.world.translation.x() = (constraint.invert_x) ? -target_translation.x() : target_translation.x(); - if (constraint.copy_y) - transform.world.translation.y() = (constraint.invert_y) ? -target_translation.y() : target_translation.y(); - if (constraint.copy_z) - transform.world.translation.z() = (constraint.invert_z) ? -target_translation.z() : target_translation.z(); - } - } - } -} - -void constraint::handle_ease_to_constraint(component::transform& transform, component::constraint::ease_to& constraint, float dt) -{ - if (constraint.function && registry.valid(constraint.target)) - { - const component::transform* target_transform = registry.try_get(constraint.target); - if (target_transform) - { - if (constraint.t < constraint.duration) - { - const float a = constraint.t / constraint.duration; - transform.world.translation = constraint.function(constraint.start, target_transform->world.translation, a); - } - else - { - transform.world.translation = target_transform->world.translation; - } - - constraint.t += dt; - } - } -} - -void constraint::handle_pivot_constraint(component::transform& transform, const component::constraint::pivot& constraint) -{ - if (registry.valid(constraint.target)) - { - const component::transform* target_transform = registry.try_get(constraint.target); - if (target_transform) - { - // Get pivot center point - const float3 pivot_center = target_transform->world.translation + constraint.offset; - - // Pivot translation - transform.world.translation = pivot_center + transform.world.rotation * (transform.world.translation - pivot_center); - } - } -} - -void constraint::handle_spring_rotation_constraint(component::transform& transform, component::constraint::spring_rotation& constraint, float dt) -{ - // Solve yaw, pitch, and roll angle spring - solve_numeric_spring(constraint.spring, dt); - - // Build yaw, pitch, and roll quaternions - const math::quaternion yaw = math::angle_axis(constraint.spring.x0[0], {0.0f, 1.0f, 0.0f}); - const math::quaternion pitch = math::angle_axis(constraint.spring.x0[1], {-1.0f, 0.0f, 0.0f}); - const math::quaternion roll = math::angle_axis(constraint.spring.x0[2], {0.0f, 0.0f, -1.0f}); - - // Update transform rotation - transform.world.rotation = math::normalize(yaw * pitch * roll); -} - -void constraint::handle_spring_to_constraint(component::transform& transform, component::constraint::spring_to& constraint, float dt) -{ - if (registry.valid(constraint.target)) - { - const component::transform* target_transform = registry.try_get(constraint.target); - if (target_transform) - { - // Spring translation - if (constraint.spring_translation) - { - // Update translation spring target - constraint.translation.x1 = target_transform->world.translation; - - // Solve translation spring - solve_numeric_spring(constraint.translation, dt); - - // Update transform translation - transform.world.translation = constraint.translation.x0; - } - - // Spring rotation - if (constraint.spring_rotation) - { - // Update rotation spring target - constraint.rotation.x1 = float4(target_transform->world.rotation); - - // Solve rotation spring - solve_numeric_spring(constraint.rotation, dt); - - // Update transform rotation - transform.world.rotation = math::normalize(math::quaternion{constraint.rotation.x0[0], constraint.rotation.x0[1], constraint.rotation.x0[2], constraint.rotation.x0[3]}); - } - } - } -} - -void constraint::handle_spring_translation_constraint(component::transform& transform, component::constraint::spring_translation& constraint, float dt) -{ - // Solve translation spring - solve_numeric_spring(constraint.spring, dt); - - // Update transform translation - transform.world.translation = constraint.spring.x0; -} - -void constraint::handle_three_dof_constraint(component::transform& transform, const component::constraint::three_dof& constraint) -{ - const math::quaternion yaw = math::angle_axis(constraint.yaw, {0.0f, 1.0f, 0.0f}); - const math::quaternion pitch = math::angle_axis(constraint.pitch, {-1.0f, 0.0f, 0.0f}); - const math::quaternion roll = math::angle_axis(constraint.roll, {0.0f, 0.0f, -1.0f}); - transform.world.rotation = math::normalize(yaw * pitch * roll); -} - -void constraint::handle_track_to_constraint(component::transform& transform, const component::constraint::track_to& constraint) -{ - if (registry.valid(constraint.target)) - { - const component::transform* target_transform = registry.try_get(constraint.target); - if (target_transform) - { - transform.world.rotation = math::look_rotation(math::normalize(math::sub(target_transform->world.translation, transform.world.translation)), constraint.up); - } - } -} - -} // namespace system -} // namespace game diff --git a/src/game/system/constraint.hpp b/src/game/system/constraint.hpp deleted file mode 100644 index 75b62b3..0000000 --- a/src/game/system/constraint.hpp +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_SYSTEM_CONSTRAINT_HPP -#define ANTKEEPER_GAME_SYSTEM_CONSTRAINT_HPP - -#include "game/system/updatable.hpp" -#include "game/component/transform.hpp" -#include "game/component/constraint/constraint.hpp" -#include "entity/id.hpp" - -namespace game { -namespace system { - -/** - * Applies constraint stacks to transform components. - * - * @see game::component::constraint_stack - * @see game::component::constraint_stack_node - * @see game::component::constraint - */ -class constraint: - public updatable -{ -public: - constraint(entity::registry& registry); - ~constraint(); - - virtual void update(double t, double dt); - - /** - * Manually evaluates an entity's constraints. - * - * @param entity_id ID of a constrained entity. - */ - void evaluate(entity::id entity_id); - -private: - void on_constraint_stack_update(entity::registry& registry, entity::id constraint_stack_eid); - - void handle_constraint(component::transform& transform, entity::id constraint_eid, float dt); - void handle_child_of_constraint(component::transform& transform, const component::constraint::child_of& constraint); - void handle_copy_rotation_constraint(component::transform& transform, const component::constraint::copy_rotation& constraint); - void handle_copy_scale_constraint(component::transform& transform, const component::constraint::copy_scale& constraint); - void handle_copy_transform_constraint(component::transform& transform, const component::constraint::copy_transform& constraint); - void handle_copy_translation_constraint(component::transform& transform, const component::constraint::copy_translation& constraint); - void handle_ease_to_constraint(component::transform& transform, component::constraint::ease_to& constraint, float dt); - void handle_pivot_constraint(component::transform& transform, const component::constraint::pivot& constraint); - void handle_spring_rotation_constraint(component::transform& transform, component::constraint::spring_rotation& constraint, float dt); - void handle_spring_to_constraint(component::transform& transform, component::constraint::spring_to& constraint, float dt); - void handle_spring_translation_constraint(component::transform& transform, component::constraint::spring_translation& constraint, float dt); - void handle_three_dof_constraint(component::transform& transform, const component::constraint::three_dof& constraint); - void handle_track_to_constraint(component::transform& transform, const component::constraint::track_to& constraint); -}; - -} // namespace system -} // namespace game - -#endif // ANTKEEPER_GAME_SYSTEM_CONSTRAINT_HPP diff --git a/src/game/system/locomotion.cpp b/src/game/system/locomotion.cpp deleted file mode 100644 index 7d2e5b8..0000000 --- a/src/game/system/locomotion.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "locomotion.hpp" -#include "game/component/collision.hpp" -#include "game/component/locomotion.hpp" -#include "game/component/transform.hpp" -#include "entity/id.hpp" - -namespace game { -namespace system { - -locomotion::locomotion(entity::registry& registry): - updatable(registry) -{} - -void locomotion::update(double t, double dt) -{ - registry.view().each( - [&](entity::id entity_id, auto& transform, auto& locomotion) - { - }); -} - -} // namespace system -} // namespace game diff --git a/src/game/system/locomotion.hpp b/src/game/system/locomotion.hpp deleted file mode 100644 index 4dedcee..0000000 --- a/src/game/system/locomotion.hpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_SYSTEM_LOCOMOTION_HPP -#define ANTKEEPER_GAME_SYSTEM_LOCOMOTION_HPP - -#include "game/system/updatable.hpp" - -namespace game { -namespace system { - -class locomotion: - public updatable -{ -public: - locomotion(entity::registry& registry); - virtual void update(double t, double dt); -}; - -} // namespace system -} // namespace game - -#endif // ANTKEEPER_GAME_SYSTEM_LOCOMOTION_HPP diff --git a/src/game/system/metamorphosis.cpp b/src/game/system/metamorphosis.cpp deleted file mode 100644 index 9bbb094..0000000 --- a/src/game/system/metamorphosis.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "game/system/metamorphosis.hpp" -#include "entity/id.hpp" - -namespace game { -namespace system { - -metamorphosis::metamorphosis(entity::registry& registry): - updatable(registry), - time_scale(1.0f) -{} - -void metamorphosis::update(double t, double dt) -{ - // registry.view().each( - // [&](entity::id entity_id, auto& component) - // { - // }); - - // registry.view().each( - // [&](entity::id entity_id, auto& component) - // { - // }); -} - -void metamorphosis::set_time_scale(float scale) -{ - time_scale = scale; -} - -} // namespace system -} // namespace game diff --git a/src/game/system/metamorphosis.hpp b/src/game/system/metamorphosis.hpp deleted file mode 100644 index f96824e..0000000 --- a/src/game/system/metamorphosis.hpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_SYSTEM_METAMORPHOSIS_HPP -#define ANTKEEPER_GAME_SYSTEM_METAMORPHOSIS_HPP - -#include "game/system/updatable.hpp" - -namespace game { -namespace system { - -class metamorphosis: - public updatable -{ -public: - metamorphosis(entity::registry& registry); - virtual void update(double t, double dt); - - /** - * Sets the factor by which the timestep `dt` will be scaled. - * - * @param scale Factor by which to scale the timestep. - */ - void set_time_scale(float scale); - -private: - float time_scale; -}; - -} // namespace system -} // namespace game - -#endif // ANTKEEPER_GAME_SYSTEM_METAMORPHOSIS_HPP diff --git a/src/game/system/morphogenesis.cpp b/src/game/system/morphogenesis.cpp deleted file mode 100644 index c14e2a4..0000000 --- a/src/game/system/morphogenesis.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "game/system/morphogenesis.hpp" -#include "entity/id.hpp" - -namespace game { -namespace system { - -morphogenesis::morphogenesis(entity::registry& registry): - updatable(registry) -{} - -void morphogenesis::update(double t, double dt) -{} - -} // namespace system -} // namespace game diff --git a/src/game/system/morphogenesis.hpp b/src/game/system/morphogenesis.hpp deleted file mode 100644 index bc8d017..0000000 --- a/src/game/system/morphogenesis.hpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_SYSTEM_MORPHOGENESIS_HPP -#define ANTKEEPER_GAME_SYSTEM_MORPHOGENESIS_HPP - -#include "game/system/updatable.hpp" - -namespace game { -namespace system { - -/** - * Generates 3D models from genomes. - */ -class morphogenesis: - public updatable -{ -public: - morphogenesis(entity::registry& registry); - virtual void update(double t, double dt); - -private: -}; - -} // namespace system -} // namespace game - -#endif // ANTKEEPER_GAME_SYSTEM_MORPHOGENESIS_HPP diff --git a/src/game/system/orbit.cpp b/src/game/system/orbit.cpp deleted file mode 100644 index f6d2428..0000000 --- a/src/game/system/orbit.cpp +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "game/system/orbit.hpp" -#include "physics/orbit/orbit.hpp" - -namespace game { -namespace system { - -orbit::orbit(entity::registry& registry): - updatable(registry), - ephemeris(nullptr), - time(0.0), - time_scale(1.0) -{ - registry.on_construct().connect<&orbit::on_orbit_construct>(this); - registry.on_update().connect<&orbit::on_orbit_update>(this); -} - -orbit::~orbit() -{ - registry.on_construct().disconnect<&orbit::on_orbit_construct>(this); - registry.on_update().disconnect<&orbit::on_orbit_update>(this); -} - -void orbit::update(double t, double dt) -{ - // Add scaled timestep to current time - set_time(time + dt * time_scale); - - if (!ephemeris) - return; - - // Calculate positions of ephemeris items, in meters - for (int i: ephemeris_indices) - positions[i] = (*ephemeris)[i].position(time) * 1000.0; - - // Propagate orbits - registry.view().each( - [&](entity::id entity_eid, auto& orbit) - { - orbit.position = positions[orbit.ephemeris_index] * orbit.scale; - - entity::id parent_id = orbit.parent; - while (parent_id != entt::null) - { - const component::orbit& parent_orbit = registry.get(parent_id); - orbit.position += positions[parent_orbit.ephemeris_index] * parent_orbit.scale; - parent_id = parent_orbit.parent; - } - }); -} - -void orbit::set_ephemeris(const physics::orbit::ephemeris* ephemeris) -{ - this->ephemeris = ephemeris; - positions.resize((ephemeris) ? ephemeris->size() : 0); -} - -void orbit::set_time(double time) -{ - this->time = time; -} - -void orbit::set_time_scale(double scale) -{ - time_scale = scale; -} - -void orbit::on_orbit_construct(entity::registry& registry, entity::id entity_id) -{ - const game::component::orbit& component = registry.get(entity_id); - ephemeris_indices.insert(component.ephemeris_index); -} - -void orbit::on_orbit_update(entity::registry& registry, entity::id entity_id) -{ - const game::component::orbit& component = registry.get(entity_id); - ephemeris_indices.insert(component.ephemeris_index); -} - -} // namespace system -} // namespace game diff --git a/src/game/system/orbit.hpp b/src/game/system/orbit.hpp deleted file mode 100644 index 73cf64a..0000000 --- a/src/game/system/orbit.hpp +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_SYSTEM_SOLAR_HPP -#define ANTKEEPER_GAME_SYSTEM_SOLAR_HPP - -#include "game/system/updatable.hpp" -#include "utility/fundamental-types.hpp" -#include "entity/id.hpp" -#include "game/component/orbit.hpp" -#include "physics/orbit/ephemeris.hpp" -#include - -namespace game { -namespace system { - -/** - * Updates the Cartesian position and velocity of orbiting bodies given their Keplerian orbital elements and the current time. - */ -class orbit: - public updatable -{ -public: - orbit(entity::registry& registry); - ~orbit(); - - /** - * Scales then adds the timestep `dt` to the current time, then recalculates the positions of orbiting bodies. - * - * @param t Time, in seconds. - * @param dt Delta time, in seconds. - */ - virtual void update(double t, double dt); - - /** - * Sets the current time. - * - * @param time Time, in days. - */ - void set_time(double time); - - /** - * Sets the factor by which the timestep `dt` will be scaled before being added to the current time. - * - * @param scale Factor by which to scale the timestep. - */ - void set_time_scale(double scale); - - /** - * Sets the ephemeris used to calculate orbital positions. - * - * @param ephemeris Ephemeris. - */ - void set_ephemeris(const physics::orbit::ephemeris* ephemeris); - -private: - void on_orbit_construct(entity::registry& registry, entity::id entity_id); - void on_orbit_update(entity::registry& registry, entity::id entity_id); - - const physics::orbit::ephemeris* ephemeris; - double time; - double time_scale; - std::vector positions; - std::unordered_set ephemeris_indices; -}; - -} // namespace system -} // namespace game - -#endif // ANTKEEPER_GAME_SYSTEM_SOLAR_HPP diff --git a/src/game/system/render.cpp b/src/game/system/render.cpp deleted file mode 100644 index 445ecdc..0000000 --- a/src/game/system/render.cpp +++ /dev/null @@ -1,296 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "render.hpp" -#include "game/component/transform.hpp" -#include "game/component/camera.hpp" -#include "scene/point-light.hpp" -#include "scene/directional-light.hpp" -#include "scene/ambient-light.hpp" -#include "scene/spot-light.hpp" - -namespace game { -namespace system { - -render::render(entity::registry& registry): - updatable(registry), - t(0.0), - dt(0.0), - renderer(nullptr) -{ - registry.on_construct().connect<&render::on_model_construct>(this); - registry.on_update().connect<&render::on_model_update>(this); - registry.on_destroy().connect<&render::on_model_destroy>(this); - registry.on_construct().connect<&render::on_light_construct>(this); - registry.on_update().connect<&render::on_light_update>(this); - registry.on_destroy().connect<&render::on_light_destroy>(this); -} - -render::~render() -{ - registry.on_construct().disconnect<&render::on_model_construct>(this); - registry.on_update().disconnect<&render::on_model_update>(this); - registry.on_destroy().disconnect<&render::on_model_destroy>(this); - registry.on_construct().disconnect<&render::on_light_construct>(this); - registry.on_update().disconnect<&render::on_light_update>(this); - registry.on_destroy().disconnect<&render::on_light_destroy>(this); -} - -void render::update(double t, double dt) -{ - this->t = t; - this->dt = dt; - - // Update model instance transforms - registry.view().each - ( - [this](entity::id entity_id, auto& transform, auto& model) - { - scene::model_instance* instance = model_instances[entity_id]; - - instance->set_transform(transform.world); - if (transform.warp) - { - instance->get_transform_tween().update(); - instance->update_tweens(); - transform.warp = false; - } - } - ); - - // Update camera transforms - registry.view().each - ( - [this](entity::id entity_id, auto& transform, auto& camera) - { - camera.object->set_transform(transform.world); - if (transform.warp) - { - camera.object->get_transform_tween().update(); - camera.object->update_tweens(); - transform.warp = false; - } - } - ); - - // Update light transforms - registry.view().each - ( - [this](entity::id entity_id, auto& transform, auto& light) - { - scene::light* light_object = lights[entity_id]; - - light_object->set_transform(transform.world); - if (transform.warp) - { - light_object->get_transform_tween().update(); - light_object->update_tweens(); - transform.warp = false; - } - } - ); -} - -void render::draw(double alpha) -{ - if (renderer) - { - for (const scene::collection* collection: layers) - { - renderer->render(static_cast(t + dt * alpha), static_cast(dt), static_cast(alpha), *collection); - } - } -} - -void render::add_layer(scene::collection* layer) -{ - layers.push_back(layer); -} - -void render::remove_layers() -{ - layers.clear(); -} - -void render::set_renderer(::render::renderer* renderer) -{ - this->renderer = renderer; -} - -scene::model_instance* render::get_model_instance(entity::id entity_id) -{ - if (auto it = model_instances.find(entity_id); it != model_instances.end()) - return it->second; - return nullptr; -} - -scene::light* render::get_light(entity::id entity_id) -{ - if (auto it = lights.find(entity_id); it != lights.end()) - return it->second; - return nullptr; -} - -void render::update_model_and_materials(entity::id entity_id, component::model& model) -{ - if (auto model_it = model_instances.find(entity_id); model_it != model_instances.end()) - { - model_it->second->set_model(model.render_model); - model_it->second->set_instanced((model.instance_count > 0), model.instance_count); - - for (auto material_it = model.materials.begin(); material_it != model.materials.end(); ++material_it) - { - model_it->second->set_material(material_it->first, material_it->second); - } - - // Add model instance to its specified layers - for (std::size_t i = 0; i < std::min(layers.size(), (sizeof(model.layers) << 3)); ++i) - { - layers[i]->remove_object(model_it->second); - - if ((model.layers >> i) & 1) - { - layers[i]->add_object(model_it->second); - } - } - } -} - -void render::update_light(entity::id entity_id, game::component::light& component) -{ - if (auto light_it = lights.find(entity_id); light_it != lights.end()) - { - scene::light* light = light_it->second; - - light->set_color(component.color); - light->set_intensity(component.intensity); - - switch (light->get_light_type()) - { - case scene::light_type::point: - { - scene::point_light* point = static_cast(light); - point->set_attenuation(component.attenuation); - break; - } - - case scene::light_type::spot: - { - scene::spot_light* spot = static_cast(light); - spot->set_attenuation(component.attenuation); - spot->set_cutoff(component.cutoff); - break; - } - - default: - break; - } - } -} - -void render::on_model_construct(entity::registry& registry, entity::id entity_id) -{ - game::component::model& component = registry.get(entity_id); - - scene::model_instance* model_instance = new scene::model_instance(); - model_instances[entity_id] = model_instance; - update_model_and_materials(entity_id, component); -} - -void render::on_model_update(entity::registry& registry, entity::id entity_id) -{ - game::component::model& component = registry.get(entity_id); - update_model_and_materials(entity_id, component); -} - -void render::on_model_destroy(entity::registry& registry, entity::id entity_id) -{ - if (auto it = model_instances.find(entity_id); it != model_instances.end()) - { - scene::model_instance* model_instance = it->second; - - // Remove model instance from all layers - for (scene::collection* layer: layers) - layer->remove_object(model_instance); - - model_instances.erase(it); - delete model_instance; - } -} - -void render::on_light_construct(entity::registry& registry, entity::id entity_id) -{ - game::component::light& component = registry.get(entity_id); - - scene::light* light = nullptr; - - switch (component.type) - { - case scene::light_type::ambient: - light = new scene::ambient_light(); - break; - - case scene::light_type::directional: - light = new scene::directional_light(); - break; - - case scene::light_type::point: - light = new scene::point_light(); - break; - - case scene::light_type::spot: - light = new scene::spot_light(); - break; - - default: - break; - } - - if (light) - { - lights[entity_id] = light; - for (scene::collection* layer: layers) - layer->add_object(light); - - update_light(entity_id, component); - } -} - -void render::on_light_update(entity::registry& registry, entity::id entity_id) -{ - game::component::light& component = registry.get(entity_id); - update_light(entity_id, component); -} - -void render::on_light_destroy(entity::registry& registry, entity::id entity_id) -{ - if (auto it = lights.find(entity_id); it != lights.end()) - { - scene::light* light = it->second; - - for (scene::collection* layer: layers) - layer->remove_object(light); - - lights.erase(it); - delete light; - } -} - -} // namespace system -} // namespace game diff --git a/src/game/system/render.hpp b/src/game/system/render.hpp deleted file mode 100644 index d122810..0000000 --- a/src/game/system/render.hpp +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_SYSTEM_RENDER_HPP -#define ANTKEEPER_GAME_SYSTEM_RENDER_HPP - -#include "game/system/updatable.hpp" -#include "scene/collection.hpp" -#include "scene/model-instance.hpp" -#include "scene/light.hpp" -#include "game/component/model.hpp" -#include "game/component/light.hpp" -#include "entity/id.hpp" -#include "render/renderer.hpp" -#include -#include - -namespace game { -namespace system { - -class render: public updatable -{ -public: - render(entity::registry& registry); - ~render(); - - virtual void update(double t, double dt); - - void draw(double alpha); - - - void add_layer(scene::collection* layer); - void remove_layers(); - - void set_renderer(::render::renderer* renderer); - - scene::model_instance* get_model_instance(entity::id entity_id); - scene::light* get_light(entity::id entity_id); - -private: - void update_model_and_materials(entity::id entity_id, game::component::model& model); - void update_light(entity::id entity_id, game::component::light& component); - - void on_model_construct(entity::registry& registry, entity::id entity_id); - void on_model_update(entity::registry& registry, entity::id entity_id); - void on_model_destroy(entity::registry& registry, entity::id entity_id); - void on_light_construct(entity::registry& registry, entity::id entity_id); - void on_light_update(entity::registry& registry, entity::id entity_id); - void on_light_destroy(entity::registry& registry, entity::id entity_id); - - double t; - double dt; - ::render::renderer* renderer; - std::vector layers; - std::unordered_map model_instances; - std::unordered_map lights; -}; - -} // namespace system -} // namespace game - -#endif // ANTKEEPER_GAME_SYSTEM_RENDER_HPP - diff --git a/src/game/system/spatial.cpp b/src/game/system/spatial.cpp deleted file mode 100644 index e6941e1..0000000 --- a/src/game/system/spatial.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "spatial.hpp" -#include "game/component/transform.hpp" -#include "game/component/constraint-stack.hpp" - -namespace game { -namespace system { - -spatial::spatial(entity::registry& registry): - updatable(registry), - updated_unconstrained_transforms(registry, entt::collector.update().where(entt::exclude)) -{} - -void spatial::update(double t, double dt) -{ - // Update world-space transforms of all updated, unconstrained transforms - for (const auto transform_eid: updated_unconstrained_transforms) - { - auto& transform = registry.get(transform_eid); - transform.world = transform.local; - } - updated_unconstrained_transforms.clear(); -} - -} // namespace system -} // namespace game diff --git a/src/game/system/spatial.hpp b/src/game/system/spatial.hpp deleted file mode 100644 index 87c673f..0000000 --- a/src/game/system/spatial.hpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_SYSTEM_SPATIAL_HPP -#define ANTKEEPER_GAME_SYSTEM_SPATIAL_HPP - -#include "game/system/updatable.hpp" -#include - -namespace game { -namespace system { - -class spatial: - public updatable -{ -public: - spatial(entity::registry& registry); - virtual void update(double t, double dt); - -private: - /// Observes entities with updated, unconstrained transforms. - entt::observer updated_unconstrained_transforms; -}; - -} // namespace system -} // namespace game - -#endif // ANTKEEPER_GAME_SYSTEM_SPATIAL_HPP diff --git a/src/game/system/spring.cpp b/src/game/system/spring.cpp deleted file mode 100644 index 01c5ebc..0000000 --- a/src/game/system/spring.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#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 deleted file mode 100644 index 42f7a7b..0000000 --- a/src/game/system/spring.hpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_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/game/system/steering.cpp b/src/game/system/steering.cpp deleted file mode 100644 index 6ed1af1..0000000 --- a/src/game/system/steering.cpp +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "game/system/steering.hpp" -#include "game/component/steering.hpp" -#include "game/component/transform.hpp" -#include "entity/id.hpp" -#include "ai/steering/behavior/wander.hpp" -#include "ai/steering/behavior/seek.hpp" -#include "math/quaternion.hpp" -#include "config.hpp" - -namespace game { -namespace system { - -steering::steering(entity::registry& registry): - updatable(registry) -{} - -void steering::update(double t, double dt) -{ - registry.view().each - ( - [&](entity::id entity_id, auto& steering, auto& transform) - { - auto& agent = steering.agent; - - // Update agent orientation - agent.orientation = transform.local.rotation; - - // Accumulate forces - float3 force = {0, 0, 0}; - if (steering.wander_weight) - { - //force += ai::steering::behavior::wander_2d(agent, steering.wander_noise * dt, steering.wander_distance, steering.wander_radius, steering.wander_angle) * steering.wander_weight; - force += ai::steering::behavior::wander_3d(agent, steering.wander_noise * dt, steering.wander_distance, steering.wander_radius, steering.wander_angle, steering.wander_angle2) * steering.wander_weight; - } - if (steering.seek_weight) - { - force += ai::steering::behavior::seek(agent, steering.seek_target) * steering.seek_weight; - } - - // Normalize force - if (steering.sum_weights) - force /= steering.sum_weights; - - // Accelerate - agent.acceleration = force / agent.mass; - agent.velocity += agent.acceleration * static_cast(dt); - - // Limit speed - const float speed_squared = math::sqr_length(agent.velocity); - if (speed_squared > agent.max_speed_squared) - { - const float speed = std::sqrt(speed_squared); - agent.velocity = (agent.velocity / speed) * agent.max_speed; - } - - // Move agent - agent.position += agent.velocity * static_cast(dt); - - // Rotate agent - if (speed_squared) - { - agent.orientation = math::look_rotation(agent.velocity / std::sqrt(speed_squared), agent.up); - agent.forward = agent.orientation * config::global_forward; - agent.up = agent.orientation * config::global_up; - } - - // Update transform - registry.patch - ( - entity_id, - [&agent](auto& component) - { - component.local.translation = agent.position; - component.local.rotation = agent.orientation; - } - ); - } - ); -} - -} // namespace system -} // namespace game diff --git a/src/game/system/steering.hpp b/src/game/system/steering.hpp deleted file mode 100644 index d391338..0000000 --- a/src/game/system/steering.hpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_SYSTEM_STEERING_HPP -#define ANTKEEPER_GAME_SYSTEM_STEERING_HPP - -#include "game/system/updatable.hpp" - -namespace game { -namespace system { - -class steering: - public updatable -{ -public: - steering(entity::registry& registry); - virtual void update(double t, double dt); -}; - -} // namespace system -} // namespace game - -#endif // ANTKEEPER_GAME_SYSTEM_STEERING_HPP diff --git a/src/game/system/subterrain.cpp b/src/game/system/subterrain.cpp deleted file mode 100644 index 17e7964..0000000 --- a/src/game/system/subterrain.cpp +++ /dev/null @@ -1,513 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "subterrain.hpp" -#include "game/component/model.hpp" -#include "game/component/cavity.hpp" -#include "entity/id.hpp" -#include "render/model.hpp" -#include "render/material.hpp" -#include "geom/mesh-functions.hpp" -#include "render/vertex-attribute.hpp" -#include "gl/vertex-attribute.hpp" -#include "gl/drawing-mode.hpp" -#include "gl/vertex-buffer.hpp" -#include "resources/resource-manager.hpp" -#include "geom/marching-cubes.hpp" -#include "geom/intersection.hpp" -#include "utility/fundamental-types.hpp" -#include -#include - -namespace game { -namespace system { - -/** - * An octree containing cubes for the marching cubes algorithm. - */ -struct cube_tree -{ -public: - cube_tree(const geom::aabb& bounds, int max_depth); - ~cube_tree(); - - const bool is_leaf() const; - const geom::aabb& get_bounds() const; - - /// Subdivides all nodes intersecting with a region to the max depth. - void subdivide_max(const geom::aabb& region); - - /// Fills a list with all leaf nodes that intersect with a region. - void query_leaves(std::list& nodes, const geom::aabb& region); - void visit_leaves(const geom::aabb& region, const std::function& f); - - /// Counts then number of nodes in the octree. - std::size_t size() const; - - cube_tree* children[8]; - float3 corners[8]; - float distances[8]; - const int max_depth; - const int depth; - const geom::aabb bounds; - -private: - cube_tree(const geom::aabb& bounds, int max_depth, int depth); - void subdivide(); -}; - -cube_tree::cube_tree(const geom::aabb& bounds, int max_depth): - cube_tree(bounds, max_depth, 0) -{} - -cube_tree::cube_tree(const geom::aabb& bounds, int max_depth, int depth): - bounds(bounds), - max_depth(max_depth), - depth(depth) -{ - corners[0] = {bounds.min_point.x(), bounds.min_point.y(), bounds.min_point.z()}; - corners[1] = {bounds.max_point.x(), bounds.min_point.y(), bounds.min_point.z()}; - corners[2] = {bounds.max_point.x(), bounds.max_point.y(), bounds.min_point.z()}; - corners[3] = {bounds.min_point.x(), bounds.max_point.y(), bounds.min_point.z()}; - corners[4] = {bounds.min_point.x(), bounds.min_point.y(), bounds.max_point.z()}; - corners[5] = {bounds.max_point.x(), bounds.min_point.y(), bounds.max_point.z()}; - corners[6] = {bounds.max_point.x(), bounds.max_point.y(), bounds.max_point.z()}; - corners[7] = {bounds.min_point.x(), bounds.max_point.y(), bounds.max_point.z()}; - - for (int i = 0; i < 8; ++i) - { - children[i] = nullptr; - distances[i] = -std::numeric_limits::infinity(); - - // For outside normals - //distances[i] = std::numeric_limits::infinity(); - } -} - -cube_tree::~cube_tree() -{ - for (cube_tree* child: children) - delete child; -} - -void cube_tree::subdivide_max(const geom::aabb& region) -{ - if (depth != max_depth && aabb_aabb_intersection(bounds, region)) - { - if (is_leaf()) - subdivide(); - - for (cube_tree* child: children) - child->subdivide_max(region); - } -} - -void cube_tree::query_leaves(std::list& nodes, const geom::aabb& region) -{ - if (aabb_aabb_intersection(bounds, region)) - { - if (is_leaf()) - { - nodes.push_back(this); - } - else - { - for (cube_tree* child: children) - child->query_leaves(nodes, region); - } - } -} - -void cube_tree::visit_leaves(const geom::aabb& region, const std::function& f) -{ - if (aabb_aabb_intersection(bounds, region)) - { - if (is_leaf()) - { - f(*this); - } - else - { - for (cube_tree* child: children) - child->visit_leaves(region, f); - } - } -} - -std::size_t cube_tree::size() const -{ - std::size_t node_count = 1; - if (!is_leaf()) - { - for (cube_tree* child: children) - node_count += child->size(); - } - - return node_count; -} - -inline const bool cube_tree::is_leaf() const -{ - return (children[0] == nullptr); -} - -inline const geom::aabb& cube_tree::get_bounds() const -{ - return bounds; -} - -void cube_tree::subdivide() -{ - const float3 center = (bounds.min_point + bounds.max_point) * 0.5f; - - for (int i = 0; i < 8; ++i) - { - geom::aabb child_bounds; - for (int j = 0; j < 3; ++j) - { - child_bounds.min_point[j] = std::min(corners[i][j], center[j]); - child_bounds.max_point[j] = std::max(corners[i][j], center[j]); - } - - children[i] = new cube_tree(child_bounds, max_depth, depth + 1); - } -} - -subterrain::subterrain(entity::registry& registry, ::resource_manager* resource_manager): - updatable(registry), - resource_manager(resource_manager) -{ - - // Load subterrain materials - subterrain_inside_material = nullptr;//resource_manager->load<::render::material>("subterrain-inside.mtl"); - subterrain_outside_material = nullptr;//resource_manager->load<::render::material>("subterrain-outside.mtl"); - - // Allocate subterrain model - subterrain_model = new ::render::model(); - - // Create inside model group - subterrain_inside_group = subterrain_model->add_group("inside"); - subterrain_inside_group->set_material(subterrain_inside_material); - subterrain_inside_group->set_drawing_mode(gl::drawing_mode::triangles); - subterrain_inside_group->set_start_index(0); - subterrain_inside_group->set_index_count(0); - - // Create outside model group - subterrain_outside_group = subterrain_model->add_group("outside"); - subterrain_outside_group->set_material(subterrain_outside_material); - subterrain_outside_group->set_drawing_mode(gl::drawing_mode::triangles); - subterrain_outside_group->set_start_index(0); - subterrain_outside_group->set_index_count(0); - - // Determine vertex size (position, normal, barycentric) - subterrain_model_vertex_size = 3 + 3 + 3; - subterrain_model_vertex_stride = subterrain_model_vertex_size * sizeof(float); - - // Get model VBO and VAO - gl::vertex_buffer* vbo = subterrain_model->get_vertex_buffer(); - gl::vertex_array* vao = subterrain_model->get_vertex_array(); - - std::size_t attribute_offset = 0; - - // Define position vertex attribute - gl::vertex_attribute position_attribute; - position_attribute.buffer = vbo; - position_attribute.offset = attribute_offset; - position_attribute.stride = subterrain_model_vertex_stride; - position_attribute.type = gl::vertex_attribute_type::float_32; - position_attribute.components = 3; - attribute_offset += position_attribute.components * sizeof(float); - - // Define normal vertex attribute - gl::vertex_attribute normal_attribute; - normal_attribute.buffer = vbo; - normal_attribute.offset = attribute_offset; - normal_attribute.stride = subterrain_model_vertex_stride; - normal_attribute.type = gl::vertex_attribute_type::float_32; - normal_attribute.components = 3; - attribute_offset += normal_attribute.components * sizeof(float); - - // Define barycentric vertex attribute - gl::vertex_attribute barycentric_attribute; - barycentric_attribute.buffer = vbo; - barycentric_attribute.offset = attribute_offset; - barycentric_attribute.stride = subterrain_model_vertex_stride; - barycentric_attribute.type = gl::vertex_attribute_type::float_32; - barycentric_attribute.components = 3; - attribute_offset += barycentric_attribute.components * sizeof(float); - - // Bind vertex attributes to VAO - vao->bind(::render::vertex_attribute::position, position_attribute); - vao->bind(::render::vertex_attribute::normal, normal_attribute); - vao->bind(::render::vertex_attribute::barycentric, barycentric_attribute); - - // Calculate adjusted bounds to fit isosurface resolution - //isosurface_resolution = 0.325f; - isosurface_resolution = 0.5f; - float ideal_volume_size = 200.0f; - int octree_depth = std::floor(std::log(ideal_volume_size / isosurface_resolution) / std::log(2)) + 1; - float adjusted_volume_size = std::pow(2.0f, octree_depth) * isosurface_resolution; - - // Set subterrain bounds - subterrain_bounds.min_point = float3{-0.5f, -1.0f, -0.5f} * adjusted_volume_size; - subterrain_bounds.max_point = float3{ 0.5f, 0.0f, 0.5f} * adjusted_volume_size; - - // Set subterrain model bounds - subterrain_model->set_bounds(subterrain_bounds); - - // Allocate cube tree - cube_tree = new game::system::cube_tree(subterrain_bounds, octree_depth); - - // Allocate mesh - subterrain_mesh = new geom::mesh(); - - first_run = true; -} - -subterrain::~subterrain() -{ - delete subterrain_model; - delete subterrain_mesh; -} - -void subterrain::update(double t, double dt) -{ - if (first_run) - { - first_run = false; - //auto subterrain_entity = registry.create(); - //registry.assign(subterrain_entity, subterrain_model); - - subterrain_model_instance = new scene::model_instance(subterrain_model); - collection->add_object(subterrain_model_instance); - } - - bool digging = false; - - registry.view().each( - [this, &digging](entity::id entity_id, auto& cavity) - { - this->dig(cavity.position, cavity.radius); - this->registry.destroy(entity_id); - - digging = true; - }); - - if (digging) - { - - //std::cout << "regenerating subterrain mesh...\n"; - regenerate_subterrain_mesh(); - //std::cout << "regenerating subterrain mesh... done\n"; - - //std::cout << "regenerating subterrain model...\n"; - regenerate_subterrain_model(); - //std::cout << "regenerating subterrain model... done\n"; - } -} - -void subterrain::set_scene(scene::collection* collection) -{ - this->collection = collection; -} - -void subterrain::regenerate_subterrain_mesh() -{ - delete subterrain_mesh; - subterrain_mesh = new geom::mesh(); - subterrain_vertices.clear(); - subterrain_triangles.clear(); - subterrain_vertex_map.clear(); - - //std::cout << "marching...\n"; - merged = 0; - march(cube_tree); - //std::cout << "merged " << merged << " vertices\n"; - //std::cout << "marching...done\n"; - - //std::cout << "vertex count: " << subterrain_vertices.size() << std::endl; - //std::cout << "triangle count: " << subterrain_triangles.size() << std::endl; - - //std::cout << "creating mesh...\n"; - create_triangle_mesh(*subterrain_mesh, subterrain_vertices, subterrain_triangles); - //std::cout << "creating mesh... done\n"; -} - -void subterrain::march(game::system::cube_tree* node) -{ - if (!node->is_leaf()) - { - for (game::system::cube_tree* child: node->children) - march(child); - return; - } - else if (node->depth != node->max_depth) - { - return; - } - - // Get node bounds - const geom::aabb& bounds = node->get_bounds(); - - // Polygonize cube - float vertex_buffer[12 * 3]; - std::uint_fast8_t vertex_count; - std::int_fast8_t triangle_buffer[5 * 3]; - std::uint_fast8_t triangle_count; - const float* corners = &node->corners[0][0]; - const float* distances = &node->distances[0]; - geom::mc::polygonize(vertex_buffer, &vertex_count, triangle_buffer, &triangle_count, corners, distances); - - // Remap local vertex buffer indices (0-11) to mesh vertex indices - std::uint_fast32_t vertex_remap[12]; - for (int i = 0; i < vertex_count; ++i) - { - const float3& vertex = reinterpret_cast(vertex_buffer[i * 3]); - - if (auto it = subterrain_vertex_map.find(vertex); it != subterrain_vertex_map.end()) - { - vertex_remap[i] = it->second; - ++merged; - } - else - { - vertex_remap[i] = subterrain_vertices.size(); - subterrain_vertex_map[vertex] = subterrain_vertices.size(); - subterrain_vertices.push_back(vertex); - } - } - - // Add triangles - for (std::uint_fast32_t i = 0; i < triangle_count; ++i) - { - subterrain_triangles.push_back( - { - vertex_remap[triangle_buffer[i * 3]], - vertex_remap[triangle_buffer[i * 3 + 1]], - vertex_remap[triangle_buffer[i * 3 + 2]] - }); - } -} - -void subterrain::regenerate_subterrain_model() -{ - float3* face_normals = new float3[subterrain_mesh->get_faces().size()]; - calculate_face_normals(face_normals, *subterrain_mesh); - - static const float3 barycentric_coords[3] = - { - float3{1, 0, 0}, - float3{0, 1, 0}, - float3{0, 0, 1} - }; - - float* vertex_data = new float[subterrain_model_vertex_size * subterrain_mesh->get_faces().size() * 3]; - float* v = vertex_data; - for (std::size_t i = 0; i < subterrain_mesh->get_faces().size(); ++i) - { - geom::mesh::face* face = subterrain_mesh->get_faces()[i]; - geom::mesh::edge* ab = face->edge; - geom::mesh::edge* bc = face->edge->next; - geom::mesh::edge* ca = face->edge->previous; - geom::mesh::vertex* a = ab->vertex; - geom::mesh::vertex* b = bc->vertex; - geom::mesh::vertex* c = ca->vertex; - geom::mesh::vertex* vertices[3] = {a, b, c}; - - for (std::size_t j = 0; j < 3; ++j) - { - geom::mesh::vertex* vertex = vertices[j]; - - float3 n = {0, 0, 0}; - geom::mesh::edge* start = vertex->edge; - geom::mesh::edge* edge = start; - do - { - if (edge->face) - { - n += face_normals[edge->face->index]; - } - - edge = edge->previous->symmetric; - } - while (edge != start); - n = math::normalize(n); - - //float3 n = reinterpret_cast(face_normals[i * 3]); - - *(v++) = vertex->position[0]; - *(v++) = vertex->position[1]; - *(v++) = vertex->position[2]; - - *(v++) = n[0]; - *(v++) = n[1]; - *(v++) = n[2]; - - *(v++) = barycentric_coords[j][0]; - *(v++) = barycentric_coords[j][1]; - *(v++) = barycentric_coords[j][2]; - } - } - - // Resized VBO and upload vertex data - gl::vertex_buffer* vbo = subterrain_model->get_vertex_buffer(); - vbo->resize(subterrain_mesh->get_faces().size() * 3 * subterrain_model_vertex_stride, vertex_data); - - // Deallocate vertex data - delete[] face_normals; - delete[] vertex_data; - - // Update model groups - subterrain_inside_group->set_index_count(subterrain_mesh->get_faces().size() * 3); - subterrain_outside_group->set_index_count(subterrain_mesh->get_faces().size() * 3); -} - -void subterrain::dig(const float3& position, float radius) -{ - // Construct region containing the cavity sphere - geom::aabb region = {position, position}; - for (int i = 0; i < 3; ++i) - { - region.min_point[i] -= radius + isosurface_resolution; - region.max_point[i] += radius + isosurface_resolution; - } - - // Subdivide the octree to the maximum depth within the region - cube_tree->subdivide_max(region); - - // Query all octree leaf nodes within the region - std::list nodes; - cube_tree->visit_leaves(region, - [&position, radius](game::system::cube_tree& node) - { - for (int i = 0; i < 8; ++i) - { - // For outside normals (also set node initial distance to +infinity) - //float distance = math::length(node->corners[i] - position) - radius; - // if (distance < node->distances[i]) - - float distance = radius - math::length(node.corners[i] - position); - if (distance > node.distances[i]) - node.distances[i] = distance; - } - }); -} - -} // namespace system -} // namespace game diff --git a/src/game/system/subterrain.hpp b/src/game/system/subterrain.hpp deleted file mode 100644 index 8b842a1..0000000 --- a/src/game/system/subterrain.hpp +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_SYSTEM_SUBTERRAIN_HPP -#define ANTKEEPER_GAME_SYSTEM_SUBTERRAIN_HPP - -#include "game/system/updatable.hpp" -#include "geom/mesh.hpp" -#include "geom/aabb.hpp" -#include "scene/collection.hpp" -#include "scene/model-instance.hpp" -#include "render/model.hpp" -#include "utility/fundamental-types.hpp" -#include - -class resource_manager; - -namespace game { -namespace system { - -struct cube_tree; - -template -struct epsilon -{ - static const double value; -}; - -template -const double epsilon::value = static_cast(Mantissa) * std::pow(10.0, Exponent); - -typedef epsilon<1, -5> epsilon_1en5; - -template -struct vector_hasher -{ - typedef math::vector vector_type; - - std::size_t operator()(const vector_type& v) const noexcept - { - static const T inverse_epsilon = T(1) / Epsilon::value; - - std::size_t hash = 0; - for (std::size_t i = 0; i < N; ++i) - { - std::int64_t j = static_cast(v[i] * inverse_epsilon); - hash ^= std::hash()(j) + 0x9e3779b9 + (hash << 6) + (hash >> 2); - } - - return hash; - } -}; - -template -struct vector_equals -{ - typedef math::vector vector_type; - - bool operator()(const vector_type& a, const vector_type& b) const noexcept - { - for (std::size_t i = 0; i < N; ++i) - { - if (std::fabs(b[i] - a[i]) >= Epsilon::value) - return false; - } - - return true; - } -}; - -class subterrain: public updatable -{ -public: - subterrain(entity::registry& registry, ::resource_manager* resource_manager); - ~subterrain(); - virtual void update(double t, double dt); - - void set_scene(scene::collection* collection); - -private: - void regenerate_subterrain_mesh(); - void march(cube_tree* node); - void regenerate_subterrain_model(); - void dig(const float3&position, float radius); - float distance(const cube_tree& node, const float3& sample) const; - - resource_manager* resource_manager; - geom::mesh* subterrain_mesh; - ::render::model* subterrain_model; - ::render::material* subterrain_inside_material; - ::render::material* subterrain_outside_material; - ::render::model_group* subterrain_inside_group; - ::render::model_group* subterrain_outside_group; - int subterrain_model_vertex_size; - int subterrain_model_vertex_stride; - geom::aabb subterrain_bounds; - cube_tree* cube_tree; - std::vector subterrain_vertices; - std::vector> subterrain_triangles; - float isosurface_resolution; - bool first_run; - int merged; - - std::unordered_map< - float3, - std::uint_fast32_t, - vector_hasher, - vector_equals> subterrain_vertex_map; - - scene::collection* collection; - scene::model_instance* subterrain_model_instance; -}; - -} // namespace system -} // namespace game - -#endif // ANTKEEPER_GAME_SYSTEM_SUBTERRAIN_HPP - diff --git a/src/game/system/terrain.cpp b/src/game/system/terrain.cpp deleted file mode 100644 index 31e4c03..0000000 --- a/src/game/system/terrain.cpp +++ /dev/null @@ -1,651 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "game/system/terrain.hpp" -#include "game/component/terrain.hpp" -#include "game/component/camera.hpp" -#include "geom/meshes/grid.hpp" -#include "geom/mesh-functions.hpp" -#include "geom/morton.hpp" -#include "geom/quadtree.hpp" -#include "geom/primitive/ray.hpp" -#include "gl/vertex-attribute.hpp" -#include "math/quaternion.hpp" -#include "render/vertex-attribute.hpp" -#include "utility/fundamental-types.hpp" -#include "math/compile.hpp" -#include - -namespace game { -namespace system { - -terrain::terrain(entity::registry& registry): - updatable(registry), - patch_side_length(0.0f), - patch_subdivisions(0), - patch_material(nullptr), - elevation_function(nullptr), - scene_collection(nullptr), - patch_base_mesh(nullptr), - patch_vertex_size(0), - patch_vertex_stride(0), - patch_vertex_data(nullptr) -{ - // Specify vertex size and stride - // (position + uv + normal + tangent + barycentric + target) - patch_vertex_size = 3 + 2 + 3 + 4 + 3 + 3; - patch_vertex_stride = patch_vertex_size * sizeof(float); - - // Init quadtee node sizes at each depth - for (std::size_t i = 0; i <= quadtree_type::max_depth; ++i) - { - quadtree_node_size[i] = 0.0f; - quadtree_node_resolution[i] = static_cast(std::exp2(i)); - } - - registry.on_construct().connect<&terrain::on_terrain_construct>(this); - registry.on_update().connect<&terrain::on_terrain_update>(this); - registry.on_destroy().connect<&terrain::on_terrain_destroy>(this); -} - -terrain::~terrain() -{ - registry.on_construct().disconnect<&terrain::on_terrain_construct>(this); - registry.on_update().disconnect<&terrain::on_terrain_update>(this); - registry.on_destroy().disconnect<&terrain::on_terrain_destroy>(this); -} - -void terrain::update(double t, double dt) -{ - // Clear quadtree - quadtree.clear(); - - // For each camera - this->registry.view().each - ( - [&](entity::id camera_eid, const auto& camera) - { - if (!camera.object) - return; - - const scene::camera& cam = *camera.object; - - // Determine camera node location - const auto [x, y, z] = cam.get_translation(); - - quadtree_node_type node_depth = quadtree.max_depth; - const float node_size = quadtree_node_size[node_depth]; - quadtree_node_type node_resolution = quadtree_node_resolution[node_depth]; - - quadtree_node_type node_x = static_cast(x / node_size + node_resolution / 2); - quadtree_node_type node_y = static_cast(z / node_size + node_resolution / 2); - quadtree_node_type node_location = geom::morton::encode(node_x, node_y); - //quadtree.insert(quadtree.node(node_depth, node_location)); - - node_stack.push(quadtree.node(node_depth, node_location)); - balance_quadtree(); - - for (const quadtree_node_type& node: quadtree) - { - if (!quadtree.is_leaf(node)) - continue; - - if (patches.find(node) == patches.end()) - { - patch* node_patch = generate_patch(node); - patches[node] = node_patch; - scene_collection->add_object(node_patch->model_instance); - } - } - } - ); - - - - /// Toggle visibility of terrain scene objects - for (auto it = patches.begin(); it != patches.end(); ++it) - { - bool active = (quadtree.contains(it->first) && quadtree.is_leaf(it->first)); - it->second->model_instance->set_active(active); - } -} - -void terrain::set_patch_side_length(float length) -{ - patch_side_length = length; - - // Recalculate node sizes at each quadtree depth - for (std::size_t i = 0; i <= quadtree_type::max_depth; ++i) - { - quadtree_node_size[i] = std::exp2(quadtree_type::max_depth - i) * patch_side_length; - } -} - -void terrain::set_patch_subdivisions(std::size_t n) -{ - patch_subdivisions = n; - - // Recalculate patch properties - patch_cell_count = (patch_subdivisions + 1) * (patch_subdivisions + 1); - patch_triangle_count = patch_cell_count * 2; - - // Resize patch vertex data buffer - delete[] patch_vertex_data; - patch_vertex_data = new float[patch_triangle_count * 3 * patch_vertex_size]; - - // Resize patch buffers - - std::size_t vertex_buffer_row_size = patch_subdivisions + 4; - std::size_t vertex_buffer_column_size = vertex_buffer_row_size; - - patch_vertex_buffer.resize(vertex_buffer_row_size); - for (std::size_t i = 0; i < patch_vertex_buffer.size(); ++i) - patch_vertex_buffer[i].resize(vertex_buffer_column_size); - - rebuild_patch_base_mesh(); -} - -void terrain::set_patch_material(::render::material* material) -{ - patch_material = material; -} - -void terrain::set_elevation_function(const std::function& f) -{ - elevation_function = f; -} - -void terrain::set_scene_collection(scene::collection* collection) -{ - scene_collection = collection; -} - -void terrain::on_terrain_construct(entity::registry& registry, entity::id entity_id) -{ -} - -void terrain::on_terrain_update(entity::registry& registry, entity::id entity_id) -{ -} - -void terrain::on_terrain_destroy(entity::registry& registry, entity::id entity_id) -{ -} - -float terrain::get_patch_size(quadtree_node_type node) const -{ - return quadtree_node_size[quadtree_type::depth(node)]; -} - -float3 terrain::get_patch_center(quadtree_node_type node) const -{ - const float node_size = get_patch_size(node); - const float node_offset = quadtree_node_size[0] * -0.5f + node_size * 0.5f; - - // Extract node location from Morton location code - quadtree_type::node_type node_location = quadtree_type::location(node); - quadtree_type::node_type node_location_x; - quadtree_type::node_type node_location_y; - geom::morton::decode(node_location, node_location_x, node_location_y); - - return float3 - { - node_offset + static_cast(node_location_x) * node_size, - 0.0f, - node_offset + static_cast(node_location_y) * node_size - }; -} - -void terrain::rebuild_patch_base_mesh() -{ - // Rebuild grid - delete patch_base_mesh; - patch_base_mesh = geom::meshes::grid_xy(1.0f, patch_subdivisions, patch_subdivisions); - - // Convert quads to triangle fans - for (std::size_t i = 0; i < patch_base_mesh->get_faces().size(); ++i) - { - geom::mesh::face* face = patch_base_mesh->get_faces()[i]; - - std::size_t edge_count = 1; - for (geom::mesh::edge* edge = face->edge->next; edge != face->edge; edge = edge->next) - ++edge_count; - - if (edge_count > 3) - { - geom::poke_face(*patch_base_mesh, face->index); - --i; - } - } - - // Transform patch base mesh coordinates from XY plane to XZ plane - const math::quaternion xy_to_xz = math::quaternion::rotate_x(math::half_pi); - for (geom::mesh::vertex* vertex: patch_base_mesh->get_vertices()) - { - vertex->position = xy_to_xz * vertex->position; - } -} - -void terrain::balance_quadtree() -{ - while (!node_stack.empty()) - { - quadtree_node_type node = node_stack.top(); - node_stack.pop(); - - if (quadtree.contains(node)) - continue; - - quadtree.insert(node); - - const auto depth = quadtree.depth(node); - if (depth < 2) - continue; - - const quadtree_node_type parent = quadtree.parent(node); - const quadtree_node_type parent_depth = depth - 1; - const quadtree_node_type parent_resolution = quadtree_node_resolution[parent_depth]; - - for (quadtree_node_type i = 0; i < quadtree.children_per_node; ++i) - { - const auto location = quadtree.location(quadtree.sibling(parent, i)); - quadtree_node_type x, y; - geom::morton::decode(location, x, y); - - if (x < parent_resolution - 1) - { - if (y < parent_resolution - 1) - node_stack.push(quadtree.node(parent_depth, geom::morton::encode(x + 1, y + 1))); - if (y > 0) - node_stack.push(quadtree.node(parent_depth, geom::morton::encode(x + 1, y - 1))); - } - - if (x > 0) - { - if (y < parent_resolution - 1) - node_stack.push(quadtree.node(parent_depth, geom::morton::encode(x - 1, y + 1))); - if (y > 0) - node_stack.push(quadtree.node(parent_depth, geom::morton::encode(x - 1, y - 1))); - } - } - } -} - -geom::mesh* terrain::generate_patch_mesh(quadtree_node_type node) const -{ - // Extract node depth - const quadtree_type::node_type node_depth = quadtree_type::depth(node); - - // Get size of node at depth - const float node_size = quadtree_node_size[node_depth]; - - // Extract node Morton location code and decode location - const quadtree_type::node_type node_location = quadtree_type::location(node); - quadtree_type::node_type node_location_x; - quadtree_type::node_type node_location_y; - geom::morton::decode(node_location, node_location_x, node_location_y); - - // Determine center of node - const float node_offset = quadtree_node_size[0] * -0.5f + node_size * 0.5f; - const float3 node_center = - { - node_offset + static_cast(node_location_x) * node_size, - 0.0f, - node_offset + static_cast(node_location_y) * node_size - }; - - // Copy patch base mesh - geom::mesh* patch_mesh = new geom::mesh(*patch_base_mesh); - - // Modify patch mesh vertex positions - for (geom::mesh::vertex* v: patch_mesh->get_vertices()) - { - v->position.x() = node_center.x() + v->position.x() * node_size; - v->position.z() = node_center.z() + v->position.z() * node_size; - v->position.y() = elevation_function(v->position.x(), v->position.z()); - } - - return patch_mesh; -} - -::render::model* terrain::generate_patch_model(quadtree_node_type node) const -{ - // Get size and position of patch - const float patch_size = get_patch_size(node); - const float3 patch_center = get_patch_center(node); - - // Calculate size of a patch cell - const float cell_size = patch_size / static_cast(patch_subdivisions + 1); - - // Init patch bounds - geom::aabb patch_bounds; - patch_bounds.min_point.x() = patch_center.x() - patch_size * 0.5f; - patch_bounds.min_point.y() = std::numeric_limits::infinity(); - patch_bounds.min_point.z() = patch_center.z() - patch_size * 0.5f; - patch_bounds.max_point.x() = patch_center.x() + patch_size * 0.5f; - patch_bounds.max_point.y() = -std::numeric_limits::infinity(); - patch_bounds.max_point.z() = patch_center.z() + patch_size * 0.5f; - - // Calculate positions and UVs of patch vertices and immediately neighboring vertices - float3 first_vertex_position = - { - patch_bounds.min_point.x() - cell_size, - patch_center.y(), - patch_bounds.min_point.z() - cell_size - }; - float3 vertex_position = first_vertex_position; - for (std::size_t i = 0; i < patch_vertex_buffer.size(); ++i) - { - // For each column - for (std::size_t j = 0; j < patch_vertex_buffer[i].size(); ++j) - { - // Calculate vertex elevation - vertex_position.y() = elevation_function(vertex_position.x(), vertex_position.z()); - - // Update patch bounds - patch_bounds.min_point.y() = std::min(patch_bounds.min_point.y(), vertex_position.y()); - patch_bounds.max_point.y() = std::max(patch_bounds.max_point.y(), vertex_position.y()); - - // Update patch vertex position - patch_vertex_buffer[i][j].position = vertex_position; - - // Calculate patch vertex UV - patch_vertex_buffer[i][j].uv.x() = (vertex_position.x() - patch_bounds.min_point.x()) / patch_size; - patch_vertex_buffer[i][j].uv.y() = (vertex_position.z() - patch_bounds.min_point.z()) / patch_size; - - // Init patch vertex normal, tangent, and bitangent - patch_vertex_buffer[i][j].normal = {0, 0, 0}; - patch_vertex_buffer[i][j].tangent = {0, 0, 0}; - patch_vertex_buffer[i][j].bitangent = {0, 0, 0}; - - vertex_position.x() += cell_size; - } - - vertex_position.z() += cell_size; - vertex_position.x() = first_vertex_position.x(); - } - - // Accumulate normals, tangents, and bitangents - for (std::size_t i = 0; i < patch_vertex_buffer.size() - 1; ++i) - { - for (std::size_t j = 0; j < patch_vertex_buffer[i].size() - 1; ++j) - { - patch_vertex& a = patch_vertex_buffer[i ][j]; - patch_vertex& b = patch_vertex_buffer[i+1][j]; - patch_vertex& c = patch_vertex_buffer[i ][j+1]; - patch_vertex& d = patch_vertex_buffer[i+1][j+1]; - - auto add_ntb = [](auto& a, auto& b, auto& c) - { - const float3 ba = b.position - a.position; - const float3 ca = c.position - a.position; - const float2 uvba = b.uv - a.uv; - const float2 uvca = c.uv - a.uv; - - const float3 normal = math::normalize(math::cross(ba, ca)); - const float f = 1.0f / (uvba.x() * uvca.y() - uvca.x() * uvba.y()); - const float3 tangent = (ba * uvca.y() - ca * uvba.y()) * f; - const float3 bitangent = (ba * -uvca.x() + ca * uvba.x()) * f; - - a.normal += normal; - a.tangent += tangent; - a.bitangent += bitangent; - - b.normal += normal; - b.tangent += tangent; - b.bitangent += bitangent; - - c.normal += normal; - c.tangent += tangent; - c.bitangent += bitangent; - }; - - if ((j + i) % 2) - { - add_ntb(a, b, c); - add_ntb(c, b, d); - } - else - { - add_ntb(a, b, d); - add_ntb(a, d, c); - } - } - } - - // Finalize normals, tangents, and bitangent signs of patch vertices - for (std::size_t i = 1; i < patch_vertex_buffer.size() - 1; ++i) - { - for (std::size_t j = 1; j < patch_vertex_buffer[i].size() - 1; ++j) - { - auto& vertex = patch_vertex_buffer[i][j]; - - // Normalize normal - vertex.normal = math::normalize(vertex.normal); - - // Gram-Schmidt orthogonalize tangent - vertex.tangent = math::normalize(vertex.tangent - vertex.normal * math::dot(vertex.normal, vertex.tangent)); - - // Calculate bitangent sign - vertex.bitangent_sign = std::copysign(1.0f, math::dot(math::cross(vertex.normal, vertex.tangent), vertex.bitangent)); - } - } - - /* - - 0 subdivisions: - +---+---+---+ - | | - + +---+ + - | | | | - + +---+ + - | | - +---+---+---+ - - 1 subdivision: - +---+---+---+---+ - | | - + +---+---+ + - | | | | | - + +---+---+ + - | | | | | - + +---+---+ + - | | - +---+---+---+---+ - - 2 subdivisions: - +---+---+---+---+---+ - | | - + +---+---+---+ + - | | | | | | - + +---+---+---+ + - | | | | | | - + +---+---+---+ + - | | | | | | - + +---+---+---+ + - | | - +---+---+---+---+---+ - */ - - // For each row - float* v = patch_vertex_data; - for (std::size_t i = 1; i < patch_vertex_buffer.size() - 2; ++i) - { - // For each column - for (std::size_t j = 1; j < patch_vertex_buffer[i].size() - 2; ++j) - { - // a---c - // | | - // b---d - const patch_vertex& a = patch_vertex_buffer[i ][j]; - const patch_vertex& b = patch_vertex_buffer[i+1][j]; - const patch_vertex& c = patch_vertex_buffer[i ][j+1]; - const patch_vertex& d = patch_vertex_buffer[i+1][j+1]; - - auto add_triangle = [&v](const patch_vertex& a, const patch_vertex& b, const patch_vertex& c) - { - auto add_vertex = [&v](const patch_vertex& vertex, const float3& barycentric) - { - // Position - *(v++) = vertex.position[0]; - *(v++) = vertex.position[1]; - *(v++) = vertex.position[2]; - - // UV - *(v++) = vertex.uv[0]; - *(v++) = vertex.uv[1]; - - // Normal - *(v++) = vertex.normal[0]; - *(v++) = vertex.normal[1]; - *(v++) = vertex.normal[2]; - - /// Tangent - *(v++) = vertex.tangent[0]; - *(v++) = vertex.tangent[1]; - *(v++) = vertex.tangent[2]; - *(v++) = vertex.bitangent_sign; - - // Barycentric - *(v++) = barycentric[0]; - *(v++) = barycentric[1]; - *(v++) = barycentric[2]; - - // Morph target (LOD transition) - *(v++) = 0.0f; - *(v++) = 0.0f; - *(v++) = 0.0f; - }; - - add_vertex(a, float3{1, 0, 0}); - add_vertex(b, float3{0, 1, 0}); - add_vertex(c, float3{0, 0, 1}); - }; - - if ((j + i) % 2) - { - add_triangle(a, b, c); - add_triangle(c, b, d); - } - else - { - add_triangle(a, b, d); - add_triangle(a, d, c); - } - } - } - - // Allocate patch model - ::render::model* patch_model = new ::render::model(); - - // Get model VBO and VAO - gl::vertex_buffer* vbo = patch_model->get_vertex_buffer(); - gl::vertex_array* vao = patch_model->get_vertex_array(); - - // Resize model VBO and upload vertex data - vbo->resize(patch_triangle_count * 3 * patch_vertex_stride, patch_vertex_data); - - std::size_t attribute_offset = 0; - - // Define position vertex attribute - gl::vertex_attribute position_attribute; - position_attribute.buffer = vbo; - position_attribute.offset = attribute_offset; - position_attribute.stride = patch_vertex_stride; - position_attribute.type = gl::vertex_attribute_type::float_32; - position_attribute.components = 3; - attribute_offset += position_attribute.components * sizeof(float); - - // Define UV vertex attribute - gl::vertex_attribute uv_attribute; - uv_attribute.buffer = vbo; - uv_attribute.offset = attribute_offset; - uv_attribute.stride = patch_vertex_stride; - uv_attribute.type = gl::vertex_attribute_type::float_32; - uv_attribute.components = 2; - attribute_offset += uv_attribute.components * sizeof(float); - - // Define normal vertex attribute - gl::vertex_attribute normal_attribute; - normal_attribute.buffer = vbo; - normal_attribute.offset = attribute_offset; - normal_attribute.stride = patch_vertex_stride; - normal_attribute.type = gl::vertex_attribute_type::float_32; - normal_attribute.components = 3; - attribute_offset += normal_attribute.components * sizeof(float); - - // Define tangent vertex attribute - gl::vertex_attribute tangent_attribute; - tangent_attribute.buffer = vbo; - tangent_attribute.offset = attribute_offset; - tangent_attribute.stride = patch_vertex_stride; - tangent_attribute.type = gl::vertex_attribute_type::float_32; - tangent_attribute.components = 4; - attribute_offset += tangent_attribute.components * sizeof(float); - - // Define barycentric vertex attribute - gl::vertex_attribute barycentric_attribute; - barycentric_attribute.buffer = vbo; - barycentric_attribute.offset = attribute_offset; - barycentric_attribute.stride = patch_vertex_stride; - barycentric_attribute.type = gl::vertex_attribute_type::float_32; - barycentric_attribute.components = 3; - attribute_offset += barycentric_attribute.components * sizeof(float); - - // Define target vertex attribute - gl::vertex_attribute target_attribute; - target_attribute.buffer = vbo; - target_attribute.offset = attribute_offset; - target_attribute.stride = patch_vertex_stride; - target_attribute.type = gl::vertex_attribute_type::float_32; - target_attribute.components = 3; - attribute_offset += target_attribute.components * sizeof(float); - - // Bind vertex attributes to VAO - vao->bind(::render::vertex_attribute::position, position_attribute); - vao->bind(::render::vertex_attribute::uv, uv_attribute); - vao->bind(::render::vertex_attribute::normal, normal_attribute); - vao->bind(::render::vertex_attribute::tangent, tangent_attribute); - vao->bind(::render::vertex_attribute::barycentric, barycentric_attribute); - vao->bind(::render::vertex_attribute::target, target_attribute); - - // Create model group - ::render::model_group* patch_model_group = patch_model->add_group("terrain"); - patch_model_group->set_material(patch_material); - patch_model_group->set_drawing_mode(gl::drawing_mode::triangles); - patch_model_group->set_start_index(0); - patch_model_group->set_index_count(patch_triangle_count * 3); - - // Set patch model bounds - patch_model->set_bounds(patch_bounds); - - return patch_model; -} - -terrain::patch* terrain::generate_patch(quadtree_node_type node) -{ - patch* node_patch = new patch(); - node_patch->mesh = nullptr;//generate_patch_mesh(node); - node_patch->model = generate_patch_model(node); - node_patch->model_instance = new scene::model_instance(node_patch->model); - return node_patch; -} - -} // namespace system -} // namespace game diff --git a/src/game/system/terrain.hpp b/src/game/system/terrain.hpp deleted file mode 100644 index 5e199a8..0000000 --- a/src/game/system/terrain.hpp +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_SYSTEM_TERRAIN_HPP -#define ANTKEEPER_GAME_SYSTEM_TERRAIN_HPP - -#include "game/system/updatable.hpp" -#include "game/component/terrain.hpp" -#include "entity/id.hpp" -#include "math/quaternion.hpp" -#include "geom/quadtree.hpp" -#include "geom/mesh.hpp" -#include "utility/fundamental-types.hpp" -#include "render/model.hpp" -#include "render/material.hpp" -#include "scene/model-instance.hpp" -#include "scene/collection.hpp" -#include "geom/view-frustum.hpp" -#include -#include - -namespace game { -namespace system { - -/** - * Generates terrain patches and performs terrain patch LOD selection. - */ -class terrain: public updatable -{ -public: - terrain(entity::registry& registry); - ~terrain(); - - virtual void update(double t, double dt); - - /** - * Sets the size of a patch. - * - * @param length Side length of a patch. - */ - void set_patch_side_length(float length); - - /** - * Sets the number of subdivisions of a patch. Zero subdivisions results in a single quad, one subdivison results in four quads, etc. - * - * @param n Number of subdivisions. - */ - void set_patch_subdivisions(std::size_t n); - - /** - * Sets the material of each patch. - * - * @param material Patch material. - */ - void set_patch_material(::render::material* material); - - /** - * Sets the terrain elevation function. - * - * @param f Function which returns the terrain height (Y-coordinate) given X- and Z-coordinates. - */ - void set_elevation_function(const std::function& f); - - /** - * Sets the scene collection into which terrain patch model instances will be inserted. - */ - void set_scene_collection(scene::collection* collection); - -private: - typedef geom::unordered_quadtree16 quadtree_type; - typedef typename quadtree_type::node_type quadtree_node_type; - - void balance_quadtree(); - - struct patch - { - geom::mesh* mesh; - ::render::model* model; - scene::model_instance* model_instance; - }; - - void on_terrain_construct(entity::registry& registry, entity::id entity_id); - void on_terrain_update(entity::registry& registry, entity::id entity_id); - void on_terrain_destroy(entity::registry& registry, entity::id entity_id); - - float get_patch_size(quadtree_node_type node) const; - float3 get_patch_center(quadtree_node_type node) const; - - void rebuild_patch_base_mesh(); - - /** - * Generates a mesh for a terrain patch given the patch's quadtree node - */ - geom::mesh* generate_patch_mesh(quadtree_node_type node) const; - - /** - * Generates a model for a terrain patch given the patch's mesh. - */ - ::render::model* generate_patch_model(quadtree_node_type node) const; - - patch* generate_patch(quadtree_node_type node); - - float patch_side_length; - std::size_t patch_subdivisions; - std::size_t patch_cell_count; - std::size_t patch_triangle_count; - std::size_t patch_vertex_size; - std::size_t patch_vertex_stride; - float* patch_vertex_data; - - struct patch_vertex - { - float3 position; - float2 uv; - float3 normal; - float3 tangent; - float3 bitangent; - float bitangent_sign; - }; - - mutable std::vector> patch_vertex_buffer; - - - ::render::material* patch_material; - std::function elevation_function; - scene::collection* scene_collection; - - geom::mesh* patch_base_mesh; - - /// Quadtree describing level of detail - quadtree_type quadtree; - float quadtree_node_size[quadtree_type::max_depth + 1]; - quadtree_node_type quadtree_node_resolution[quadtree_type::max_depth + 1]; - - /// Map linking quadtree nodes to terrain patches - std::unordered_map patches; - - std::stack node_stack; -}; - -} // namespace system -} // namespace game - -#endif // ANTKEEPER_GAME_SYSTEM_TERRAIN_HPP diff --git a/src/game/system/updatable.cpp b/src/game/system/updatable.cpp deleted file mode 100644 index e71b8d6..0000000 --- a/src/game/system/updatable.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "updatable.hpp" - -namespace game { -namespace system { - -updatable::updatable(entity::registry& registry): - registry(registry) -{} - -} // namespace system -} // namespace game diff --git a/src/game/system/updatable.hpp b/src/game/system/updatable.hpp deleted file mode 100644 index 05adf6c..0000000 --- a/src/game/system/updatable.hpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_SYSTEM_UPDATABLE_HPP -#define ANTKEEPER_GAME_SYSTEM_UPDATABLE_HPP - -#include "entity/registry.hpp" - -namespace game { -namespace system { - -/** - * Abstract base class for updatable systems. - */ -class updatable -{ -public: - /** - * Creates an updatable system. - * - * @param registry Reference to the registry on which the system will operate. - */ - updatable(entity::registry& registry); - - /** - * Perform's a system's update() function. - * - * @param t Total elapsed time, in seconds. - * @param dt Delta time, in seconds. - * @param registry Entity registry. - */ - virtual void update(double t, double dt) = 0; - -protected: - /// Registry on which the system operate - entity::registry& registry; -}; - -} // namespace system -} // namespace game - -#endif // ANTKEEPER_GAME_SYSTEM_UPDATABLE_HPP diff --git a/src/game/system/vegetation.cpp b/src/game/system/vegetation.cpp deleted file mode 100644 index 5aad557..0000000 --- a/src/game/system/vegetation.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "vegetation.hpp" -#include "game/component/model.hpp" -#include "game/component/transform.hpp" -#include "scene/model-instance.hpp" -#include "scene/lod-group.hpp" -#include "scene/collection.hpp" -#include "render/material.hpp" -#include "geom/aabb.hpp" -#include "utility/fundamental-types.hpp" -#include - -namespace game { -namespace system { - -vegetation::vegetation(entity::registry& registry): - updatable(registry), - terrain_patch_size(1.0f), - vegetation_patch_size(1.0f), - vegetation_patch_columns(1), - vegetation_patch_rows(1), - vegetation_density(1.0f), - vegetation_model(nullptr) -{ - registry.on_construct().connect<&vegetation::on_terrain_construct>(this); - registry.on_destroy().connect<&vegetation::on_terrain_destroy>(this); -} - -vegetation::~vegetation() -{ - registry.on_construct().disconnect<&vegetation::on_terrain_construct>(this); - registry.on_destroy().disconnect<&vegetation::on_terrain_destroy>(this); -} - -void vegetation::update(double t, double dt) -{} - -void vegetation::set_terrain_patch_size(float size) -{ - terrain_patch_size = size; - vegetation_patch_size = terrain_patch_size / static_cast(vegetation_patch_columns); -} - -void vegetation::set_vegetation_patch_resolution(int subdivisions) -{ - // Determine number of vegetation patch columns and rows per terrain patch - vegetation_patch_columns = static_cast(std::pow(2, subdivisions)); - vegetation_patch_rows = vegetation_patch_columns; - vegetation_patch_size = terrain_patch_size / static_cast(vegetation_patch_columns); -} - -void vegetation::set_vegetation_density(float density) -{ - vegetation_density = density; -} - -void vegetation::set_vegetation_model(::model* model) -{ - vegetation_model = model; -} - -void vegetation::set_scene(scene::collection* collection) -{ - this->scene_collection = collection; -} - -void vegetation::on_terrain_construct(entity::registry& registry, entity::id entity_id) -{} - -void vegetation::on_terrain_destroy(entity::registry& registry, entity::id entity_id) -{} - -} // namespace system -} // namespace game diff --git a/src/game/system/vegetation.hpp b/src/game/system/vegetation.hpp deleted file mode 100644 index 6b24ddc..0000000 --- a/src/game/system/vegetation.hpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GAME_SYSTEM_VEGETATION_HPP -#define ANTKEEPER_GAME_SYSTEM_VEGETATION_HPP - -#include "game/system/updatable.hpp" -#include "game/component/terrain.hpp" -#include "entity/id.hpp" -#include "scene/collection.hpp" - -class model; - -namespace game { -namespace system { - -/** - * Places vegetation patches on terrain. - */ -class vegetation: public updatable -{ -public: - vegetation(entity::registry& registry); - ~vegetation(); - virtual void update(double t, double dt); - - /** - * Sets the terrain patch size. - * - * @param size Size of the terrain patch. - */ - void set_terrain_patch_size(float size); - - /** - * Sets the vegetation patch size. - * - * @param subdivisions Number of times a terrain patch should be subdivided into vegetation patches. - */ - void set_vegetation_patch_resolution(int subdivisions); - - void set_vegetation_density(float density); - - void set_vegetation_model(::model* model); - - void set_scene(scene::collection* collection); - -private: - void on_terrain_construct(entity::registry& registry, entity::id entity_id); - void on_terrain_destroy(entity::registry& registry, entity::id entity_id); - - float terrain_patch_size; - float vegetation_patch_size; - int vegetation_patch_columns; - int vegetation_patch_rows; - float vegetation_density; - model* vegetation_model; - scene::collection* scene_collection; -}; - -} // namespace system -} // namespace game - -#endif // ANTKEEPER_GAME_SYSTEM_VEGETATION_HPP - diff --git a/src/game/systems/astronomy-system.cpp b/src/game/systems/astronomy-system.cpp new file mode 100644 index 0000000..dce4005 --- /dev/null +++ b/src/game/systems/astronomy-system.cpp @@ -0,0 +1,622 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include "game/systems/astronomy-system.hpp" +#include +#include "game/components/blackbody-component.hpp" +#include "game/components/transform-component.hpp" +#include "game/components/diffuse-reflector-component.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +astronomy_system::astronomy_system(entity::registry& registry): + updatable_system(registry), + time_days(0.0), + time_centuries(0.0), + time_scale(1.0), + observer_eid(entt::null), + reference_body_eid(entt::null), + transmittance_samples(0), + sun_light(nullptr), + sky_light(nullptr), + moon_light(nullptr), + bounce_light(nullptr), + bounce_albedo{0, 0, 0}, + sky_pass(nullptr), + starlight_illuminance{0, 0, 0} +{ + // Construct ENU to EUS transformation + enu_to_eus = math::transformation::se3 + { + {0, 0, 0}, + math::quaternion::rotate_x(-math::half_pi) + }; + + registry.on_construct<::observer_component>().connect<&astronomy_system::on_observer_modified>(this); + registry.on_update<::observer_component>().connect<&astronomy_system::on_observer_modified>(this); + registry.on_destroy<::observer_component>().connect<&astronomy_system::on_observer_destroyed>(this); + registry.on_construct<::celestial_body_component>().connect<&astronomy_system::on_celestial_body_modified>(this); + registry.on_update<::celestial_body_component>().connect<&astronomy_system::on_celestial_body_modified>(this); + registry.on_destroy<::celestial_body_component>().connect<&astronomy_system::on_celestial_body_destroyed>(this); + registry.on_construct<::orbit_component>().connect<&astronomy_system::on_orbit_modified>(this); + registry.on_update<::orbit_component>().connect<&astronomy_system::on_orbit_modified>(this); + registry.on_destroy<::orbit_component>().connect<&astronomy_system::on_orbit_destroyed>(this); + registry.on_construct<::atmosphere_component>().connect<&astronomy_system::on_atmosphere_modified>(this); + registry.on_update<::atmosphere_component>().connect<&astronomy_system::on_atmosphere_modified>(this); + registry.on_destroy<::atmosphere_component>().connect<&astronomy_system::on_atmosphere_destroyed>(this); +} + +astronomy_system::~astronomy_system() +{ + registry.on_construct<::observer_component>().disconnect<&astronomy_system::on_observer_modified>(this); + registry.on_update<::observer_component>().disconnect<&astronomy_system::on_observer_modified>(this); + registry.on_destroy<::observer_component>().disconnect<&astronomy_system::on_observer_destroyed>(this); + registry.on_construct<::celestial_body_component>().disconnect<&astronomy_system::on_celestial_body_modified>(this); + registry.on_update<::celestial_body_component>().disconnect<&astronomy_system::on_celestial_body_modified>(this); + registry.on_destroy<::celestial_body_component>().disconnect<&astronomy_system::on_celestial_body_destroyed>(this); + registry.on_construct<::orbit_component>().disconnect<&astronomy_system::on_orbit_modified>(this); + registry.on_update<::orbit_component>().disconnect<&astronomy_system::on_orbit_modified>(this); + registry.on_destroy<::orbit_component>().disconnect<&astronomy_system::on_orbit_destroyed>(this); + registry.on_construct<::atmosphere_component>().disconnect<&astronomy_system::on_atmosphere_modified>(this); + registry.on_update<::atmosphere_component>().disconnect<&astronomy_system::on_atmosphere_modified>(this); + registry.on_destroy<::atmosphere_component>().disconnect<&astronomy_system::on_atmosphere_destroyed>(this); +} + +void astronomy_system::update(double t, double dt) +{ + double3 sky_light_illuminance = {0.0, 0.0, 0.0}; + + // Add scaled timestep to current time + set_time(time_days + dt * time_scale); + + // Abort if no valid observer entity or reference body entity + if (observer_eid == entt::null || reference_body_eid == entt::null) + return; + + // Get pointer to observer component + const auto observer = registry.try_get(observer_eid); + + // Abort if no observer component + if (!observer) + return; + + // Get pointers to reference body components + const auto + [ + reference_body, + reference_orbit, + reference_atmosphere + ] = registry.try_get(reference_body_eid); + + // Abort if no reference body or reference orbit + if (!reference_body || !reference_orbit) + return; + + // Update ICRF to EUS transformation + update_icrf_to_eus(*reference_body, *reference_orbit); + + // Set the transform component translations of orbiting bodies to their topocentric positions + registry.view().each( + [&](entity::id entity_id, const auto& body, const auto& orbit, auto& transform) + { + // Skip reference body entity + if (entity_id == reference_body_eid) + return; + + // Transform orbital Cartesian position (r) from the ICRF frame to the EUS frame + const double3 r_eus = icrf_to_eus * orbit.position; + + // Evaluate body orientation polynomials + const double body_pole_ra = math::polynomial::horner(body.pole_ra.begin(), body.pole_ra.end(), time_centuries); + const double body_pole_dec = math::polynomial::horner(body.pole_dec.begin(), body.pole_dec.end(), time_centuries); + const double body_prime_meridian = math::polynomial::horner(body.prime_meridian.begin(), body.prime_meridian.end(), time_days); + + // Determine body orientation in the ICRF frame + math::quaternion rotation_icrf = physics::orbit::frame::bcbf::to_bci + ( + body_pole_ra, + body_pole_dec, + body_prime_meridian + ).r; + + // Transform body orientation from the ICRF frame to the EUS frame. + math::quaternion rotation_eus = math::normalize(icrf_to_eus.r * rotation_icrf); + + // Update local transform + if (orbit.parent != entt::null) + { + transform.local.translation = math::normalize(float3(r_eus)); + transform.local.rotation = math::quaternion(rotation_eus); + transform.local.scale = {1.0f, 1.0f, 1.0f}; + } + }); + + constexpr double3 bounce_normal = {0, 1, 0}; + double3 bounce_illuminance = {0, 0, 0}; + + // Update blackbody lighting + registry.view().each( + [&, bounce_normal](entity::id entity_id, const auto& blackbody_body, const auto& blackbody_orbit, const auto& blackbody) + { + // Transform blackbody position from ICRF frame to EUS frame + const double3 blackbody_position_eus = icrf_to_eus * blackbody_orbit.position; + + // Measure distance and direction, in EUS frame, from observer to blackbody + const double observer_blackbody_distance = math::length(blackbody_position_eus); + const double3 observer_blackbody_direction_eus = blackbody_position_eus / observer_blackbody_distance; + + // Measure blackbody solid angle as seen by observer + const double observer_blackbody_angular_radius = astro::angular_radius(blackbody_body.radius, observer_blackbody_distance); + const double observer_blackbody_solid_angle = geom::solid_angle::cone(observer_blackbody_angular_radius); + + // Calculate illuminance from blackbody reaching observer + const double3 observer_blackbody_illuminance = blackbody.luminance * observer_blackbody_solid_angle; + + // Calculate illuminance from blackbody reaching observer after atmospheric extinction + double3 observer_blackbody_transmitted_illuminance = observer_blackbody_illuminance; + if (reference_atmosphere) + { + // Construct ray at observer pointing towards the blackbody + const geom::ray ray = {{0, 0, 0}, observer_blackbody_direction_eus}; + + // Integrate atmospheric spectral transmittance factor between observer and blackbody + const double3 transmittance = integrate_transmittance(*observer, *reference_body, *reference_atmosphere, ray); + + // Attenuate illuminance from blackbody reaching observer by spectral transmittance factor + observer_blackbody_transmitted_illuminance *= transmittance; + } + + // Update sun light + if (sun_light != nullptr) + { + const double3 blackbody_up_eus = icrf_to_eus.r * double3{0, 0, 1}; + sun_light->set_rotation + ( + math::look_rotation + ( + float3(-observer_blackbody_direction_eus), + float3(blackbody_up_eus) + ) + ); + + sun_light->set_color(float3(observer_blackbody_transmitted_illuminance)); + + // Bounce sun light + bounce_illuminance += std::max(0.0, math::dot(bounce_normal, -observer_blackbody_direction_eus)) * observer_blackbody_transmitted_illuminance * bounce_albedo; + } + + // Update sky light + if (sky_light != nullptr) + { + // Calculate sky illuminance + double3 blackbody_position_enu_spherical = physics::orbit::frame::enu::spherical(enu_to_eus.inverse() * blackbody_position_eus); + const double sky_illuminance = 25000.0 * std::max(0.0, std::sin(blackbody_position_enu_spherical.y())); + + // Add sky illuminance to sky light illuminance + sky_light_illuminance += {sky_illuminance, sky_illuminance, sky_illuminance}; + + // Add starlight illuminance to sky light illuminance + sky_light_illuminance += starlight_illuminance; + + // Update sky light + sky_light->set_color(float3(sky_light_illuminance)); + + // Bounce sky light + bounce_illuminance += sky_light_illuminance * bounce_albedo; + } + + // Upload blackbody params to sky pass + if (this->sky_pass) + { + this->sky_pass->set_sun_position(float3(blackbody_position_eus)); + this->sky_pass->set_sun_luminance(float3(blackbody.luminance)); + this->sky_pass->set_sun_illuminance(float3(observer_blackbody_illuminance), float3(observer_blackbody_transmitted_illuminance)); + this->sky_pass->set_sun_angular_radius(static_cast(observer_blackbody_angular_radius)); + } + + // Update diffuse reflectors + this->registry.view().each( + [&, bounce_normal](entity::id entity_id, const auto& reflector_body, const auto& reflector_orbit, const auto& reflector, const auto& transform) + { + // Transform reflector position from ICRF frame to EUS frame + const double3 reflector_position_eus = icrf_to_eus * reflector_orbit.position; + + // Measure distance and direction, in EUS frame, from observer to reflector + const double observer_reflector_distance = math::length(reflector_position_eus); + const double3 observer_reflector_direction_eus = reflector_position_eus / observer_reflector_distance; + + // Measure distance and direction, in EUS frame, from reflector to blackbody + double3 reflector_blackbody_direction_eus = blackbody_position_eus - reflector_position_eus; + const double reflector_blackbody_distance = math::length(reflector_blackbody_direction_eus); + reflector_blackbody_direction_eus /= reflector_blackbody_distance; + + // Measure blackbody solid angle as seen by reflector + const double reflector_blackbody_angular_radius = astro::angular_radius(blackbody_body.radius, reflector_blackbody_distance); + const double reflector_blackbody_solid_angle = geom::solid_angle::cone(reflector_blackbody_angular_radius); + + // Calculate blackbody illuminance reaching reflector + const double3 reflector_blackbody_illuminance = blackbody.luminance * reflector_blackbody_solid_angle; + + // Measure reflector solid angle as seen by observer + const double observer_reflector_angular_radius = astro::angular_radius(reflector_body.radius, observer_reflector_distance); + const double observer_reflector_solid_angle = geom::solid_angle::cone(observer_reflector_angular_radius); + + // Determine phase factor of reflector as seen by observer + const double observer_reflector_phase_factor = dot(observer_reflector_direction_eus, -reflector_blackbody_direction_eus) * 0.5 + 0.5; + + // Measure observer reference body solid angle as seen by reflector + const double reflector_observer_angular_radius = astro::angular_radius(reference_body->radius, observer_reflector_distance); + const double reflector_observer_solid_angle = geom::solid_angle::cone(reflector_observer_angular_radius); + + // Determine phase factor of observer reference body as by reflector + const double reflector_observer_phase_factor = dot(-observer_reflector_direction_eus, -observer_blackbody_direction_eus) * 0.5 + 0.5; + + // Calculate spectral transmittance between observer and reflector factor due to atmospheric extinction + double3 observer_reflector_transmittance = {1, 1, 1}; + if (reference_atmosphere) + { + const geom::ray ray = {{0, 0, 0}, observer_reflector_direction_eus}; + observer_reflector_transmittance = integrate_transmittance(*observer, *reference_body, *reference_atmosphere, ray); + } + + // Measure luminance of observer reference body as seen by reflector + const double3 reflector_observer_luminance = observer_blackbody_illuminance * reference_body->albedo * observer_reflector_transmittance * reflector_observer_phase_factor * math::inv_pi; + + // Measure illuminance from observer reference body reaching reflector + const double3 reflector_observer_illuminance = reflector_observer_luminance * reflector_observer_solid_angle; + + // Measure luminance of reflector as seen by observer + const double3 observer_reflector_luminance = (reflector_blackbody_illuminance * observer_reflector_phase_factor + reflector_observer_illuminance) * reflector.albedo * observer_reflector_transmittance * math::inv_pi; + + // Measure illuminance from reflector reaching observer + const double3 observer_reflector_illuminance = observer_reflector_luminance * observer_reflector_solid_angle; + + if (this->sky_pass) + { + this->sky_pass->set_moon_position(transform.local.translation); + this->sky_pass->set_moon_rotation(transform.local.rotation); + this->sky_pass->set_moon_angular_radius(static_cast(observer_reflector_angular_radius)); + this->sky_pass->set_moon_sunlight_direction(float3(-reflector_blackbody_direction_eus)); + this->sky_pass->set_moon_sunlight_illuminance(float3(reflector_blackbody_illuminance * observer_reflector_transmittance)); + this->sky_pass->set_moon_planetlight_direction(float3(observer_reflector_direction_eus)); + this->sky_pass->set_moon_planetlight_illuminance(float3(reflector_observer_illuminance * observer_reflector_transmittance)); + this->sky_pass->set_moon_illuminance(float3(observer_reflector_illuminance / observer_reflector_transmittance), float3(observer_reflector_illuminance)); + } + + if (this->moon_light) + { + const float3 reflector_up_eus = float3(icrf_to_eus.r * double3{0, 0, 1}); + + this->moon_light->set_color(float3(observer_reflector_illuminance)); + this->moon_light->set_rotation + ( + math::look_rotation + ( + float3(-observer_reflector_direction_eus), + reflector_up_eus + ) + ); + + // Bounce moon light + bounce_illuminance += std::max(0.0, math::dot(bounce_normal, -observer_reflector_direction_eus)) * observer_reflector_illuminance * bounce_albedo; + } + }); + }); + + if (bounce_light) + { + bounce_light->set_color(float3(bounce_illuminance)); + } +} + +void astronomy_system::set_time(double t) +{ + time_days = t; + time_centuries = time_days * physics::time::jd::centuries_per_day; +} + +void astronomy_system::set_time_scale(double scale) +{ + time_scale = scale; +} + +void astronomy_system::set_observer(entity::id eid) +{ + if (observer_eid != eid) + { + observer_eid = eid; + + if (observer_eid != entt::null) + observer_modified(); + else + reference_body_eid = entt::null; + } +} + +void astronomy_system::set_transmittance_samples(std::size_t samples) +{ + transmittance_samples = samples; +} + +void astronomy_system::set_sun_light(scene::directional_light* light) +{ + sun_light = light; +} + +void astronomy_system::set_sky_light(scene::ambient_light* light) +{ + sky_light = light; +} + +void astronomy_system::set_moon_light(scene::directional_light* light) +{ + moon_light = light; +} + +void astronomy_system::set_bounce_light(scene::directional_light* light) +{ + bounce_light = light; +} + +void astronomy_system::set_bounce_albedo(const double3& albedo) +{ + bounce_albedo = albedo; +} + +void astronomy_system::set_starlight_illuminance(const double3& illuminance) +{ + starlight_illuminance = illuminance; +} + +void astronomy_system::set_sky_pass(::render::sky_pass* pass) +{ + this->sky_pass = pass; + + if (sky_pass) + { + if (observer_eid != entt::null) + { + // Get pointer to observer + const auto observer = registry.try_get(reference_body_eid); + + sky_pass->set_observer_elevation(static_cast(observer->elevation)); + } + + if (reference_body_eid != entt::null) + { + // Get pointer to reference celestial body + const auto reference_body = registry.try_get(reference_body_eid); + + if (reference_body) + sky_pass->set_planet_radius(static_cast(reference_body->radius)); + else + sky_pass->set_planet_radius(0.0f); + } + } +} + +void astronomy_system::on_observer_modified(entity::registry& registry, entity::id entity_id) +{ + if (entity_id == observer_eid) + observer_modified(); +} + +void astronomy_system::on_observer_destroyed(entity::registry& registry, entity::id entity_id) +{ + if (entity_id == observer_eid) + observer_modified(); +} + +void astronomy_system::on_celestial_body_modified(entity::registry& registry, entity::id entity_id) +{ + if (entity_id == reference_body_eid) + reference_body_modified(); +} + +void astronomy_system::on_celestial_body_destroyed(entity::registry& registry, entity::id entity_id) +{ + if (entity_id == reference_body_eid) + reference_body_modified(); +} + +void astronomy_system::on_orbit_modified(entity::registry& registry, entity::id entity_id) +{ + if (entity_id == reference_body_eid) + reference_orbit_modified(); +} + +void astronomy_system::on_orbit_destroyed(entity::registry& registry, entity::id entity_id) +{ + if (entity_id == reference_body_eid) + reference_orbit_modified(); +} + +void astronomy_system::on_atmosphere_modified(entity::registry& registry, entity::id entity_id) +{ + if (entity_id == reference_body_eid) + reference_atmosphere_modified(); +} + +void astronomy_system::on_atmosphere_destroyed(entity::registry& registry, entity::id entity_id) +{ + if (entity_id == reference_body_eid) + reference_atmosphere_modified(); +} + +void astronomy_system::observer_modified() +{ + // Get pointer to observer component + const auto observer = registry.try_get(observer_eid); + + if (observer) + { + if (reference_body_eid != observer->reference_body_eid) + { + // Reference body changed + reference_body_eid = observer->reference_body_eid; + reference_body_modified(); + reference_orbit_modified(); + reference_atmosphere_modified(); + } + + if (reference_body_eid != entt::null) + { + // Get pointer to reference celestial body + const auto reference_body = registry.try_get(reference_body_eid); + + // Update BCBF to EUS transformation + if (reference_body) + update_bcbf_to_eus(*observer, *reference_body); + } + + // Upload observer elevation to sky pass + if (sky_pass) + sky_pass->set_observer_elevation(static_cast(observer->elevation)); + } +} + +void astronomy_system::reference_body_modified() +{ + // Get pointer to reference celestial body + const auto reference_body = registry.try_get(reference_body_eid); + + if (reference_body) + { + // Get pointer to observer + const auto observer = registry.try_get(observer_eid); + + // Update BCBF to EUS transformation + if (observer) + update_bcbf_to_eus(*observer, *reference_body); + } + + // Update reference celestial body-related sky pass parameters + if (sky_pass) + { + if (reference_body) + sky_pass->set_planet_radius(static_cast(reference_body->radius)); + else + sky_pass->set_planet_radius(0.0f); + } +} + +void astronomy_system::reference_orbit_modified() +{ + +} + +void astronomy_system::reference_atmosphere_modified() +{ + +} + +void astronomy_system::update_bcbf_to_eus(const ::observer_component& observer, const ::celestial_body_component& body) +{ + // Construct BCBF to EUS transformation + bcbf_to_eus = physics::orbit::frame::bcbf::to_enu + ( + body.radius + observer.elevation, + observer.latitude, + observer.longitude + ) * enu_to_eus; +} + +void astronomy_system::update_icrf_to_eus(const ::celestial_body_component& body, const ::orbit_component& orbit) +{ + // Evaluate reference body orientation polynomials + const double body_pole_ra = math::polynomial::horner(body.pole_ra.begin(), body.pole_ra.end(), time_centuries); + const double body_pole_dec = math::polynomial::horner(body.pole_dec.begin(), body.pole_dec.end(), time_centuries); + const double body_prime_meridian = math::polynomial::horner(body.prime_meridian.begin(), body.prime_meridian.end(), time_days); + + // Construct ICRF frame to BCBF transformation + math::transformation::se3 icrf_to_bcbf = physics::orbit::frame::bci::to_bcbf + ( + body_pole_ra, + body_pole_dec, + body_prime_meridian + ); + icrf_to_bcbf.t = icrf_to_bcbf.r * -orbit.position; + + /// Construct ICRF to EUS transformation + icrf_to_eus = icrf_to_bcbf * bcbf_to_eus; + + // Pass ICRF to EUS transformation to sky pass + if (sky_pass) + { + // Upload topocentric frame to sky pass + sky_pass->set_icrf_to_eus + ( + math::transformation::se3 + { + float3(icrf_to_eus.t), + math::quaternion(icrf_to_eus.r) + } + ); + } +} + +double3 astronomy_system::integrate_transmittance(const ::observer_component& observer, const ::celestial_body_component& body, const ::atmosphere_component& atmosphere, geom::ray ray) const +{ + double3 transmittance = {1, 1, 1}; + + // Make ray height relative to center of reference body + ray.origin.y() += body.radius + observer.elevation; + + // Construct sphere representing upper limit of the atmosphere + geom::sphere atmosphere_sphere; + atmosphere_sphere.center = {0, 0, 0}; + atmosphere_sphere.radius = body.radius + atmosphere.upper_limit; + + // Check for intersection between the ray and atmosphere + auto intersection = geom::ray_sphere_intersection(ray, atmosphere_sphere); + if (std::get<0>(intersection)) + { + // Get point of intersection + const double3 intersection_point = ray.extrapolate(std::get<2>(intersection)); + + // Integrate optical of Rayleigh, Mie, and ozone particles + const double optical_depth_r = physics::gas::atmosphere::optical_depth_exp(ray.origin, intersection_point, body.radius, atmosphere.rayleigh_scale_height, transmittance_samples); + const double optical_depth_m = physics::gas::atmosphere::optical_depth_exp(ray.origin, intersection_point, body.radius, atmosphere.mie_scale_height, transmittance_samples); + const double optical_depth_o = physics::gas::atmosphere::optical_depth_tri(ray.origin, intersection_point, body.radius, atmosphere.ozone_lower_limit, atmosphere.ozone_upper_limit, atmosphere.ozone_mode, transmittance_samples); + + // Calculate transmittance factor due to scattering and absorption + const double3 extinction_r = atmosphere.rayleigh_scattering * optical_depth_r; + const double extinction_m = atmosphere.mie_extinction * optical_depth_m; + const double3 extinction_o = atmosphere.ozone_absorption * optical_depth_o; + transmittance = extinction_r + double3{extinction_m, extinction_m, extinction_m} + extinction_o; + transmittance.x() = std::exp(-transmittance.x()); + transmittance.y() = std::exp(-transmittance.y()); + transmittance.z() = std::exp(-transmittance.z()); + } + + return transmittance; +} + diff --git a/src/game/systems/astronomy-system.hpp b/src/game/systems/astronomy-system.hpp new file mode 100644 index 0000000..09b86c7 --- /dev/null +++ b/src/game/systems/astronomy-system.hpp @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_ASTRONOMY_SYSTEM_HPP +#define ANTKEEPER_GAME_ASTRONOMY_SYSTEM_HPP + +#include "game/systems/updatable-system.hpp" +#include +#include +#include +#include +#include +#include +#include "game/components/observer-component.hpp" +#include "game/components/atmosphere-component.hpp" +#include "game/components/celestial-body-component.hpp" +#include "game/components/orbit-component.hpp" +#include + + +/** + * Calculates apparent properties of celestial bodies as seen by an observer. + */ +class astronomy_system: + public updatable_system +{ +public: + astronomy_system(entity::registry& registry); + ~astronomy_system(); + + /** + * Adds the timestep `dt`, scaled by set time scale, to the current time, then calculates apparent properties of celestial bodies as seen by an observer. + * + * @param t Time, in seconds. + * @param dt Delta time, in seconds. + */ + virtual void update(double t, double dt); + + /** + * Sets the current time. + * + * @param t Time since epoch, in days. + */ + void set_time(double t); + + /** + * Sets the factor by which the timestep `dt` will be scaled before being added to the current time. + * + * @param scale Factor by which to scale the timestep. + */ + void set_time_scale(double scale); + + /** + * Sets the observer entity. + * + * @param eid Entity ID of the observer. + */ + void set_observer(entity::id eid); + + /** + * Sets the number of samples to take when integrating atmospheric transmittance. + * + * @param samples Number of integration samples. + */ + void set_transmittance_samples(std::size_t samples); + + void set_sun_light(scene::directional_light* light); + void set_sky_light(scene::ambient_light* light); + void set_moon_light(scene::directional_light* light); + void set_bounce_light(scene::directional_light* light); + void set_bounce_albedo(const double3& albedo); + void set_starlight_illuminance(const double3& illuminance); + void set_sky_pass(::render::sky_pass* pass); + +private: + void on_observer_modified(entity::registry& registry, entity::id entity_id); + void on_observer_destroyed(entity::registry& registry, entity::id entity_id); + void on_celestial_body_modified(entity::registry& registry, entity::id entity_id); + void on_celestial_body_destroyed(entity::registry& registry, entity::id entity_id); + void on_orbit_modified(entity::registry& registry, entity::id entity_id); + void on_orbit_destroyed(entity::registry& registry, entity::id entity_id); + void on_atmosphere_modified(entity::registry& registry, entity::id entity_id); + void on_atmosphere_destroyed(entity::registry& registry, entity::id entity_id); + + /// Called each time the observer is modified. + void observer_modified(); + + /// Called each time the celestial body of the reference body is modified. + void reference_body_modified(); + + /// Called each time the orbit of the reference body is modified. + void reference_orbit_modified(); + + /// Called each time the atmosphere of the reference body is modified. + void reference_atmosphere_modified(); + + /// Updates the BCBF to EUS transformation. + void update_bcbf_to_eus(const ::observer_component& observer, const ::celestial_body_component& body); + + /// Updates the ICRF to EUS transformation. + void update_icrf_to_eus(const ::celestial_body_component& body, const ::orbit_component& orbit); + + /** + * Integrates a transmittance factor due to atmospheric extinction along a ray. + * + * @param ray Ray to cast, in the EUS frame. + * @param samples Number of samples to integrate. + * + * @return Spectral transmittance factor. + */ + double3 integrate_transmittance(const ::observer_component& observer, const ::celestial_body_component& body, const ::atmosphere_component& atmosphere, geom::ray ray) const; + + /// Time since epoch, in days. + double time_days; + + /// Time since epoch, in centuries. + double time_centuries; + + /// Time scale. + double time_scale; + + /// Number of transmittance integration samples. + std::size_t transmittance_samples; + + /// Entity ID of the observer. + entity::id observer_eid; + + /// Entity ID of the reference body. + entity::id reference_body_eid; + + /// ENU to EUS transformation. + math::transformation::se3 enu_to_eus; + + /// BCBF to EUS transformation. + math::transformation::se3 bcbf_to_eus; + + /// ICRF to EUS tranformation. + math::transformation::se3 icrf_to_eus; + + scene::directional_light* sun_light; + scene::ambient_light* sky_light; + scene::directional_light* moon_light; + scene::directional_light* bounce_light; + double3 bounce_albedo; + ::render::sky_pass* sky_pass; + double3 starlight_illuminance; +}; + + +#endif // ANTKEEPER_GAME_ASTRONOMY_SYSTEM_HPP diff --git a/src/game/systems/atmosphere-system.cpp b/src/game/systems/atmosphere-system.cpp new file mode 100644 index 0000000..e9e8e50 --- /dev/null +++ b/src/game/systems/atmosphere-system.cpp @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include "game/systems/atmosphere-system.hpp" +#include +#include +#include + + +atmosphere_system::atmosphere_system(entity::registry& registry): + updatable_system(registry), + rgb_wavelengths{0, 0, 0}, + rgb_ozone_cross_sections{0, 0, 0}, + active_atmosphere_eid(entt::null), + sky_pass(nullptr) +{ + registry.on_construct<::atmosphere_component>().connect<&atmosphere_system::on_atmosphere_construct>(this); + registry.on_update<::atmosphere_component>().connect<&atmosphere_system::on_atmosphere_update>(this); + registry.on_destroy<::atmosphere_component>().connect<&atmosphere_system::on_atmosphere_destroy>(this); +} + +atmosphere_system::~atmosphere_system() +{ + registry.on_construct<::atmosphere_component>().disconnect<&atmosphere_system::on_atmosphere_construct>(this); + registry.on_update<::atmosphere_component>().disconnect<&atmosphere_system::on_atmosphere_update>(this); + registry.on_destroy<::atmosphere_component>().disconnect<&atmosphere_system::on_atmosphere_destroy>(this); +} + +void atmosphere_system::update(double t, double dt) +{} + +void atmosphere_system::set_rgb_wavelengths(const double3& wavelengths) +{ + rgb_wavelengths = wavelengths; + + // Update ozone cross sections + rgb_ozone_cross_sections = + { + physics::gas::ozone::cross_section_293k(wavelengths.x() * 1e9), + physics::gas::ozone::cross_section_293k(wavelengths.y() * 1e9), + physics::gas::ozone::cross_section_293k(wavelengths.z() * 1e9) + }; + + // Update atmosphere components + registry.view<::atmosphere_component>().each + ( + [&](entity::id entity_id, auto& component) + { + update_atmosphere(entity_id); + } + ); +} + +void atmosphere_system::set_sky_pass(::render::sky_pass* pass) +{ + sky_pass = pass; + update_sky_pass(); +} + +void atmosphere_system::set_active_atmosphere(entity::id entity_id) +{ + if (entity_id != active_atmosphere_eid) + { + active_atmosphere_eid = entity_id; + update_sky_pass(); + } +} + +void atmosphere_system::update_atmosphere(entity::id entity_id) +{ + // Get atmosphere component of the entity + ::atmosphere_component* component = registry.try_get<::atmosphere_component>(entity_id); + + // Abort if entity has no atmosphere component + if (!component) + return; + + // Calculate Rayleigh scattering coefficients + const double rayleigh_density = physics::number_density(component->rayleigh_concentration); + const double rayleigh_polarization = physics::gas::atmosphere::polarization(component->index_of_refraction, rayleigh_density); + component->rayleigh_scattering = + { + physics::gas::atmosphere::scattering(rayleigh_density, rayleigh_polarization, rgb_wavelengths.x()), + physics::gas::atmosphere::scattering(rayleigh_density, rayleigh_polarization, rgb_wavelengths.y()), + physics::gas::atmosphere::scattering(rayleigh_density, rayleigh_polarization, rgb_wavelengths.z()) + }; + + // Calculate Mie scattering and extinction coefficients + const double mie_density = physics::number_density(component->mie_concentration); + const double mie_polarization = physics::gas::atmosphere::polarization(component->index_of_refraction, mie_density); + component->mie_scattering = physics::gas::atmosphere::scattering(mie_density, mie_polarization); + component->mie_extinction = physics::gas::atmosphere::extinction(component->mie_scattering, component->mie_albedo); + + // Calculate ozone absorption coefficients + const double ozone_density = physics::number_density(component->ozone_concentration); + component->ozone_absorption = + { + physics::gas::ozone::absorption(rgb_ozone_cross_sections.x(), ozone_density), + physics::gas::ozone::absorption(rgb_ozone_cross_sections.y(), ozone_density), + physics::gas::ozone::absorption(rgb_ozone_cross_sections.z(), ozone_density) + }; + + // Update sky pass parameters + if (entity_id == active_atmosphere_eid) + { + update_sky_pass(); + } +} + +void atmosphere_system::update_sky_pass() +{ + // Abort if no sky pass set + if (!sky_pass) + return; + + // Abort if active atmosphere entity is not valid + if (!registry.valid(active_atmosphere_eid)) + return; + + // Get atmosphere component of the entity + ::atmosphere_component* component = registry.try_get<::atmosphere_component>(active_atmosphere_eid); + + // Abort if entity has no atmosphere component + if (!component) + return; + + sky_pass->set_atmosphere_upper_limit(static_cast(component->upper_limit)); + sky_pass->set_rayleigh_parameters(static_cast(component->rayleigh_scale_height), math::vector(component->rayleigh_scattering)); + sky_pass->set_mie_parameters(static_cast(component->mie_scale_height), static_cast(component->mie_scattering), static_cast(component->mie_extinction), static_cast(component->mie_anisotropy)); + sky_pass->set_ozone_parameters(static_cast(component->ozone_lower_limit), static_cast(component->ozone_upper_limit), static_cast(component->ozone_mode), math::vector(component->ozone_absorption)); + sky_pass->set_airglow_illuminance(math::vector(component->airglow_illuminance)); +} + +void atmosphere_system::on_atmosphere_construct(entity::registry& registry, entity::id entity_id) +{ + update_atmosphere(entity_id); +} + +void atmosphere_system::on_atmosphere_update(entity::registry& registry, entity::id entity_id) +{ + update_atmosphere(entity_id); +} + +void atmosphere_system::on_atmosphere_destroy(entity::registry& registry, entity::id entity_id) +{ + if (entity_id == active_atmosphere_eid) + active_atmosphere_eid = entt::null; +} + diff --git a/src/game/systems/atmosphere-system.hpp b/src/game/systems/atmosphere-system.hpp new file mode 100644 index 0000000..568f041 --- /dev/null +++ b/src/game/systems/atmosphere-system.hpp @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_ATMOSPHERE_SYSTEM_HPP +#define ANTKEEPER_GAME_ATMOSPHERE_SYSTEM_HPP + +#include "game/systems/updatable-system.hpp" +#include +#include +#include "game/components/atmosphere-component.hpp" +#include + + +/** + * Updates variables related to atmospheric scattering. + */ +class atmosphere_system: + public updatable_system +{ +public: + atmosphere_system(entity::registry& registry); + ~atmosphere_system(); + + virtual void update(double t, double dt); + + /** + * Sets the wavelengths of red, green, and blue light. + * + * @param wavelengths Vector containing the wavelengths of red (x), green (y), and blue (z) light, in meters. + */ + void set_rgb_wavelengths(const double3& wavelengths); + + void set_sky_pass(::render::sky_pass* pass); + + /** + * Sets the entity ID of the active atmosphere. + * + * @param entity_id Entity ID of the active atmosphere. + */ + void set_active_atmosphere(entity::id entity_id); + +private: + void update_atmosphere(entity::id entity_id); + void update_sky_pass(); + + void on_atmosphere_construct(entity::registry& registry, entity::id entity_id); + void on_atmosphere_update(entity::registry& registry, entity::id entity_id); + void on_atmosphere_destroy(entity::registry& registry, entity::id entity_id); + + entity::id active_atmosphere_eid; + double3 rgb_wavelengths; + double3 rgb_ozone_cross_sections; + ::atmosphere_component* atmosphere_component; + ::render::sky_pass* sky_pass; +}; + + +#endif // ANTKEEPER_GAME_ATMOSPHERE_SYSTEM_HPP diff --git a/src/game/systems/behavior-system.cpp b/src/game/systems/behavior-system.cpp new file mode 100644 index 0000000..6fce8cc --- /dev/null +++ b/src/game/systems/behavior-system.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include "game/systems/behavior-system.hpp" +#include "game/components/behavior-component.hpp" +#include + + +behavior_system::behavior_system(entity::registry& registry): + updatable_system(registry) +{} + +void behavior_system::update(double t, double dt) +{ + // registry.view().each( + // [&](entity::id entity_id, auto& behavior) + // { + // }); +} + diff --git a/src/game/systems/behavior-system.hpp b/src/game/systems/behavior-system.hpp new file mode 100644 index 0000000..c93bb35 --- /dev/null +++ b/src/game/systems/behavior-system.hpp @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_BEHAVIOR_SYSTEM_HPP +#define ANTKEEPER_GAME_BEHAVIOR_SYSTEM_HPP + +#include "game/systems/updatable-system.hpp" + +class behavior_system: + public updatable_system +{ +public: + behavior_system(entity::registry& registry); + virtual void update(double t, double dt); +}; + + +#endif // ANTKEEPER_GAME_BEHAVIOR_SYSTEM_HPP diff --git a/src/game/systems/blackbody-system.cpp b/src/game/systems/blackbody-system.cpp new file mode 100644 index 0000000..3ce4068 --- /dev/null +++ b/src/game/systems/blackbody-system.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include "game/systems/blackbody-system.hpp" +#include +#include +#include +#include +#include + + +blackbody_system::blackbody_system(entity::registry& registry): + updatable_system(registry), + illuminant(color::illuminant::deg2::d50) +{ + // Construct a range of sample wavelengths in the visible spectrum + visible_wavelengths_nm.resize(780 - 280); + std::iota(visible_wavelengths_nm.begin(), visible_wavelengths_nm.end(), 280); + + registry.on_construct<::blackbody_component>().connect<&blackbody_system::on_blackbody_construct>(this); + registry.on_update<::blackbody_component>().connect<&blackbody_system::on_blackbody_update>(this); + registry.on_construct<::celestial_body_component>().connect<&blackbody_system::on_celestial_body_construct>(this); + registry.on_update<::celestial_body_component>().connect<&blackbody_system::on_celestial_body_update>(this); +} + +blackbody_system::~blackbody_system() +{ + registry.on_construct<::blackbody_component>().disconnect<&blackbody_system::on_blackbody_construct>(this); + registry.on_update<::blackbody_component>().disconnect<&blackbody_system::on_blackbody_update>(this); + registry.on_construct<::celestial_body_component>().disconnect<&blackbody_system::on_celestial_body_construct>(this); + registry.on_update<::celestial_body_component>().disconnect<&blackbody_system::on_celestial_body_update>(this); +} + +void blackbody_system::update(double t, double dt) +{} + +void blackbody_system::set_illuminant(const math::vector2& illuminant) +{ + this->illuminant = illuminant; +} + +void blackbody_system::update_luminance(entity::id entity_id) +{ + // Get blackbody and celestial body components of the entity + auto [blackbody, celestial_body] = registry.try_get(entity_id); + + // Abort if entity is missing a blackbody or celestial body component + if (!blackbody || !celestial_body) + return; + + // Construct chromatic adaptation transform + const double3x3 cat = color::cat::matrix(illuminant, color::aces::white_point); + + // Construct a lambda function which calculates the blackbody's RGB luminance of a given wavelength + auto rgb_luminance = [temperature = blackbody->temperature, cat](double wavelength_nm) -> double3 + { + // Convert wavelength from nanometers to meters + const double wavelength_m = wavelength_nm * 1e-9; + + // Calculate the spectral intensity of the wavelength + const double spectral_radiance = physics::light::blackbody::spectral_radiance(temperature, wavelength_m); + + + // Calculate the ACEScg color of the wavelength using CIE color matching functions + double3 spectral_color = color::aces::ap1.from_xyz * cat * color::xyz::match(wavelength_nm); + + // Scale the spectral color by spectral intensity + return spectral_color * spectral_radiance * 1e-9 * physics::light::max_luminous_efficacy; + }; + + // Integrate the blackbody RGB luminance over wavelengths in the visible spectrum + blackbody->luminance = math::quadrature::simpson(rgb_luminance, visible_wavelengths_nm.begin(), visible_wavelengths_nm.end()); +} + +void blackbody_system::on_blackbody_construct(entity::registry& registry, entity::id entity_id) +{ + update_luminance(entity_id); +} + +void blackbody_system::on_blackbody_update(entity::registry& registry, entity::id entity_id) +{ + update_luminance(entity_id); +} + +void blackbody_system::on_celestial_body_construct(entity::registry& registry, entity::id entity_id) +{ + update_luminance(entity_id); +} + +void blackbody_system::on_celestial_body_update(entity::registry& registry, entity::id entity_id) +{ + update_luminance(entity_id); +} + diff --git a/src/game/systems/blackbody-system.hpp b/src/game/systems/blackbody-system.hpp new file mode 100644 index 0000000..78c8036 --- /dev/null +++ b/src/game/systems/blackbody-system.hpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_BLACKBODY_SYSTEM_HPP +#define ANTKEEPER_GAME_BLACKBODY_SYSTEM_HPP + +#include "game/systems/updatable-system.hpp" +#include +#include +#include "game/components/blackbody-component.hpp" +#include "game/components/celestial-body-component.hpp" +#include + + +/** + * Calculates the RGB luminous intensity of blackbody radiators. + */ +class blackbody_system: + public updatable_system +{ +public: + blackbody_system(entity::registry& registry); + ~blackbody_system(); + + virtual void update(double t, double dt); + + /** + * Sets the blackbody illuminant. + * + * @param illuminant CIE chromaticity coordinates of an illuminant. + */ + void set_illuminant(const math::vector2& illuminant); + +private: + void update_luminance(entity::id entity_id); + + void on_blackbody_construct(entity::registry& registry, entity::id entity_id); + void on_blackbody_update(entity::registry& registry, entity::id entity_id); + + void on_celestial_body_construct(entity::registry& registry, entity::id entity_id); + void on_celestial_body_update(entity::registry& registry, entity::id entity_id); + + math::vector2 illuminant; + std::vector visible_wavelengths_nm; +}; + + +#endif // ANTKEEPER_GAME_BLACKBODY_SYSTEM_HPP diff --git a/src/game/systems/camera-system.cpp b/src/game/systems/camera-system.cpp new file mode 100644 index 0000000..9f8367e --- /dev/null +++ b/src/game/systems/camera-system.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include "game/systems/camera-system.hpp" + + +camera_system::camera_system(entity::registry& registry): + updatable_system(registry), + viewport{0, 0, 0, 0} +{} + +void camera_system::update(double t, double dt) +{ + +} + +void camera_system::set_viewport(const float4& viewport) +{ + this->viewport = viewport; +} + diff --git a/src/game/systems/camera-system.hpp b/src/game/systems/camera-system.hpp new file mode 100644 index 0000000..949ca11 --- /dev/null +++ b/src/game/systems/camera-system.hpp @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_CAMERA_SYSTEM_HPP +#define ANTKEEPER_GAME_CAMERA_SYSTEM_HPP + +#include "game/systems/updatable-system.hpp" +#include + + +class camera_system: public updatable_system +{ +public: + camera_system(entity::registry& registry); + virtual void update(double t, double dt); + + void set_viewport(const float4& viewport); + +private: + float4 viewport; +}; + + +#endif // ANTKEEPER_GAME_CAMERA_SYSTEM_HPP + diff --git a/src/game/systems/collision-system.cpp b/src/game/systems/collision-system.cpp new file mode 100644 index 0000000..6bd5749 --- /dev/null +++ b/src/game/systems/collision-system.cpp @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include "game/systems/collision-system.hpp" +#include "game/components/transform-component.hpp" +#include "game/components/picking-component.hpp" +#include +#include +#include +#include + + +collision_system::collision_system(entity::registry& registry): + updatable_system(registry) +{ + registry.on_construct().connect<&collision_system::on_collision_construct>(this); + registry.on_update().connect<&collision_system::on_collision_update>(this); + registry.on_destroy().connect<&collision_system::on_collision_destroy>(this); +} + +void collision_system::update(double t, double dt) +{ + registry.on_construct().disconnect<&collision_system::on_collision_construct>(this); + registry.on_update().disconnect<&collision_system::on_collision_update>(this); + registry.on_destroy().disconnect<&collision_system::on_collision_destroy>(this); +} + +entity::id collision_system::pick_nearest(const geom::primitive::ray& ray, std::uint32_t flags) const +{ + entity::id nearest_eid = entt::null; + float nearest_distance = std::numeric_limits::infinity(); + + // For each entity with picking and transform components + registry.view().each + ( + [&](entity::id entity_id, const auto& picking, const auto& transform) + { + // Skip entity if picking flags don't match + if (!~(flags | picking.flags)) + return; + + // Transform picking sphere + const geom::primitive::sphere sphere = + { + transform.world * picking.sphere.center, + picking.sphere.radius * math::max(transform.world.scale) + }; + + // Test for intersection between ray and sphere + auto result = geom::primitive::intersection(ray, sphere); + if (result) + { + float t0 = std::get<0>(*result); + float t1 = std::get<1>(*result); + + if (t0 < nearest_distance) + { + nearest_eid = entity_id; + nearest_distance = t0; + } + } + } + ); + + return nearest_eid; +} + +entity::id collision_system::pick_nearest(const float3& origin, const float3& normal, std::uint32_t flags) const +{ + entity::id nearest_eid = entt::null; + float nearest_sqr_distance = std::numeric_limits::infinity(); + + // Construct picking plane + const geom::primitive::plane picking_plane = geom::primitive::plane(origin, normal); + + // For each entity with picking and transform components + registry.view().each + ( + [&](entity::id entity_id, const auto& picking, const auto& transform) + { + // Skip entity if picking flags don't match + if (!~(flags | picking.flags)) + return; + + // Transform picking sphere center + float3 picking_sphere_center = transform.world * picking.sphere.center; + + // Skip entity if picking sphere center has negative distance from picking plane + if (picking_plane.distance(picking_sphere_center) < 0.0f) + return; + + // Measure distance from picking plane origin to picking sphere center + const float sqr_distance = math::sqr_distance(picking_sphere_center, origin); + + // Check if entity is nearer than the current nearest entity + if (sqr_distance < nearest_sqr_distance) + { + nearest_eid = entity_id; + nearest_sqr_distance = sqr_distance; + } + } + ); + + return nearest_eid; +} + +void collision_system::on_collision_construct(entity::registry& registry, entity::id entity_id) +{} + +void collision_system::on_collision_update(entity::registry& registry, entity::id entity_id) +{} + +void collision_system::on_collision_destroy(entity::registry& registry, entity::id entity_id) +{} + diff --git a/src/game/systems/collision-system.hpp b/src/game/systems/collision-system.hpp new file mode 100644 index 0000000..312ed06 --- /dev/null +++ b/src/game/systems/collision-system.hpp @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_COLLISION_SYSTEM_HPP +#define ANTKEEPER_GAME_COLLISION_SYSTEM_HPP + +#include "game/systems/updatable-system.hpp" +#include +#include "game/components/collision-component.hpp" +#include + + +/** + * Maintains a spatially partitioned set of collision meshes. + */ +class collision_system: public updatable_system +{ +public: + collision_system(entity::registry& registry); + virtual void update(double t, double dt); + + /** + * Picks the nearest entity with the specified picking flags that intersects a ray. + * + * @param ray Picking ray. + * @param flags Picking flags. + * + * @return ID of the picked entity, or `entt::null` if no entity was picked. + */ + entity::id pick_nearest(const geom::primitive::ray& ray, std::uint32_t flags) const; + + /** + * Picks the nearest entity with the specified picking flags that has a non-negative distance from a plane. + * + * @param origin Origin of the picking plane. + * @param normal Picking plane normal direction. + * @param flags Picking flags. + * + * @return ID of the picked entity, or `entt::null` if no entity was picked. + */ + entity::id pick_nearest(const float3& origin, const float3& normal, std::uint32_t flags) const; + +private: + void on_collision_construct(entity::registry& registry, entity::id entity_id); + void on_collision_update(entity::registry& registry, entity::id entity_id); + void on_collision_destroy(entity::registry& registry, entity::id entity_id); +}; + + +#endif // ANTKEEPER_GAME_COLLISION_SYSTEM_HPP + diff --git a/src/game/systems/constraint-system.cpp b/src/game/systems/constraint-system.cpp new file mode 100644 index 0000000..8ba2f18 --- /dev/null +++ b/src/game/systems/constraint-system.cpp @@ -0,0 +1,350 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include "game/systems/constraint-system.hpp" +#include "game/components/constraint-stack-component.hpp" +#include +#include + +constraint_system::constraint_system(entity::registry& registry): + updatable_system(registry) +{ + registry.on_construct().connect<&constraint_system::on_constraint_stack_update>(this); + registry.on_update().connect<&constraint_system::on_constraint_stack_update>(this); + registry.on_destroy().connect<&constraint_system::on_constraint_stack_update>(this); +} + +constraint_system::~constraint_system() +{ + registry.on_construct().disconnect<&constraint_system::on_constraint_stack_update>(this); + registry.on_update().disconnect<&constraint_system::on_constraint_stack_update>(this); + registry.on_destroy().disconnect<&constraint_system::on_constraint_stack_update>(this); +} + +void constraint_system::update(double t, double dt) +{ + // For each entity with transform and constraint stack components + registry.view().each + ( + [&](entity::id transform_eid, auto& transform, auto& stack) + { + // Init world-space transform + transform.world = transform.local; + + // Get entity ID of first constraint + entity::id constraint_eid = stack.head; + + // Consecutively apply constraints + while (registry.valid(constraint_eid)) + { + // Get constraint stack node of the constraint + const constraint_stack_node_component* node = registry.try_get(constraint_eid); + + // Abort if constraint is missing a constraint stack node + if (!node) + break; + + // Apply constraint if enabled + if (node->active) + handle_constraint(transform, constraint_eid, static_cast(dt)); + + // Get entity ID of next constraint in the stack + constraint_eid = node->next; + } + } + ); +} + +void constraint_system::evaluate(entity::id entity_id) +{ + if (!registry.valid(entity_id)) + return; + + // Get transform and constraint stack components of the entity + const auto [transform, stack] = registry.try_get(entity_id); + + if (!transform || !stack) + return; + + // Init world-space transform + transform->world = transform->local; + + // Get entity ID of first constraint + entity::id constraint_eid = stack->head; + + // Consecutively apply constraints + while (registry.valid(constraint_eid)) + { + // Get constraint stack node of the constraint + const constraint_stack_node_component* node = registry.try_get(constraint_eid); + + // Abort if constraint is missing a constraint stack node + if (!node) + break; + + // Apply constraint if enabled + if (node->active) + handle_constraint(*transform, constraint_eid, 0.0f); + + // Get entity ID of next constraint in the stack + constraint_eid = node->next; + } +} + +void constraint_system::on_constraint_stack_update(entity::registry& registry, entity::id constraint_stack_eid) +{ + registry.sort + ( + [](const auto& lhs, const auto& rhs) + { + return lhs.priority < rhs.priority; + } + ); +} + +void constraint_system::handle_constraint(transform_component& transform, entity::id constraint_eid, float dt) +{ + if (auto constraint = registry.try_get(constraint_eid); constraint) + handle_copy_translation_constraint(transform, *constraint); + else if (auto constraint = registry.try_get(constraint_eid); constraint) + handle_copy_rotation_constraint(transform, *constraint); + else if (auto constraint = registry.try_get(constraint_eid); constraint) + handle_copy_scale_constraint(transform, *constraint); + else if (auto constraint = registry.try_get(constraint_eid); constraint) + handle_copy_transform_constraint(transform, *constraint); + else if (auto constraint = registry.try_get(constraint_eid); constraint) + handle_track_to_constraint(transform, *constraint); + else if (auto constraint = registry.try_get(constraint_eid); constraint) + handle_three_dof_constraint(transform, *constraint); + else if (auto constraint = registry.try_get(constraint_eid); constraint) + handle_pivot_constraint(transform, *constraint); + else if (auto constraint = registry.try_get(constraint_eid); constraint) + handle_child_of_constraint(transform, *constraint); + else if (auto constraint = registry.try_get(constraint_eid); constraint) + handle_spring_to_constraint(transform, *constraint, dt); + else if (auto constraint = registry.try_get(constraint_eid); constraint) + handle_spring_translation_constraint(transform, *constraint, dt); + else if (auto constraint = registry.try_get(constraint_eid); constraint) + handle_spring_rotation_constraint(transform, *constraint, dt); + else if (auto constraint = registry.try_get(constraint_eid); constraint) + handle_ease_to_constraint(transform, *constraint, dt); +} + +void constraint_system::handle_child_of_constraint(transform_component& transform, const child_of_constraint& constraint) +{ + if (registry.valid(constraint.target)) + { + const transform_component* target_transform = registry.try_get(constraint.target); + if (target_transform) + { + transform.world = target_transform->world * transform.world; + } + } +} + +void constraint_system::handle_copy_rotation_constraint(transform_component& transform, const copy_rotation_constraint& constraint) +{ + if (registry.valid(constraint.target)) + { + const transform_component* target_transform = registry.try_get(constraint.target); + if (target_transform) + { + transform.world.rotation = target_transform->world.rotation; + } + } +} + +void constraint_system::handle_copy_scale_constraint(transform_component& transform, const copy_scale_constraint& constraint) +{ + if (registry.valid(constraint.target)) + { + const transform_component* target_transform = registry.try_get(constraint.target); + if (target_transform) + { + const auto& target_scale = target_transform->world.scale; + + if (constraint.copy_x) + transform.world.scale.x() = target_scale.x(); + if (constraint.copy_y) + transform.world.scale.y() = target_scale.y(); + if (constraint.copy_z) + transform.world.scale.z() = target_scale.z(); + } + } +} + +void constraint_system::handle_copy_transform_constraint(transform_component& transform, const copy_transform_constraint& constraint) +{ + if (registry.valid(constraint.target)) + { + const transform_component* target_transform = registry.try_get(constraint.target); + if (target_transform) + { + transform.world = target_transform->world; + } + } +} + +void constraint_system::handle_copy_translation_constraint(transform_component& transform, const copy_translation_constraint& constraint) +{ + if (registry.valid(constraint.target)) + { + const transform_component* target_transform = registry.try_get(constraint.target); + if (target_transform) + { + const auto& target_translation = target_transform->world.translation; + + if (constraint.offset) + { + if (constraint.copy_x) + transform.world.translation.x() += (constraint.invert_x) ? -target_translation.x() : target_translation.x(); + if (constraint.copy_y) + transform.world.translation.y() += (constraint.invert_y) ? -target_translation.y() : target_translation.y(); + if (constraint.copy_z) + transform.world.translation.z() += (constraint.invert_z) ? -target_translation.z() : target_translation.z(); + } + else + { + if (constraint.copy_x) + transform.world.translation.x() = (constraint.invert_x) ? -target_translation.x() : target_translation.x(); + if (constraint.copy_y) + transform.world.translation.y() = (constraint.invert_y) ? -target_translation.y() : target_translation.y(); + if (constraint.copy_z) + transform.world.translation.z() = (constraint.invert_z) ? -target_translation.z() : target_translation.z(); + } + } + } +} + +void constraint_system::handle_ease_to_constraint(transform_component& transform, ease_to_constraint& constraint, float dt) +{ + if (constraint.function && registry.valid(constraint.target)) + { + const transform_component* target_transform = registry.try_get(constraint.target); + if (target_transform) + { + if (constraint.t < constraint.duration) + { + const float a = constraint.t / constraint.duration; + transform.world.translation = constraint.function(constraint.start, target_transform->world.translation, a); + } + else + { + transform.world.translation = target_transform->world.translation; + } + + constraint.t += dt; + } + } +} + +void constraint_system::handle_pivot_constraint(transform_component& transform, const pivot_constraint& constraint) +{ + if (registry.valid(constraint.target)) + { + const transform_component* target_transform = registry.try_get(constraint.target); + if (target_transform) + { + // Get pivot center point + const float3 pivot_center = target_transform->world.translation + constraint.offset; + + // Pivot translation + transform.world.translation = pivot_center + transform.world.rotation * (transform.world.translation - pivot_center); + } + } +} + +void constraint_system::handle_spring_rotation_constraint(transform_component& transform, spring_rotation_constraint& constraint, float dt) +{ + // Solve yaw, pitch, and roll angle spring + solve_numeric_spring(constraint.spring, dt); + + // Build yaw, pitch, and roll quaternions + const math::quaternion yaw = math::angle_axis(constraint.spring.x0[0], {0.0f, 1.0f, 0.0f}); + const math::quaternion pitch = math::angle_axis(constraint.spring.x0[1], {-1.0f, 0.0f, 0.0f}); + const math::quaternion roll = math::angle_axis(constraint.spring.x0[2], {0.0f, 0.0f, -1.0f}); + + // Update transform rotation + transform.world.rotation = math::normalize(yaw * pitch * roll); +} + +void constraint_system::handle_spring_to_constraint(transform_component& transform, spring_to_constraint& constraint, float dt) +{ + if (registry.valid(constraint.target)) + { + const transform_component* target_transform = registry.try_get(constraint.target); + if (target_transform) + { + // Spring translation + if (constraint.spring_translation) + { + // Update translation spring target + constraint.translation.x1 = target_transform->world.translation; + + // Solve translation spring + solve_numeric_spring(constraint.translation, dt); + + // Update transform translation + transform.world.translation = constraint.translation.x0; + } + + // Spring rotation + if (constraint.spring_rotation) + { + // Update rotation spring target + constraint.rotation.x1 = float4(target_transform->world.rotation); + + // Solve rotation spring + solve_numeric_spring(constraint.rotation, dt); + + // Update transform rotation + transform.world.rotation = math::normalize(math::quaternion{constraint.rotation.x0[0], constraint.rotation.x0[1], constraint.rotation.x0[2], constraint.rotation.x0[3]}); + } + } + } +} + +void constraint_system::handle_spring_translation_constraint(transform_component& transform, spring_translation_constraint& constraint, float dt) +{ + // Solve translation spring + solve_numeric_spring(constraint.spring, dt); + + // Update transform translation + transform.world.translation = constraint.spring.x0; +} + +void constraint_system::handle_three_dof_constraint(transform_component& transform, const three_dof_constraint& constraint) +{ + const math::quaternion yaw = math::angle_axis(constraint.yaw, {0.0f, 1.0f, 0.0f}); + const math::quaternion pitch = math::angle_axis(constraint.pitch, {-1.0f, 0.0f, 0.0f}); + const math::quaternion roll = math::angle_axis(constraint.roll, {0.0f, 0.0f, -1.0f}); + transform.world.rotation = math::normalize(yaw * pitch * roll); +} + +void constraint_system::handle_track_to_constraint(transform_component& transform, const track_to_constraint& constraint) +{ + if (registry.valid(constraint.target)) + { + const transform_component* target_transform = registry.try_get(constraint.target); + if (target_transform) + { + transform.world.rotation = math::look_rotation(math::normalize(math::sub(target_transform->world.translation, transform.world.translation)), constraint.up); + } + } +} diff --git a/src/game/systems/constraint-system.hpp b/src/game/systems/constraint-system.hpp new file mode 100644 index 0000000..75e4389 --- /dev/null +++ b/src/game/systems/constraint-system.hpp @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_CONSTRAINT_SYSTEM_HPP +#define ANTKEEPER_GAME_CONSTRAINT_SYSTEM_HPP + +#include "game/systems/updatable-system.hpp" +#include "game/components/transform-component.hpp" +#include "game/constraints/child-of-constraint.hpp" +#include "game/constraints/copy-rotation-constraint.hpp" +#include "game/constraints/copy-scale-constraint.hpp" +#include "game/constraints/copy-transform-constraint.hpp" +#include "game/constraints/copy-translation-constraint.hpp" +#include "game/constraints/ease-to-constraint.hpp" +#include "game/constraints/pivot-constraint.hpp" +#include "game/constraints/spring-rotation-constraint.hpp" +#include "game/constraints/spring-to-constraint.hpp" +#include "game/constraints/spring-translation-constraint.hpp" +#include "game/constraints/three-dof-constraint.hpp" +#include "game/constraints/track-to-constraint.hpp" +#include + + +/** + * Applies constraint stacks to transform components. + */ +class constraint_system: + public updatable_system +{ +public: + constraint_system(entity::registry& registry); + ~constraint_system(); + + virtual void update(double t, double dt); + + /** + * Manually evaluates an entity's constraints. + * + * @param entity_id ID of a constrained entity. + */ + void evaluate(entity::id entity_id); + +private: + void on_constraint_stack_update(entity::registry& registry, entity::id constraint_stack_eid); + + void handle_constraint(transform_component& transform, entity::id constraint_eid, float dt); + void handle_child_of_constraint(transform_component& transform, const child_of_constraint& constraint); + void handle_copy_rotation_constraint(transform_component& transform, const copy_rotation_constraint& constraint); + void handle_copy_scale_constraint(transform_component& transform, const copy_scale_constraint& constraint); + void handle_copy_transform_constraint(transform_component& transform, const copy_transform_constraint& constraint); + void handle_copy_translation_constraint(transform_component& transform, const copy_translation_constraint& constraint); + void handle_ease_to_constraint(transform_component& transform, ease_to_constraint& constraint, float dt); + void handle_pivot_constraint(transform_component& transform, const pivot_constraint& constraint); + void handle_spring_rotation_constraint(transform_component& transform, spring_rotation_constraint& constraint, float dt); + void handle_spring_to_constraint(transform_component& transform, spring_to_constraint& constraint, float dt); + void handle_spring_translation_constraint(transform_component& transform, spring_translation_constraint& constraint, float dt); + void handle_three_dof_constraint(transform_component& transform, const three_dof_constraint& constraint); + void handle_track_to_constraint(transform_component& transform, const track_to_constraint& constraint); +}; + + +#endif // ANTKEEPER_GAME_CONSTRAINT_SYSTEM_HPP diff --git a/src/game/systems/locomotion-system.cpp b/src/game/systems/locomotion-system.cpp new file mode 100644 index 0000000..80bb11d --- /dev/null +++ b/src/game/systems/locomotion-system.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include "game/systems/locomotion-system.hpp" +#include "game/components/collision-component.hpp" +#include "game/components/locomotion-component.hpp" +#include "game/components/transform-component.hpp" +#include + + +locomotion_system::locomotion_system(entity::registry& registry): + updatable_system(registry) +{} + +void locomotion_system::update(double t, double dt) +{ + registry.view().each( + [&](entity::id entity_id, auto& transform, auto& locomotion) + { + }); +} + diff --git a/src/game/systems/locomotion-system.hpp b/src/game/systems/locomotion-system.hpp new file mode 100644 index 0000000..ba120ce --- /dev/null +++ b/src/game/systems/locomotion-system.hpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_LOCOMOTION_SYSTEM_HPP +#define ANTKEEPER_GAME_LOCOMOTION_SYSTEM_HPP + +#include "game/systems/updatable-system.hpp" + + +class locomotion_system: + public updatable_system +{ +public: + locomotion_system(entity::registry& registry); + virtual void update(double t, double dt); +}; + + +#endif // ANTKEEPER_GAME_LOCOMOTION_SYSTEM_HPP diff --git a/src/game/systems/metamorphosis-system.cpp b/src/game/systems/metamorphosis-system.cpp new file mode 100644 index 0000000..a928028 --- /dev/null +++ b/src/game/systems/metamorphosis-system.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include "game/systems/metamorphosis-system.hpp" +#include + + +metamorphosis_system::metamorphosis_system(entity::registry& registry): + updatable_system(registry), + time_scale(1.0f) +{} + +void metamorphosis_system::update(double t, double dt) +{ + +} + +void metamorphosis_system::set_time_scale(float scale) +{ + time_scale = scale; +} + diff --git a/src/game/systems/metamorphosis-system.hpp b/src/game/systems/metamorphosis-system.hpp new file mode 100644 index 0000000..225f1cd --- /dev/null +++ b/src/game/systems/metamorphosis-system.hpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_METAMORPHOSIS_SYSTEM_HPP +#define ANTKEEPER_GAME_METAMORPHOSIS_SYSTEM_HPP + +#include "game/systems/updatable-system.hpp" + + +class metamorphosis_system: + public updatable_system +{ +public: + metamorphosis_system(entity::registry& registry); + virtual void update(double t, double dt); + + /** + * Sets the factor by which the timestep `dt` will be scaled. + * + * @param scale Factor by which to scale the timestep. + */ + void set_time_scale(float scale); + +private: + float time_scale; +}; + + +#endif // ANTKEEPER_GAME_METAMORPHOSIS_SYSTEM_HPP diff --git a/src/game/systems/morphogenesis-system.cpp b/src/game/systems/morphogenesis-system.cpp new file mode 100644 index 0000000..97a6553 --- /dev/null +++ b/src/game/systems/morphogenesis-system.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include "game/systems/morphogenesis-system.hpp" +#include + + +morphogenesis_system::morphogenesis_system(entity::registry& registry): + updatable_system(registry) +{} + +void morphogenesis_system::update(double t, double dt) +{} + diff --git a/src/game/systems/morphogenesis-system.hpp b/src/game/systems/morphogenesis-system.hpp new file mode 100644 index 0000000..4c6ec35 --- /dev/null +++ b/src/game/systems/morphogenesis-system.hpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_MORPHOGENESIS_SYSTEM_HPP +#define ANTKEEPER_GAME_MORPHOGENESIS_SYSTEM_HPP + +#include "game/systems/updatable-system.hpp" + +/** + * Generates 3D models from genomes. + */ +class morphogenesis_system: + public updatable_system +{ +public: + morphogenesis_system(entity::registry& registry); + virtual void update(double t, double dt); + +private: +}; + + +#endif // ANTKEEPER_GAME_MORPHOGENESIS_SYSTEM_HPP diff --git a/src/game/systems/orbit-system.cpp b/src/game/systems/orbit-system.cpp new file mode 100644 index 0000000..358f27e --- /dev/null +++ b/src/game/systems/orbit-system.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include "game/systems/orbit-system.hpp" +#include + + +orbit_system::orbit_system(entity::registry& registry): + updatable_system(registry), + ephemeris(nullptr), + time(0.0), + time_scale(1.0) +{ + registry.on_construct<::orbit_component>().connect<&orbit_system::on_orbit_construct>(this); + registry.on_update<::orbit_component>().connect<&orbit_system::on_orbit_update>(this); +} + +orbit_system::~orbit_system() +{ + registry.on_construct<::orbit_component>().disconnect<&orbit_system::on_orbit_construct>(this); + registry.on_update<::orbit_component>().disconnect<&orbit_system::on_orbit_update>(this); +} + +void orbit_system::update(double t, double dt) +{ + // Add scaled timestep to current time + set_time(time + dt * time_scale); + + if (!ephemeris) + return; + + // Calculate positions of ephemeris items, in meters + for (int i: ephemeris_indices) + positions[i] = (*ephemeris)[i].position(time) * 1000.0; + + // Propagate orbits + registry.view().each( + [&](entity::id entity_eid, auto& orbit) + { + orbit.position = positions[orbit.ephemeris_index] * orbit.scale; + + entity::id parent_id = orbit.parent; + while (parent_id != entt::null) + { + const orbit_component& parent_orbit = registry.get(parent_id); + orbit.position += positions[parent_orbit.ephemeris_index] * parent_orbit.scale; + parent_id = parent_orbit.parent; + } + }); +} + +void orbit_system::set_ephemeris(const physics::orbit::ephemeris* ephemeris) +{ + this->ephemeris = ephemeris; + positions.resize((ephemeris) ? ephemeris->size() : 0); +} + +void orbit_system::set_time(double time) +{ + this->time = time; +} + +void orbit_system::set_time_scale(double scale) +{ + time_scale = scale; +} + +void orbit_system::on_orbit_construct(entity::registry& registry, entity::id entity_id) +{ + const ::orbit_component& component = registry.get<::orbit_component>(entity_id); + ephemeris_indices.insert(component.ephemeris_index); +} + +void orbit_system::on_orbit_update(entity::registry& registry, entity::id entity_id) +{ + const ::orbit_component& component = registry.get<::orbit_component>(entity_id); + ephemeris_indices.insert(component.ephemeris_index); +} + diff --git a/src/game/systems/orbit-system.hpp b/src/game/systems/orbit-system.hpp new file mode 100644 index 0000000..128fa99 --- /dev/null +++ b/src/game/systems/orbit-system.hpp @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_ORBIT_SYSTEM_HPP +#define ANTKEEPER_GAME_ORBIT_SYSTEM_HPP + +#include "game/systems/updatable-system.hpp" +#include +#include +#include "game/components/orbit-component.hpp" +#include +#include + +/** + * Updates the Cartesian position and velocity of orbiting bodies given their Keplerian orbital elements and the current time. + */ +class orbit_system: + public updatable_system +{ +public: + orbit_system(entity::registry& registry); + ~orbit_system(); + + /** + * Scales then adds the timestep `dt` to the current time, then recalculates the positions of orbiting bodies. + * + * @param t Time, in seconds. + * @param dt Delta time, in seconds. + */ + virtual void update(double t, double dt); + + /** + * Sets the current time. + * + * @param time Time, in days. + */ + void set_time(double time); + + /** + * Sets the factor by which the timestep `dt` will be scaled before being added to the current time. + * + * @param scale Factor by which to scale the timestep. + */ + void set_time_scale(double scale); + + /** + * Sets the ephemeris used to calculate orbital positions. + * + * @param ephemeris Ephemeris. + */ + void set_ephemeris(const physics::orbit::ephemeris* ephemeris); + +private: + void on_orbit_construct(entity::registry& registry, entity::id entity_id); + void on_orbit_update(entity::registry& registry, entity::id entity_id); + + const physics::orbit::ephemeris* ephemeris; + double time; + double time_scale; + std::vector positions; + std::unordered_set ephemeris_indices; +}; + + +#endif // ANTKEEPER_GAME_ORBIT_SYSTEM_HPP diff --git a/src/game/systems/render-system.cpp b/src/game/systems/render-system.cpp new file mode 100644 index 0000000..cba828d --- /dev/null +++ b/src/game/systems/render-system.cpp @@ -0,0 +1,292 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include "game/systems/render-system.hpp" +#include "game/components/transform-component.hpp" +#include "game/components/camera-component.hpp" +#include +#include +#include +#include + + +render_system::render_system(entity::registry& registry): + updatable_system(registry), + t(0.0), + dt(0.0), + renderer(nullptr) +{ + registry.on_construct().connect<&render_system::on_model_construct>(this); + registry.on_update().connect<&render_system::on_model_update>(this); + registry.on_destroy().connect<&render_system::on_model_destroy>(this); + registry.on_construct().connect<&render_system::on_light_construct>(this); + registry.on_update().connect<&render_system::on_light_update>(this); + registry.on_destroy().connect<&render_system::on_light_destroy>(this); +} + +render_system::~render_system() +{ + registry.on_construct().disconnect<&render_system::on_model_construct>(this); + registry.on_update().disconnect<&render_system::on_model_update>(this); + registry.on_destroy().disconnect<&render_system::on_model_destroy>(this); + registry.on_construct().disconnect<&render_system::on_light_construct>(this); + registry.on_update().disconnect<&render_system::on_light_update>(this); + registry.on_destroy().disconnect<&render_system::on_light_destroy>(this); +} + +void render_system::update(double t, double dt) +{ + this->t = t; + this->dt = dt; + + // Update model instance transforms + registry.view().each + ( + [this](entity::id entity_id, auto& transform, auto& model) + { + scene::model_instance* instance = model_instances[entity_id]; + + instance->set_transform(transform.world); + if (transform.warp) + { + instance->get_transform_tween().update(); + instance->update_tweens(); + transform.warp = false; + } + } + ); + + // Update camera transforms + registry.view().each + ( + [this](entity::id entity_id, auto& transform, auto& camera) + { + camera.object->set_transform(transform.world); + if (transform.warp) + { + camera.object->get_transform_tween().update(); + camera.object->update_tweens(); + transform.warp = false; + } + } + ); + + // Update light transforms + registry.view().each + ( + [this](entity::id entity_id, auto& transform, auto& light) + { + scene::light* light_object = lights[entity_id]; + + light_object->set_transform(transform.world); + if (transform.warp) + { + light_object->get_transform_tween().update(); + light_object->update_tweens(); + transform.warp = false; + } + } + ); +} + +void render_system::draw(double alpha) +{ + if (renderer) + { + for (const scene::collection* collection: layers) + { + renderer->render(static_cast(t + dt * alpha), static_cast(dt), static_cast(alpha), *collection); + } + } +} + +void render_system::add_layer(scene::collection* layer) +{ + layers.push_back(layer); +} + +void render_system::remove_layers() +{ + layers.clear(); +} + +void render_system::set_renderer(render::renderer* renderer) +{ + this->renderer = renderer; +} + +scene::model_instance* render_system::get_model_instance(entity::id entity_id) +{ + if (auto it = model_instances.find(entity_id); it != model_instances.end()) + return it->second; + return nullptr; +} + +scene::light* render_system::get_light(entity::id entity_id) +{ + if (auto it = lights.find(entity_id); it != lights.end()) + return it->second; + return nullptr; +} + +void render_system::update_model_and_materials(entity::id entity_id, model_component& model) +{ + if (auto model_it = model_instances.find(entity_id); model_it != model_instances.end()) + { + model_it->second->set_model(model.render_model); + model_it->second->set_instanced((model.instance_count > 0), model.instance_count); + + for (auto material_it = model.materials.begin(); material_it != model.materials.end(); ++material_it) + { + model_it->second->set_material(material_it->first, material_it->second); + } + + // Add model instance to its specified layers + for (std::size_t i = 0; i < std::min(layers.size(), (sizeof(model.layers) << 3)); ++i) + { + layers[i]->remove_object(model_it->second); + + if ((model.layers >> i) & 1) + { + layers[i]->add_object(model_it->second); + } + } + } +} + +void render_system::update_light(entity::id entity_id, ::light_component& component) +{ + if (auto light_it = lights.find(entity_id); light_it != lights.end()) + { + scene::light* light = light_it->second; + + light->set_color(component.color); + light->set_intensity(component.intensity); + + switch (light->get_light_type()) + { + case scene::light_type::point: + { + scene::point_light* point = static_cast(light); + point->set_attenuation(component.attenuation); + break; + } + + case scene::light_type::spot: + { + scene::spot_light* spot = static_cast(light); + spot->set_attenuation(component.attenuation); + spot->set_cutoff(component.cutoff); + break; + } + + default: + break; + } + } +} + +void render_system::on_model_construct(entity::registry& registry, entity::id entity_id) +{ + ::model_component& component = registry.get<::model_component>(entity_id); + + scene::model_instance* model_instance = new scene::model_instance(); + model_instances[entity_id] = model_instance; + update_model_and_materials(entity_id, component); +} + +void render_system::on_model_update(entity::registry& registry, entity::id entity_id) +{ + ::model_component& component = registry.get<::model_component>(entity_id); + update_model_and_materials(entity_id, component); +} + +void render_system::on_model_destroy(entity::registry& registry, entity::id entity_id) +{ + if (auto it = model_instances.find(entity_id); it != model_instances.end()) + { + scene::model_instance* model_instance = it->second; + + // Remove model instance from all layers + for (scene::collection* layer: layers) + layer->remove_object(model_instance); + + model_instances.erase(it); + delete model_instance; + } +} + +void render_system::on_light_construct(entity::registry& registry, entity::id entity_id) +{ + ::light_component& component = registry.get<::light_component>(entity_id); + + scene::light* light = nullptr; + + switch (component.type) + { + case scene::light_type::ambient: + light = new scene::ambient_light(); + break; + + case scene::light_type::directional: + light = new scene::directional_light(); + break; + + case scene::light_type::point: + light = new scene::point_light(); + break; + + case scene::light_type::spot: + light = new scene::spot_light(); + break; + + default: + break; + } + + if (light) + { + lights[entity_id] = light; + for (scene::collection* layer: layers) + layer->add_object(light); + + update_light(entity_id, component); + } +} + +void render_system::on_light_update(entity::registry& registry, entity::id entity_id) +{ + ::light_component& component = registry.get<::light_component>(entity_id); + update_light(entity_id, component); +} + +void render_system::on_light_destroy(entity::registry& registry, entity::id entity_id) +{ + if (auto it = lights.find(entity_id); it != lights.end()) + { + scene::light* light = it->second; + + for (scene::collection* layer: layers) + layer->remove_object(light); + + lights.erase(it); + delete light; + } +} + diff --git a/src/game/systems/render-system.hpp b/src/game/systems/render-system.hpp new file mode 100644 index 0000000..74b4ad5 --- /dev/null +++ b/src/game/systems/render-system.hpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_RENDER_SYSTEM_HPP +#define ANTKEEPER_GAME_RENDER_SYSTEM_HPP + +#include "game/systems/updatable-system.hpp" +#include +#include +#include +#include "game/components/model-component.hpp" +#include "game/components/light-component.hpp" +#include +#include +#include +#include + + +class render_system: public updatable_system +{ +public: + render_system(entity::registry& registry); + ~render_system(); + + virtual void update(double t, double dt); + + void draw(double alpha); + + + void add_layer(scene::collection* layer); + void remove_layers(); + + void set_renderer(::render::renderer* renderer); + + scene::model_instance* get_model_instance(entity::id entity_id); + scene::light* get_light(entity::id entity_id); + +private: + void update_model_and_materials(entity::id entity_id, ::model_component& model); + void update_light(entity::id entity_id, ::light_component& component); + + void on_model_construct(entity::registry& registry, entity::id entity_id); + void on_model_update(entity::registry& registry, entity::id entity_id); + void on_model_destroy(entity::registry& registry, entity::id entity_id); + void on_light_construct(entity::registry& registry, entity::id entity_id); + void on_light_update(entity::registry& registry, entity::id entity_id); + void on_light_destroy(entity::registry& registry, entity::id entity_id); + + double t; + double dt; + ::render::renderer* renderer; + std::vector layers; + std::unordered_map model_instances; + std::unordered_map lights; +}; + + +#endif // ANTKEEPER_GAME_RENDER_SYSTEM_HPP + diff --git a/src/game/systems/spatial-system.cpp b/src/game/systems/spatial-system.cpp new file mode 100644 index 0000000..b4c9d17 --- /dev/null +++ b/src/game/systems/spatial-system.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include "game/systems/spatial-system.hpp" +#include "game/components/transform-component.hpp" +#include "game/components/constraint-stack-component.hpp" + + +spatial_system::spatial_system(entity::registry& registry): + updatable_system(registry), + updated_unconstrained_transforms(registry, entt::collector.update().where(entt::exclude)) +{} + +void spatial_system::update(double t, double dt) +{ + // Update world-space transforms of all updated, unconstrained transforms + for (const auto transform_eid: updated_unconstrained_transforms) + { + auto& transform = registry.get(transform_eid); + transform.world = transform.local; + } + updated_unconstrained_transforms.clear(); +} + diff --git a/src/game/systems/spatial-system.hpp b/src/game/systems/spatial-system.hpp new file mode 100644 index 0000000..076ada7 --- /dev/null +++ b/src/game/systems/spatial-system.hpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_SPATIAL_SYSTEM_HPP +#define ANTKEEPER_GAME_SPATIAL_SYSTEM_HPP + +#include "game/systems/updatable-system.hpp" +#include + + +class spatial_system: + public updatable_system +{ +public: + spatial_system(entity::registry& registry); + virtual void update(double t, double dt); + +private: + /// Observes entities with updated, unconstrained transforms. + entt::observer updated_unconstrained_transforms; +}; + + +#endif // ANTKEEPER_GAME_SPATIAL_SYSTEM_HPP diff --git a/src/game/systems/spring-system.cpp b/src/game/systems/spring-system.cpp new file mode 100644 index 0000000..4c9c391 --- /dev/null +++ b/src/game/systems/spring-system.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include "game/systems/spring-system.hpp" +#include "game/components/spring-component.hpp" +#include + + +spring_system::spring_system(entity::registry& registry): + updatable_system(registry) +{} + +spring_system::~spring_system() +{} + +void spring_system::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); + } + ); +} + diff --git a/src/game/systems/spring-system.hpp b/src/game/systems/spring-system.hpp new file mode 100644 index 0000000..fc9ed6f --- /dev/null +++ b/src/game/systems/spring-system.hpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_SPRING_SYSTEM_HPP +#define ANTKEEPER_GAME_SPRING_SYSTEM_HPP + +#include "game/systems/updatable-system.hpp" + +/** + * Solves numeric springs. + */ +class spring_system: + public updatable_system +{ +public: + spring_system(entity::registry& registry); + ~spring_system(); + + virtual void update(double t, double dt); +}; + + +#endif // ANTKEEPER_GAME_SPRING_SYSTEM_HPP diff --git a/src/game/systems/steering-system.cpp b/src/game/systems/steering-system.cpp new file mode 100644 index 0000000..a8e81e0 --- /dev/null +++ b/src/game/systems/steering-system.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include "game/systems/steering-system.hpp" +#include "game/components/steering-component.hpp" +#include "game/components/transform-component.hpp" +#include +#include +#include +#include +#include + +steering_system::steering_system(entity::registry& registry): + updatable_system(registry) +{} + +void steering_system::update(double t, double dt) +{ + registry.view().each + ( + [&](entity::id entity_id, auto& steering, auto& transform) + { + auto& agent = steering.agent; + + // Update agent orientation + agent.orientation = transform.local.rotation; + + // Accumulate forces + float3 force = {0, 0, 0}; + if (steering.wander_weight) + { + //force += ai::steering::behavior::wander_2d(agent, steering.wander_noise * dt, steering.wander_distance, steering.wander_radius, steering.wander_angle) * steering.wander_weight; + force += ai::steering::behavior::wander_3d(agent, steering.wander_noise * dt, steering.wander_distance, steering.wander_radius, steering.wander_angle, steering.wander_angle2) * steering.wander_weight; + } + if (steering.seek_weight) + { + force += ai::steering::behavior::seek(agent, steering.seek_target) * steering.seek_weight; + } + + // Normalize force + if (steering.sum_weights) + force /= steering.sum_weights; + + // Accelerate + agent.acceleration = force / agent.mass; + agent.velocity += agent.acceleration * static_cast(dt); + + // Limit speed + const float speed_squared = math::sqr_length(agent.velocity); + if (speed_squared > agent.max_speed_squared) + { + const float speed = std::sqrt(speed_squared); + agent.velocity = (agent.velocity / speed) * agent.max_speed; + } + + // Move agent + agent.position += agent.velocity * static_cast(dt); + + // Rotate agent + if (speed_squared) + { + agent.orientation = math::look_rotation(agent.velocity / std::sqrt(speed_squared), agent.up); + agent.forward = agent.orientation * config::global_forward; + agent.up = agent.orientation * config::global_up; + } + + // Update transform + registry.patch<::transform_component> + ( + entity_id, + [&agent](auto& component) + { + component.local.translation = agent.position; + component.local.rotation = agent.orientation; + } + ); + } + ); +} + diff --git a/src/game/systems/steering-system.hpp b/src/game/systems/steering-system.hpp new file mode 100644 index 0000000..d46af1a --- /dev/null +++ b/src/game/systems/steering-system.hpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_STEERING_SYSTEM_HPP +#define ANTKEEPER_GAME_STEERING_SYSTEM_HPP + +#include "game/systems/updatable-system.hpp" + + +class steering_system: + public updatable_system +{ +public: + steering_system(entity::registry& registry); + virtual void update(double t, double dt); +}; + + +#endif // ANTKEEPER_GAME_STEERING_SYSTEM_HPP diff --git a/src/game/systems/subterrain-system.cpp b/src/game/systems/subterrain-system.cpp new file mode 100644 index 0000000..94a626c --- /dev/null +++ b/src/game/systems/subterrain-system.cpp @@ -0,0 +1,509 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include "game/systems/subterrain-system.hpp" +#include "game/components/model-component.hpp" +#include "game/components/cavity-component.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/** + * An octree containing cubes for the marching cubes algorithm. + */ +struct cube_tree +{ +public: + cube_tree(const geom::aabb& bounds, int max_depth); + ~cube_tree(); + + const bool is_leaf() const; + const geom::aabb& get_bounds() const; + + /// Subdivides all nodes intersecting with a region to the max depth. + void subdivide_max(const geom::aabb& region); + + /// Fills a list with all leaf nodes that intersect with a region. + void query_leaves(std::list& nodes, const geom::aabb& region); + void visit_leaves(const geom::aabb& region, const std::function& f); + + /// Counts then number of nodes in the octree. + std::size_t size() const; + + cube_tree* children[8]; + float3 corners[8]; + float distances[8]; + const int max_depth; + const int depth; + const geom::aabb bounds; + +private: + cube_tree(const geom::aabb& bounds, int max_depth, int depth); + void subdivide(); +}; + +cube_tree::cube_tree(const geom::aabb& bounds, int max_depth): + cube_tree(bounds, max_depth, 0) +{} + +cube_tree::cube_tree(const geom::aabb& bounds, int max_depth, int depth): + bounds(bounds), + max_depth(max_depth), + depth(depth) +{ + corners[0] = {bounds.min_point.x(), bounds.min_point.y(), bounds.min_point.z()}; + corners[1] = {bounds.max_point.x(), bounds.min_point.y(), bounds.min_point.z()}; + corners[2] = {bounds.max_point.x(), bounds.max_point.y(), bounds.min_point.z()}; + corners[3] = {bounds.min_point.x(), bounds.max_point.y(), bounds.min_point.z()}; + corners[4] = {bounds.min_point.x(), bounds.min_point.y(), bounds.max_point.z()}; + corners[5] = {bounds.max_point.x(), bounds.min_point.y(), bounds.max_point.z()}; + corners[6] = {bounds.max_point.x(), bounds.max_point.y(), bounds.max_point.z()}; + corners[7] = {bounds.min_point.x(), bounds.max_point.y(), bounds.max_point.z()}; + + for (int i = 0; i < 8; ++i) + { + children[i] = nullptr; + distances[i] = -std::numeric_limits::infinity(); + + // For outside normals + //distances[i] = std::numeric_limits::infinity(); + } +} + +cube_tree::~cube_tree() +{ + for (cube_tree* child: children) + delete child; +} + +void cube_tree::subdivide_max(const geom::aabb& region) +{ + if (depth != max_depth && aabb_aabb_intersection(bounds, region)) + { + if (is_leaf()) + subdivide(); + + for (cube_tree* child: children) + child->subdivide_max(region); + } +} + +void cube_tree::query_leaves(std::list& nodes, const geom::aabb& region) +{ + if (aabb_aabb_intersection(bounds, region)) + { + if (is_leaf()) + { + nodes.push_back(this); + } + else + { + for (cube_tree* child: children) + child->query_leaves(nodes, region); + } + } +} + +void cube_tree::visit_leaves(const geom::aabb& region, const std::function& f) +{ + if (aabb_aabb_intersection(bounds, region)) + { + if (is_leaf()) + { + f(*this); + } + else + { + for (cube_tree* child: children) + child->visit_leaves(region, f); + } + } +} + +std::size_t cube_tree::size() const +{ + std::size_t node_count = 1; + if (!is_leaf()) + { + for (cube_tree* child: children) + node_count += child->size(); + } + + return node_count; +} + +inline const bool cube_tree::is_leaf() const +{ + return (children[0] == nullptr); +} + +inline const geom::aabb& cube_tree::get_bounds() const +{ + return bounds; +} + +void cube_tree::subdivide() +{ + const float3 center = (bounds.min_point + bounds.max_point) * 0.5f; + + for (int i = 0; i < 8; ++i) + { + geom::aabb child_bounds; + for (int j = 0; j < 3; ++j) + { + child_bounds.min_point[j] = std::min(corners[i][j], center[j]); + child_bounds.max_point[j] = std::max(corners[i][j], center[j]); + } + + children[i] = new cube_tree(child_bounds, max_depth, depth + 1); + } +} + +subterrain_system::subterrain_system(entity::registry& registry, ::resource_manager* resource_manager): + updatable_system(registry), + resource_manager(resource_manager) +{ + + // Load subterrain materials + subterrain_inside_material = nullptr;//resource_manager->load<::render::material>("subterrain-inside.mtl"); + subterrain_outside_material = nullptr;//resource_manager->load<::render::material>("subterrain-outside.mtl"); + + // Allocate subterrain model + subterrain_model = new ::render::model(); + + // Create inside model group + subterrain_inside_group = subterrain_model->add_group("inside"); + subterrain_inside_group->set_material(subterrain_inside_material); + subterrain_inside_group->set_drawing_mode(gl::drawing_mode::triangles); + subterrain_inside_group->set_start_index(0); + subterrain_inside_group->set_index_count(0); + + // Create outside model group + subterrain_outside_group = subterrain_model->add_group("outside"); + subterrain_outside_group->set_material(subterrain_outside_material); + subterrain_outside_group->set_drawing_mode(gl::drawing_mode::triangles); + subterrain_outside_group->set_start_index(0); + subterrain_outside_group->set_index_count(0); + + // Determine vertex size (position, normal, barycentric) + subterrain_model_vertex_size = 3 + 3 + 3; + subterrain_model_vertex_stride = subterrain_model_vertex_size * sizeof(float); + + // Get model VBO and VAO + gl::vertex_buffer* vbo = subterrain_model->get_vertex_buffer(); + gl::vertex_array* vao = subterrain_model->get_vertex_array(); + + std::size_t attribute_offset = 0; + + // Define position vertex attribute + gl::vertex_attribute position_attribute; + position_attribute.buffer = vbo; + position_attribute.offset = attribute_offset; + position_attribute.stride = subterrain_model_vertex_stride; + position_attribute.type = gl::vertex_attribute_type::float_32; + position_attribute.components = 3; + attribute_offset += position_attribute.components * sizeof(float); + + // Define normal vertex attribute + gl::vertex_attribute normal_attribute; + normal_attribute.buffer = vbo; + normal_attribute.offset = attribute_offset; + normal_attribute.stride = subterrain_model_vertex_stride; + normal_attribute.type = gl::vertex_attribute_type::float_32; + normal_attribute.components = 3; + attribute_offset += normal_attribute.components * sizeof(float); + + // Define barycentric vertex attribute + gl::vertex_attribute barycentric_attribute; + barycentric_attribute.buffer = vbo; + barycentric_attribute.offset = attribute_offset; + barycentric_attribute.stride = subterrain_model_vertex_stride; + barycentric_attribute.type = gl::vertex_attribute_type::float_32; + barycentric_attribute.components = 3; + attribute_offset += barycentric_attribute.components * sizeof(float); + + // Bind vertex attributes to VAO + vao->bind(::render::vertex_attribute::position, position_attribute); + vao->bind(::render::vertex_attribute::normal, normal_attribute); + vao->bind(::render::vertex_attribute::barycentric, barycentric_attribute); + + // Calculate adjusted bounds to fit isosurface resolution + //isosurface_resolution = 0.325f; + isosurface_resolution = 0.5f; + float ideal_volume_size = 200.0f; + int octree_depth = std::floor(std::log(ideal_volume_size / isosurface_resolution) / std::log(2)) + 1; + float adjusted_volume_size = std::pow(2.0f, octree_depth) * isosurface_resolution; + + // Set subterrain bounds + subterrain_bounds.min_point = float3{-0.5f, -1.0f, -0.5f} * adjusted_volume_size; + subterrain_bounds.max_point = float3{ 0.5f, 0.0f, 0.5f} * adjusted_volume_size; + + // Set subterrain model bounds + subterrain_model->set_bounds(subterrain_bounds); + + // Allocate cube tree + cube_tree = new ::cube_tree(subterrain_bounds, octree_depth); + + // Allocate mesh + subterrain_mesh = new geom::mesh(); + + first_run = true; +} + +subterrain_system::~subterrain_system() +{ + delete subterrain_model; + delete subterrain_mesh; +} + +void subterrain_system::update(double t, double dt) +{ + if (first_run) + { + first_run = false; + //auto subterrain_entity = registry.create(); + //registry.assign(subterrain_entity, subterrain_model); + + subterrain_model_instance = new scene::model_instance(subterrain_model); + collection->add_object(subterrain_model_instance); + } + + bool digging = false; + + registry.view().each( + [this, &digging](entity::id entity_id, auto& cavity) + { + this->dig(cavity.position, cavity.radius); + this->registry.destroy(entity_id); + + digging = true; + }); + + if (digging) + { + + //std::cout << "regenerating subterrain mesh...\n"; + regenerate_subterrain_mesh(); + //std::cout << "regenerating subterrain mesh... done\n"; + + //std::cout << "regenerating subterrain model...\n"; + regenerate_subterrain_model(); + //std::cout << "regenerating subterrain model... done\n"; + } +} + +void subterrain_system::set_scene(scene::collection* collection) +{ + this->collection = collection; +} + +void subterrain_system::regenerate_subterrain_mesh() +{ + delete subterrain_mesh; + subterrain_mesh = new geom::mesh(); + subterrain_vertices.clear(); + subterrain_triangles.clear(); + subterrain_vertex_map.clear(); + + //std::cout << "marching...\n"; + merged = 0; + march(cube_tree); + //std::cout << "merged " << merged << " vertices\n"; + //std::cout << "marching...done\n"; + + //std::cout << "vertex count: " << subterrain_vertices.size() << std::endl; + //std::cout << "triangle count: " << subterrain_triangles.size() << std::endl; + + //std::cout << "creating mesh...\n"; + create_triangle_mesh(*subterrain_mesh, subterrain_vertices, subterrain_triangles); + //std::cout << "creating mesh... done\n"; +} + +void subterrain_system::march(::cube_tree* node) +{ + if (!node->is_leaf()) + { + for (::cube_tree* child: node->children) + march(child); + return; + } + else if (node->depth != node->max_depth) + { + return; + } + + // Get node bounds + const geom::aabb& bounds = node->get_bounds(); + + // Polygonize cube + float vertex_buffer[12 * 3]; + std::uint_fast8_t vertex_count; + std::int_fast8_t triangle_buffer[5 * 3]; + std::uint_fast8_t triangle_count; + const float* corners = &node->corners[0][0]; + const float* distances = &node->distances[0]; + geom::mc::polygonize(vertex_buffer, &vertex_count, triangle_buffer, &triangle_count, corners, distances); + + // Remap local vertex buffer indices (0-11) to mesh vertex indices + std::uint_fast32_t vertex_remap[12]; + for (int i = 0; i < vertex_count; ++i) + { + const float3& vertex = reinterpret_cast(vertex_buffer[i * 3]); + + if (auto it = subterrain_vertex_map.find(vertex); it != subterrain_vertex_map.end()) + { + vertex_remap[i] = it->second; + ++merged; + } + else + { + vertex_remap[i] = subterrain_vertices.size(); + subterrain_vertex_map[vertex] = subterrain_vertices.size(); + subterrain_vertices.push_back(vertex); + } + } + + // Add triangles + for (std::uint_fast32_t i = 0; i < triangle_count; ++i) + { + subterrain_triangles.push_back( + { + vertex_remap[triangle_buffer[i * 3]], + vertex_remap[triangle_buffer[i * 3 + 1]], + vertex_remap[triangle_buffer[i * 3 + 2]] + }); + } +} + +void subterrain_system::regenerate_subterrain_model() +{ + float3* face_normals = new float3[subterrain_mesh->get_faces().size()]; + calculate_face_normals(face_normals, *subterrain_mesh); + + static const float3 barycentric_coords[3] = + { + float3{1, 0, 0}, + float3{0, 1, 0}, + float3{0, 0, 1} + }; + + float* vertex_data = new float[subterrain_model_vertex_size * subterrain_mesh->get_faces().size() * 3]; + float* v = vertex_data; + for (std::size_t i = 0; i < subterrain_mesh->get_faces().size(); ++i) + { + geom::mesh::face* face = subterrain_mesh->get_faces()[i]; + geom::mesh::edge* ab = face->edge; + geom::mesh::edge* bc = face->edge->next; + geom::mesh::edge* ca = face->edge->previous; + geom::mesh::vertex* a = ab->vertex; + geom::mesh::vertex* b = bc->vertex; + geom::mesh::vertex* c = ca->vertex; + geom::mesh::vertex* vertices[3] = {a, b, c}; + + for (std::size_t j = 0; j < 3; ++j) + { + geom::mesh::vertex* vertex = vertices[j]; + + float3 n = {0, 0, 0}; + geom::mesh::edge* start = vertex->edge; + geom::mesh::edge* edge = start; + do + { + if (edge->face) + { + n += face_normals[edge->face->index]; + } + + edge = edge->previous->symmetric; + } + while (edge != start); + n = math::normalize(n); + + //float3 n = reinterpret_cast(face_normals[i * 3]); + + *(v++) = vertex->position[0]; + *(v++) = vertex->position[1]; + *(v++) = vertex->position[2]; + + *(v++) = n[0]; + *(v++) = n[1]; + *(v++) = n[2]; + + *(v++) = barycentric_coords[j][0]; + *(v++) = barycentric_coords[j][1]; + *(v++) = barycentric_coords[j][2]; + } + } + + // Resized VBO and upload vertex data + gl::vertex_buffer* vbo = subterrain_model->get_vertex_buffer(); + vbo->resize(subterrain_mesh->get_faces().size() * 3 * subterrain_model_vertex_stride, vertex_data); + + // Deallocate vertex data + delete[] face_normals; + delete[] vertex_data; + + // Update model groups + subterrain_inside_group->set_index_count(subterrain_mesh->get_faces().size() * 3); + subterrain_outside_group->set_index_count(subterrain_mesh->get_faces().size() * 3); +} + +void subterrain_system::dig(const float3& position, float radius) +{ + // Construct region containing the cavity sphere + geom::aabb region = {position, position}; + for (int i = 0; i < 3; ++i) + { + region.min_point[i] -= radius + isosurface_resolution; + region.max_point[i] += radius + isosurface_resolution; + } + + // Subdivide the octree to the maximum depth within the region + cube_tree->subdivide_max(region); + + // Query all octree leaf nodes within the region + std::list<::cube_tree*> nodes; + cube_tree->visit_leaves(region, + [&position, radius](::cube_tree& node) + { + for (int i = 0; i < 8; ++i) + { + // For outside normals (also set node initial distance to +infinity) + //float distance = math::length(node->corners[i] - position) - radius; + // if (distance < node->distances[i]) + + float distance = radius - math::length(node.corners[i] - position); + if (distance > node.distances[i]) + node.distances[i] = distance; + } + }); +} + diff --git a/src/game/systems/subterrain-system.hpp b/src/game/systems/subterrain-system.hpp new file mode 100644 index 0000000..5c9fd8b --- /dev/null +++ b/src/game/systems/subterrain-system.hpp @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_SUBTERRAIN_SYSTEM_HPP +#define ANTKEEPER_GAME_SUBTERRAIN_SYSTEM_HPP + +#include "game/systems/updatable-system.hpp" +#include +#include +#include +#include +#include +#include +#include + +class resource_manager; + + +struct cube_tree; + +template +struct epsilon +{ + static const double value; +}; + +template +const double epsilon::value = static_cast(Mantissa) * std::pow(10.0, Exponent); + +typedef epsilon<1, -5> epsilon_1en5; + +template +struct vector_hasher +{ + typedef math::vector vector_type; + + std::size_t operator()(const vector_type& v) const noexcept + { + static const T inverse_epsilon = T(1) / Epsilon::value; + + std::size_t hash = 0; + for (std::size_t i = 0; i < N; ++i) + { + std::int64_t j = static_cast(v[i] * inverse_epsilon); + hash ^= std::hash()(j) + 0x9e3779b9 + (hash << 6) + (hash >> 2); + } + + return hash; + } +}; + +template +struct vector_equals +{ + typedef math::vector vector_type; + + bool operator()(const vector_type& a, const vector_type& b) const noexcept + { + for (std::size_t i = 0; i < N; ++i) + { + if (std::fabs(b[i] - a[i]) >= Epsilon::value) + return false; + } + + return true; + } +}; + +class subterrain_system: public updatable_system +{ +public: + subterrain_system(entity::registry& registry, ::resource_manager* resource_manager); + ~subterrain_system(); + virtual void update(double t, double dt); + + void set_scene(scene::collection* collection); + +private: + void regenerate_subterrain_mesh(); + void march(cube_tree* node); + void regenerate_subterrain_model(); + void dig(const float3&position, float radius); + float distance(const cube_tree& node, const float3& sample) const; + + resource_manager* resource_manager; + geom::mesh* subterrain_mesh; + ::render::model* subterrain_model; + ::render::material* subterrain_inside_material; + ::render::material* subterrain_outside_material; + ::render::model_group* subterrain_inside_group; + ::render::model_group* subterrain_outside_group; + int subterrain_model_vertex_size; + int subterrain_model_vertex_stride; + geom::aabb subterrain_bounds; + cube_tree* cube_tree; + std::vector subterrain_vertices; + std::vector> subterrain_triangles; + float isosurface_resolution; + bool first_run; + int merged; + + std::unordered_map< + float3, + std::uint_fast32_t, + vector_hasher, + vector_equals> subterrain_vertex_map; + + scene::collection* collection; + scene::model_instance* subterrain_model_instance; +}; + + +#endif // ANTKEEPER_GAME_SUBTERRAIN_SYSTEM_HPP diff --git a/src/game/systems/terrain-system.cpp b/src/game/systems/terrain-system.cpp new file mode 100644 index 0000000..69257c1 --- /dev/null +++ b/src/game/systems/terrain-system.cpp @@ -0,0 +1,648 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include "game/systems/terrain-system.hpp" +#include "game/components/terrain-component.hpp" +#include "game/components/camera-component.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +terrain_system::terrain_system(entity::registry& registry): + updatable_system(registry), + patch_side_length(0.0f), + patch_subdivisions(0), + patch_material(nullptr), + elevation_function(nullptr), + scene_collection(nullptr), + patch_base_mesh(nullptr), + patch_vertex_size(0), + patch_vertex_stride(0), + patch_vertex_data(nullptr) +{ + // Specify vertex size and stride + // (position + uv + normal + tangent + barycentric + target) + patch_vertex_size = 3 + 2 + 3 + 4 + 3 + 3; + patch_vertex_stride = patch_vertex_size * sizeof(float); + + // Init quadtee node sizes at each depth + for (std::size_t i = 0; i <= quadtree_type::max_depth; ++i) + { + quadtree_node_size[i] = 0.0f; + quadtree_node_resolution[i] = static_cast(std::exp2(i)); + } + + // registry.on_construct().connect<&terrain_system::on_terrain_construct>(this); + // registry.on_update().connect<&terrain_system::on_terrain_update>(this); + // registry.on_destroy().connect<&terrain_system::on_terrain_destroy>(this); +} + +terrain_system::~terrain_system() +{ + // registry.on_construct().disconnect<&terrain_system::on_terrain_construct>(this); + // registry.on_update().disconnect<&terrain_system::on_terrain_update>(this); + // registry.on_destroy().disconnect<&terrain_system::on_terrain_destroy>(this); +} + +void terrain_system::update(double t, double dt) +{ + /* + // Clear quadtree + quadtree.clear(); + + // For each camera + this->registry.view().each + ( + [&](entity::id camera_eid, const auto& camera) + { + if (!camera.object) + return; + + const scene::camera& cam = *camera.object; + + // Determine camera node location + const auto [x, y, z] = cam.get_translation(); + + quadtree_node_type node_depth = quadtree.max_depth; + const float node_size = quadtree_node_size[node_depth]; + quadtree_node_type node_resolution = quadtree_node_resolution[node_depth]; + + quadtree_node_type node_x = static_cast(x / node_size + node_resolution / 2); + quadtree_node_type node_y = static_cast(z / node_size + node_resolution / 2); + quadtree_node_type node_location = geom::morton::encode(node_x, node_y); + //quadtree.insert(quadtree.node(node_depth, node_location)); + + node_stack.push(quadtree.node(node_depth, node_location)); + balance_quadtree(); + + for (const quadtree_node_type& node: quadtree) + { + if (!quadtree.is_leaf(node)) + continue; + + if (patches.find(node) == patches.end()) + { + patch* node_patch = generate_patch(node); + patches[node] = node_patch; + scene_collection->add_object(node_patch->model_instance); + } + } + } + ); + + + + /// Toggle visibility of terrain scene objects + for (auto it = patches.begin(); it != patches.end(); ++it) + { + bool active = (quadtree.contains(it->first) && quadtree.is_leaf(it->first)); + it->second->model_instance->set_active(active); + } + */ +} + +void terrain_system::set_patch_side_length(float length) +{ + patch_side_length = length; + + // Recalculate node sizes at each quadtree depth + for (std::size_t i = 0; i <= quadtree_type::max_depth; ++i) + { + quadtree_node_size[i] = std::exp2(quadtree_type::max_depth - i) * patch_side_length; + } +} + +void terrain_system::set_patch_subdivisions(std::size_t n) +{ + patch_subdivisions = n; + + // Recalculate patch properties + patch_cell_count = (patch_subdivisions + 1) * (patch_subdivisions + 1); + patch_triangle_count = patch_cell_count * 2; + + // Resize patch vertex data buffer + delete[] patch_vertex_data; + patch_vertex_data = new float[patch_triangle_count * 3 * patch_vertex_size]; + + // Resize patch buffers + + std::size_t vertex_buffer_row_size = patch_subdivisions + 4; + std::size_t vertex_buffer_column_size = vertex_buffer_row_size; + + patch_vertex_buffer.resize(vertex_buffer_row_size); + for (std::size_t i = 0; i < patch_vertex_buffer.size(); ++i) + patch_vertex_buffer[i].resize(vertex_buffer_column_size); + + rebuild_patch_base_mesh(); +} + +void terrain_system::set_patch_material(::render::material* material) +{ + patch_material = material; +} + +void terrain_system::set_elevation_function(const std::function& f) +{ + elevation_function = f; +} + +void terrain_system::set_scene_collection(scene::collection* collection) +{ + scene_collection = collection; +} + +void terrain_system::on_terrain_construct(entity::registry& registry, entity::id entity_id) +{ +} + +void terrain_system::on_terrain_update(entity::registry& registry, entity::id entity_id) +{ +} + +void terrain_system::on_terrain_destroy(entity::registry& registry, entity::id entity_id) +{ +} + +float terrain_system::get_patch_size(quadtree_node_type node) const +{ + return quadtree_node_size[quadtree_type::depth(node)]; +} + +float3 terrain_system::get_patch_center(quadtree_node_type node) const +{ + const float node_size = get_patch_size(node); + const float node_offset = quadtree_node_size[0] * -0.5f + node_size * 0.5f; + + // Extract node location from Morton location code + quadtree_type::node_type node_location = quadtree_type::location(node); + quadtree_type::node_type node_location_x; + quadtree_type::node_type node_location_y; + geom::morton::decode(node_location, node_location_x, node_location_y); + + return float3 + { + node_offset + static_cast(node_location_x) * node_size, + 0.0f, + node_offset + static_cast(node_location_y) * node_size + }; +} + +void terrain_system::rebuild_patch_base_mesh() +{ + // Rebuild grid + delete patch_base_mesh; + patch_base_mesh = geom::meshes::grid_xy(1.0f, patch_subdivisions, patch_subdivisions); + + // Convert quads to triangle fans + for (std::size_t i = 0; i < patch_base_mesh->get_faces().size(); ++i) + { + geom::mesh::face* face = patch_base_mesh->get_faces()[i]; + + std::size_t edge_count = 1; + for (geom::mesh::edge* edge = face->edge->next; edge != face->edge; edge = edge->next) + ++edge_count; + + if (edge_count > 3) + { + geom::poke_face(*patch_base_mesh, face->index); + --i; + } + } + + // Transform patch base mesh coordinates from XY plane to XZ plane + const math::quaternion xy_to_xz = math::quaternion::rotate_x(math::half_pi); + for (geom::mesh::vertex* vertex: patch_base_mesh->get_vertices()) + { + vertex->position = xy_to_xz * vertex->position; + } +} + +void terrain_system::balance_quadtree() +{ + while (!node_stack.empty()) + { + quadtree_node_type node = node_stack.top(); + node_stack.pop(); + + if (quadtree.contains(node)) + continue; + + quadtree.insert(node); + + const auto depth = quadtree.depth(node); + if (depth < 2) + continue; + + const quadtree_node_type parent = quadtree.parent(node); + const quadtree_node_type parent_depth = depth - 1; + const quadtree_node_type parent_resolution = quadtree_node_resolution[parent_depth]; + + for (quadtree_node_type i = 0; i < quadtree.children_per_node; ++i) + { + const auto location = quadtree.location(quadtree.sibling(parent, i)); + quadtree_node_type x, y; + geom::morton::decode(location, x, y); + + if (x < parent_resolution - 1) + { + if (y < parent_resolution - 1) + node_stack.push(quadtree.node(parent_depth, geom::morton::encode(x + 1, y + 1))); + if (y > 0) + node_stack.push(quadtree.node(parent_depth, geom::morton::encode(x + 1, y - 1))); + } + + if (x > 0) + { + if (y < parent_resolution - 1) + node_stack.push(quadtree.node(parent_depth, geom::morton::encode(x - 1, y + 1))); + if (y > 0) + node_stack.push(quadtree.node(parent_depth, geom::morton::encode(x - 1, y - 1))); + } + } + } +} + +geom::mesh* terrain_system::generate_patch_mesh(quadtree_node_type node) const +{ + // Extract node depth + const quadtree_type::node_type node_depth = quadtree_type::depth(node); + + // Get size of node at depth + const float node_size = quadtree_node_size[node_depth]; + + // Extract node Morton location code and decode location + const quadtree_type::node_type node_location = quadtree_type::location(node); + quadtree_type::node_type node_location_x; + quadtree_type::node_type node_location_y; + geom::morton::decode(node_location, node_location_x, node_location_y); + + // Determine center of node + const float node_offset = quadtree_node_size[0] * -0.5f + node_size * 0.5f; + const float3 node_center = + { + node_offset + static_cast(node_location_x) * node_size, + 0.0f, + node_offset + static_cast(node_location_y) * node_size + }; + + // Copy patch base mesh + geom::mesh* patch_mesh = new geom::mesh(*patch_base_mesh); + + // Modify patch mesh vertex positions + for (geom::mesh::vertex* v: patch_mesh->get_vertices()) + { + v->position.x() = node_center.x() + v->position.x() * node_size; + v->position.z() = node_center.z() + v->position.z() * node_size; + v->position.y() = elevation_function(v->position.x(), v->position.z()); + } + + return patch_mesh; +} + +::render::model* terrain_system::generate_patch_model(quadtree_node_type node) const +{ + // Get size and position of patch + const float patch_size = get_patch_size(node); + const float3 patch_center = get_patch_center(node); + + // Calculate size of a patch cell + const float cell_size = patch_size / static_cast(patch_subdivisions + 1); + + // Init patch bounds + geom::aabb patch_bounds; + patch_bounds.min_point.x() = patch_center.x() - patch_size * 0.5f; + patch_bounds.min_point.y() = std::numeric_limits::infinity(); + patch_bounds.min_point.z() = patch_center.z() - patch_size * 0.5f; + patch_bounds.max_point.x() = patch_center.x() + patch_size * 0.5f; + patch_bounds.max_point.y() = -std::numeric_limits::infinity(); + patch_bounds.max_point.z() = patch_center.z() + patch_size * 0.5f; + + // Calculate positions and UVs of patch vertices and immediately neighboring vertices + float3 first_vertex_position = + { + patch_bounds.min_point.x() - cell_size, + patch_center.y(), + patch_bounds.min_point.z() - cell_size + }; + float3 vertex_position = first_vertex_position; + for (std::size_t i = 0; i < patch_vertex_buffer.size(); ++i) + { + // For each column + for (std::size_t j = 0; j < patch_vertex_buffer[i].size(); ++j) + { + // Calculate vertex elevation + vertex_position.y() = elevation_function(vertex_position.x(), vertex_position.z()); + + // Update patch bounds + patch_bounds.min_point.y() = std::min(patch_bounds.min_point.y(), vertex_position.y()); + patch_bounds.max_point.y() = std::max(patch_bounds.max_point.y(), vertex_position.y()); + + // Update patch vertex position + patch_vertex_buffer[i][j].position = vertex_position; + + // Calculate patch vertex UV + patch_vertex_buffer[i][j].uv.x() = (vertex_position.x() - patch_bounds.min_point.x()) / patch_size; + patch_vertex_buffer[i][j].uv.y() = (vertex_position.z() - patch_bounds.min_point.z()) / patch_size; + + // Init patch vertex normal, tangent, and bitangent + patch_vertex_buffer[i][j].normal = {0, 0, 0}; + patch_vertex_buffer[i][j].tangent = {0, 0, 0}; + patch_vertex_buffer[i][j].bitangent = {0, 0, 0}; + + vertex_position.x() += cell_size; + } + + vertex_position.z() += cell_size; + vertex_position.x() = first_vertex_position.x(); + } + + // Accumulate normals, tangents, and bitangents + for (std::size_t i = 0; i < patch_vertex_buffer.size() - 1; ++i) + { + for (std::size_t j = 0; j < patch_vertex_buffer[i].size() - 1; ++j) + { + patch_vertex& a = patch_vertex_buffer[i ][j]; + patch_vertex& b = patch_vertex_buffer[i+1][j]; + patch_vertex& c = patch_vertex_buffer[i ][j+1]; + patch_vertex& d = patch_vertex_buffer[i+1][j+1]; + + auto add_ntb = [](auto& a, auto& b, auto& c) + { + const float3 ba = b.position - a.position; + const float3 ca = c.position - a.position; + const float2 uvba = b.uv - a.uv; + const float2 uvca = c.uv - a.uv; + + const float3 normal = math::normalize(math::cross(ba, ca)); + const float f = 1.0f / (uvba.x() * uvca.y() - uvca.x() * uvba.y()); + const float3 tangent = (ba * uvca.y() - ca * uvba.y()) * f; + const float3 bitangent = (ba * -uvca.x() + ca * uvba.x()) * f; + + a.normal += normal; + a.tangent += tangent; + a.bitangent += bitangent; + + b.normal += normal; + b.tangent += tangent; + b.bitangent += bitangent; + + c.normal += normal; + c.tangent += tangent; + c.bitangent += bitangent; + }; + + if ((j + i) % 2) + { + add_ntb(a, b, c); + add_ntb(c, b, d); + } + else + { + add_ntb(a, b, d); + add_ntb(a, d, c); + } + } + } + + // Finalize normals, tangents, and bitangent signs of patch vertices + for (std::size_t i = 1; i < patch_vertex_buffer.size() - 1; ++i) + { + for (std::size_t j = 1; j < patch_vertex_buffer[i].size() - 1; ++j) + { + auto& vertex = patch_vertex_buffer[i][j]; + + // Normalize normal + vertex.normal = math::normalize(vertex.normal); + + // Gram-Schmidt orthogonalize tangent + vertex.tangent = math::normalize(vertex.tangent - vertex.normal * math::dot(vertex.normal, vertex.tangent)); + + // Calculate bitangent sign + vertex.bitangent_sign = std::copysign(1.0f, math::dot(math::cross(vertex.normal, vertex.tangent), vertex.bitangent)); + } + } + + /* + + 0 subdivisions: + +---+---+---+ + | | + + +---+ + + | | | | + + +---+ + + | | + +---+---+---+ + + 1 subdivision: + +---+---+---+---+ + | | + + +---+---+ + + | | | | | + + +---+---+ + + | | | | | + + +---+---+ + + | | + +---+---+---+---+ + + 2 subdivisions: + +---+---+---+---+---+ + | | + + +---+---+---+ + + | | | | | | + + +---+---+---+ + + | | | | | | + + +---+---+---+ + + | | | | | | + + +---+---+---+ + + | | + +---+---+---+---+---+ + */ + + // For each row + float* v = patch_vertex_data; + for (std::size_t i = 1; i < patch_vertex_buffer.size() - 2; ++i) + { + // For each column + for (std::size_t j = 1; j < patch_vertex_buffer[i].size() - 2; ++j) + { + // a---c + // | | + // b---d + const patch_vertex& a = patch_vertex_buffer[i ][j]; + const patch_vertex& b = patch_vertex_buffer[i+1][j]; + const patch_vertex& c = patch_vertex_buffer[i ][j+1]; + const patch_vertex& d = patch_vertex_buffer[i+1][j+1]; + + auto add_triangle = [&v](const patch_vertex& a, const patch_vertex& b, const patch_vertex& c) + { + auto add_vertex = [&v](const patch_vertex& vertex, const float3& barycentric) + { + // Position + *(v++) = vertex.position[0]; + *(v++) = vertex.position[1]; + *(v++) = vertex.position[2]; + + // UV + *(v++) = vertex.uv[0]; + *(v++) = vertex.uv[1]; + + // Normal + *(v++) = vertex.normal[0]; + *(v++) = vertex.normal[1]; + *(v++) = vertex.normal[2]; + + /// Tangent + *(v++) = vertex.tangent[0]; + *(v++) = vertex.tangent[1]; + *(v++) = vertex.tangent[2]; + *(v++) = vertex.bitangent_sign; + + // Barycentric + *(v++) = barycentric[0]; + *(v++) = barycentric[1]; + *(v++) = barycentric[2]; + + // Morph target (LOD transition) + *(v++) = 0.0f; + *(v++) = 0.0f; + *(v++) = 0.0f; + }; + + add_vertex(a, float3{1, 0, 0}); + add_vertex(b, float3{0, 1, 0}); + add_vertex(c, float3{0, 0, 1}); + }; + + if ((j + i) % 2) + { + add_triangle(a, b, c); + add_triangle(c, b, d); + } + else + { + add_triangle(a, b, d); + add_triangle(a, d, c); + } + } + } + + // Allocate patch model + ::render::model* patch_model = new ::render::model(); + + // Get model VBO and VAO + gl::vertex_buffer* vbo = patch_model->get_vertex_buffer(); + gl::vertex_array* vao = patch_model->get_vertex_array(); + + // Resize model VBO and upload vertex data + vbo->resize(patch_triangle_count * 3 * patch_vertex_stride, patch_vertex_data); + + std::size_t attribute_offset = 0; + + // Define position vertex attribute + gl::vertex_attribute position_attribute; + position_attribute.buffer = vbo; + position_attribute.offset = attribute_offset; + position_attribute.stride = patch_vertex_stride; + position_attribute.type = gl::vertex_attribute_type::float_32; + position_attribute.components = 3; + attribute_offset += position_attribute.components * sizeof(float); + + // Define UV vertex attribute + gl::vertex_attribute uv_attribute; + uv_attribute.buffer = vbo; + uv_attribute.offset = attribute_offset; + uv_attribute.stride = patch_vertex_stride; + uv_attribute.type = gl::vertex_attribute_type::float_32; + uv_attribute.components = 2; + attribute_offset += uv_attribute.components * sizeof(float); + + // Define normal vertex attribute + gl::vertex_attribute normal_attribute; + normal_attribute.buffer = vbo; + normal_attribute.offset = attribute_offset; + normal_attribute.stride = patch_vertex_stride; + normal_attribute.type = gl::vertex_attribute_type::float_32; + normal_attribute.components = 3; + attribute_offset += normal_attribute.components * sizeof(float); + + // Define tangent vertex attribute + gl::vertex_attribute tangent_attribute; + tangent_attribute.buffer = vbo; + tangent_attribute.offset = attribute_offset; + tangent_attribute.stride = patch_vertex_stride; + tangent_attribute.type = gl::vertex_attribute_type::float_32; + tangent_attribute.components = 4; + attribute_offset += tangent_attribute.components * sizeof(float); + + // Define barycentric vertex attribute + gl::vertex_attribute barycentric_attribute; + barycentric_attribute.buffer = vbo; + barycentric_attribute.offset = attribute_offset; + barycentric_attribute.stride = patch_vertex_stride; + barycentric_attribute.type = gl::vertex_attribute_type::float_32; + barycentric_attribute.components = 3; + attribute_offset += barycentric_attribute.components * sizeof(float); + + // Define target vertex attribute + gl::vertex_attribute target_attribute; + target_attribute.buffer = vbo; + target_attribute.offset = attribute_offset; + target_attribute.stride = patch_vertex_stride; + target_attribute.type = gl::vertex_attribute_type::float_32; + target_attribute.components = 3; + attribute_offset += target_attribute.components * sizeof(float); + + // Bind vertex attributes to VAO + vao->bind(::render::vertex_attribute::position, position_attribute); + vao->bind(::render::vertex_attribute::uv, uv_attribute); + vao->bind(::render::vertex_attribute::normal, normal_attribute); + vao->bind(::render::vertex_attribute::tangent, tangent_attribute); + vao->bind(::render::vertex_attribute::barycentric, barycentric_attribute); + vao->bind(::render::vertex_attribute::target, target_attribute); + + // Create model group + ::render::model_group* patch_model_group = patch_model->add_group("terrain"); + patch_model_group->set_material(patch_material); + patch_model_group->set_drawing_mode(gl::drawing_mode::triangles); + patch_model_group->set_start_index(0); + patch_model_group->set_index_count(patch_triangle_count * 3); + + // Set patch model bounds + patch_model->set_bounds(patch_bounds); + + return patch_model; +} + +terrain_system::patch* terrain_system::generate_patch(quadtree_node_type node) +{ + patch* node_patch = new patch(); + node_patch->mesh = nullptr;//generate_patch_mesh(node); + node_patch->model = generate_patch_model(node); + node_patch->model_instance = new scene::model_instance(node_patch->model); + return node_patch; +} + diff --git a/src/game/systems/terrain-system.hpp b/src/game/systems/terrain-system.hpp new file mode 100644 index 0000000..c797918 --- /dev/null +++ b/src/game/systems/terrain-system.hpp @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_TERRAIN_SYSTEM_HPP +#define ANTKEEPER_GAME_TERRAIN_SYSTEM_HPP + +#include "game/systems/updatable-system.hpp" +#include "game/components/terrain-component.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/** + * Generates terrain patches and performs terrain patch LOD selection. + */ +class terrain_system: public updatable_system +{ +public: + terrain_system(entity::registry& registry); + ~terrain_system(); + + virtual void update(double t, double dt); + + /** + * Sets the size of a patch. + * + * @param length Side length of a patch. + */ + void set_patch_side_length(float length); + + /** + * Sets the number of subdivisions of a patch. Zero subdivisions results in a single quad, one subdivison results in four quads, etc. + * + * @param n Number of subdivisions. + */ + void set_patch_subdivisions(std::size_t n); + + /** + * Sets the material of each patch. + * + * @param material Patch material. + */ + void set_patch_material(::render::material* material); + + /** + * Sets the terrain elevation function. + * + * @param f Function which returns the terrain height (Y-coordinate) given X- and Z-coordinates. + */ + void set_elevation_function(const std::function& f); + + /** + * Sets the scene collection into which terrain patch model instances will be inserted. + */ + void set_scene_collection(scene::collection* collection); + +private: + typedef geom::unordered_quadtree16 quadtree_type; + typedef typename quadtree_type::node_type quadtree_node_type; + + void balance_quadtree(); + + struct patch + { + geom::mesh* mesh; + ::render::model* model; + scene::model_instance* model_instance; + }; + + void on_terrain_construct(entity::registry& registry, entity::id entity_id); + void on_terrain_update(entity::registry& registry, entity::id entity_id); + void on_terrain_destroy(entity::registry& registry, entity::id entity_id); + + float get_patch_size(quadtree_node_type node) const; + float3 get_patch_center(quadtree_node_type node) const; + + void rebuild_patch_base_mesh(); + + /** + * Generates a mesh for a terrain patch given the patch's quadtree node + */ + geom::mesh* generate_patch_mesh(quadtree_node_type node) const; + + /** + * Generates a model for a terrain patch given the patch's mesh. + */ + ::render::model* generate_patch_model(quadtree_node_type node) const; + + patch* generate_patch(quadtree_node_type node); + + float patch_side_length; + std::size_t patch_subdivisions; + std::size_t patch_cell_count; + std::size_t patch_triangle_count; + std::size_t patch_vertex_size; + std::size_t patch_vertex_stride; + float* patch_vertex_data; + + struct patch_vertex + { + float3 position; + float2 uv; + float3 normal; + float3 tangent; + float3 bitangent; + float bitangent_sign; + }; + + mutable std::vector> patch_vertex_buffer; + + + ::render::material* patch_material; + std::function elevation_function; + scene::collection* scene_collection; + + geom::mesh* patch_base_mesh; + + /// Quadtree describing level of detail + quadtree_type quadtree; + float quadtree_node_size[quadtree_type::max_depth + 1]; + quadtree_node_type quadtree_node_resolution[quadtree_type::max_depth + 1]; + + /// Map linking quadtree nodes to terrain patches + std::unordered_map patches; + + std::stack node_stack; +}; + + +#endif // ANTKEEPER_GAME_TERRAIN_SYSTEM_HPP diff --git a/src/game/systems/updatable-system.cpp b/src/game/systems/updatable-system.cpp new file mode 100644 index 0000000..c94821f --- /dev/null +++ b/src/game/systems/updatable-system.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include "game/systems/updatable-system.hpp" + + +updatable_system::updatable_system(entity::registry& registry): + registry(registry) +{} + diff --git a/src/game/systems/updatable-system.hpp b/src/game/systems/updatable-system.hpp new file mode 100644 index 0000000..da4495d --- /dev/null +++ b/src/game/systems/updatable-system.hpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_GAME_UPDATABLE_SYSTEM_HPP +#define ANTKEEPER_GAME_UPDATABLE_SYSTEM_HPP + +#include + + +/** + * Abstract base class for updatable systems. + */ +class updatable_system +{ +public: + /** + * Creates an updatable system. + * + * @param registry Reference to the registry on which the system will operate. + */ + updatable_system(entity::registry& registry); + + /** + * Perform's a system's update() function. + * + * @param t Total elapsed time, in seconds. + * @param dt Delta time, in seconds. + * @param registry Entity registry. + */ + virtual void update(double t, double dt) = 0; + +protected: + /// Registry on which the system operate + entity::registry& registry; +}; + + +#endif // ANTKEEPER_GAME_UPDATABLE_SYSTEM_HPP diff --git a/src/game/world.cpp b/src/game/world.cpp index ff9dfc3..3ae3706 100644 --- a/src/game/world.cpp +++ b/src/game/world.cpp @@ -18,80 +18,79 @@ */ #include "game/world.hpp" -#include "color/color.hpp" -#include "config.hpp" -#include "debug/log.hpp" -#include "entity/archetype.hpp" -#include "entity/commands.hpp" -#include "game/component/atmosphere.hpp" -#include "game/component/blackbody.hpp" -#include "game/component/celestial-body.hpp" -#include "game/component/observer.hpp" -#include "game/component/orbit.hpp" -#include "game/component/terrain.hpp" -#include "game/component/transform.hpp" -#include "game/system/astronomy.hpp" -#include "game/system/atmosphere.hpp" -#include "game/system/orbit.hpp" -#include "game/system/terrain.hpp" -#include "geom/solid-angle.hpp" -#include "geom/spherical.hpp" -#include "gl/drawing-mode.hpp" -#include "gl/texture-filter.hpp" -#include "gl/texture-wrapping.hpp" -#include "gl/vertex-array.hpp" -#include "gl/vertex-attribute.hpp" -#include "gl/vertex-buffer.hpp" -#include "i18n/string-table.hpp" -#include "math/hash/hash.hpp" -#include "math/noise/noise.hpp" -#include "physics/light/photometry.hpp" -#include "physics/light/vmag.hpp" -#include "physics/orbit/ephemeris.hpp" -#include "physics/orbit/orbit.hpp" -#include "physics/time/constants.hpp" -#include "physics/time/gregorian.hpp" -#include "physics/time/utc.hpp" -#include "render/material-flags.hpp" -#include "render/material.hpp" -#include "render/model.hpp" -#include "render/passes/ground-pass.hpp" -#include "render/passes/shadow-map-pass.hpp" -#include "render/passes/sky-pass.hpp" -#include "render/vertex-attribute.hpp" -#include "resources/image.hpp" -#include "resources/json.hpp" -#include "resources/resource-manager.hpp" -#include "scene/ambient-light.hpp" -#include "scene/directional-light.hpp" -#include "scene/text.hpp" +#include +#include +#include +#include +#include "game/commands/commands.hpp" +#include "game/components/atmosphere-component.hpp" +#include "game/components/blackbody-component.hpp" +#include "game/components/celestial-body-component.hpp" +#include "game/components/observer-component.hpp" +#include "game/components/orbit-component.hpp" +#include "game/components/terrain-component.hpp" +#include "game/components/transform-component.hpp" +#include "game/systems/astronomy-system.hpp" +#include "game/systems/atmosphere-system.hpp" +#include "game/systems/orbit-system.hpp" +#include "game/systems/terrain-system.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include #include -namespace game { namespace world { /// Loads an ephemeris. -static void load_ephemeris(game::context& ctx); +static void load_ephemeris(::context& ctx); /// Creates the fixed stars. -static void create_stars(game::context& ctx); +static void create_stars(::context& ctx); /// Creates the Sun. -static void create_sun(game::context& ctx); +static void create_sun(::context& ctx); /// Creates the Earth-Moon system. -static void create_earth_moon_system(game::context& ctx); +static void create_earth_moon_system(::context& ctx); /// Creates the Earth. -static void create_earth(game::context& ctx); +static void create_earth(::context& ctx); /// Creates the Moon. -static void create_moon(game::context& ctx); +static void create_moon(::context& ctx); -void cosmogenesis(game::context& ctx) +void cosmogenesis(::context& ctx) { debug::log::trace("Generating cosmos..."); @@ -103,7 +102,7 @@ void cosmogenesis(game::context& ctx) debug::log::trace("Generated cosmos"); } -void create_observer(game::context& ctx) +void create_observer(::context& ctx) { debug::log::trace("Creating observer..."); @@ -113,7 +112,7 @@ void create_observer(game::context& ctx) ctx.entities["observer"] = observer_eid; // Construct observer component - game::component::observer observer; + ::observer_component observer; // Set observer reference body if (auto it = ctx.entities.find("earth"); it != ctx.entities.end()) @@ -127,7 +126,7 @@ void create_observer(game::context& ctx) observer.longitude = 0.0; // Assign observer component to observer entity - ctx.entity_registry->emplace(observer_eid, observer); + ctx.entity_registry->emplace<::observer_component>(observer_eid, observer); // Set atmosphere system active atmosphere ctx.atmosphere_system->set_active_atmosphere(observer.reference_body_eid); @@ -139,16 +138,16 @@ void create_observer(game::context& ctx) debug::log::trace("Created observer"); } -void set_location(game::context& ctx, double elevation, double latitude, double longitude) +void set_location(::context& ctx, double elevation, double latitude, double longitude) { if (auto it = ctx.entities.find("observer"); it != ctx.entities.end()) { entity::id observer_eid = it->second; - if (ctx.entity_registry->valid(observer_eid) && ctx.entity_registry->all_of(observer_eid)) + if (ctx.entity_registry->valid(observer_eid) && ctx.entity_registry->all_of<::observer_component>(observer_eid)) { // Update observer location - ctx.entity_registry->patch + ctx.entity_registry->patch<::observer_component> ( observer_eid, [&](auto& component) @@ -162,7 +161,7 @@ void set_location(game::context& ctx, double elevation, double latitude, double } } -void set_time(game::context& ctx, double t) +void set_time(::context& ctx, double t) { try { @@ -177,7 +176,7 @@ void set_time(game::context& ctx, double t) } } -void set_time(game::context& ctx, int year, int month, int day, int hour, int minute, double second) +void set_time(::context& ctx, int year, int month, int day, int hour, int minute, double second) { double longitude = 0.0; @@ -187,7 +186,7 @@ void set_time(game::context& ctx, int year, int month, int day, int hour, int mi entity::id observer_eid = it->second; if (ctx.entity_registry->valid(observer_eid)) { - const auto observer = ctx.entity_registry->try_get(observer_eid); + const auto observer = ctx.entity_registry->try_get<::observer_component>(observer_eid); if (observer) longitude = observer->longitude; } @@ -202,7 +201,7 @@ void set_time(game::context& ctx, int year, int month, int day, int hour, int mi set_time(ctx, t); } -void set_time_scale(game::context& ctx, double scale) +void set_time_scale(::context& ctx, double scale) { // Convert time scale from seconds to days const double astronomical_scale = scale / physics::time::seconds_per_day; @@ -211,12 +210,12 @@ void set_time_scale(game::context& ctx, double scale) ctx.astronomy_system->set_time_scale(astronomical_scale); } -void load_ephemeris(game::context& ctx) +void load_ephemeris(::context& ctx) { ctx.orbit_system->set_ephemeris(ctx.resource_manager->load>("de421.eph")); } -void create_stars(game::context& ctx) +void create_stars(::context& ctx) { debug::log::trace("Generating fixed stars..."); @@ -353,7 +352,7 @@ void create_stars(game::context& ctx) debug::log::trace("Generated fixed stars"); } -void create_sun(game::context& ctx) +void create_sun(::context& ctx) { debug::log::trace("Generating Sun..."); @@ -399,7 +398,7 @@ void create_sun(game::context& ctx) debug::log::trace("Generated Sun"); } -void create_earth_moon_system(game::context& ctx) +void create_earth_moon_system(::context& ctx) { debug::log::trace("Generating Earth-Moon system..."); @@ -419,7 +418,7 @@ void create_earth_moon_system(game::context& ctx) debug::log::trace("Generated Earth-Moon system"); } -void create_earth(game::context& ctx) +void create_earth(::context& ctx) { debug::log::trace("Generating Earth..."); @@ -430,13 +429,13 @@ void create_earth(game::context& ctx) ctx.entities["earth"] = earth_eid; // Assign orbital parent - ctx.entity_registry->get(earth_eid).parent = ctx.entities["em_bary"]; + ctx.entity_registry->get<::orbit_component>(earth_eid).parent = ctx.entities["em_bary"]; } debug::log::trace("Generated Earth"); } -void create_moon(game::context& ctx) +void create_moon(::context& ctx) { debug::log::trace("Generating Moon..."); @@ -447,7 +446,7 @@ void create_moon(game::context& ctx) ctx.entities["moon"] = moon_eid; // Assign orbital parent - ctx.entity_registry->get(moon_eid).parent = ctx.entities["em_bary"]; + ctx.entity_registry->get<::orbit_component>(moon_eid).parent = ctx.entities["em_bary"]; // Pass moon model to sky pass ctx.sky_pass->set_moon_model(ctx.resource_manager->load("moon.mdl")); @@ -467,7 +466,7 @@ void create_moon(game::context& ctx) debug::log::trace("Generated Moon"); } -void enter_ecoregion(game::context& ctx, const ecoregion& ecoregion) +void enter_ecoregion(::context& ctx, const ecoregion& ecoregion) { /* image img; @@ -531,7 +530,7 @@ void enter_ecoregion(game::context& ctx, const ecoregion& ecoregion) ctx.active_ecoregion = &ecoregion; // Set location - game::world::set_location(ctx, ecoregion.elevation, ecoregion.latitude, ecoregion.longitude); + ::world::set_location(ctx, ecoregion.elevation, ecoregion.latitude, ecoregion.longitude); // Setup sky ctx.sky_pass->set_sky_model(ctx.resource_manager->load("celestial-hemisphere.mdl")); @@ -571,4 +570,3 @@ void enter_ecoregion(game::context& ctx, const ecoregion& ecoregion) } } // namespace world -} // namespace game diff --git a/src/game/world.hpp b/src/game/world.hpp index 8fbb033..cc569fc 100644 --- a/src/game/world.hpp +++ b/src/game/world.hpp @@ -23,16 +23,15 @@ #include "game/context.hpp" #include "game/ecoregion.hpp" -namespace game { /// World creation and manipulation functions. namespace world { /// Creates the cosmos. -void cosmogenesis(game::context& ctx); +void cosmogenesis(::context& ctx); /// Creates the observer. -void create_observer(game::context& ctx); +void create_observer(::context& ctx); /** * Sets the location of the observer. @@ -42,7 +41,7 @@ void create_observer(game::context& ctx); * @param latitude Latitude, in radians. * @param longitude Longitude, in radians. */ -void set_location(game::context& ctx, double elevation, double latitude, double longitude); +void set_location(::context& ctx, double elevation, double latitude, double longitude); /** * Sets the current time. @@ -50,7 +49,7 @@ void set_location(game::context& ctx, double elevation, double latitude, double * @param ctx Game context. * @param t UT1 time, in days. */ -void set_time(game::context& ctx, double t); +void set_time(::context& ctx, double t); /** * Sets the current time. @@ -63,7 +62,7 @@ void set_time(game::context& ctx, double t); * @param minute Minute number on `[0, 59]`. * @param second Fractional second on `[0.0, 60.0)`. */ -void set_time(game::context& ctx, int year, int month, int day, int hour, int minute, double second); +void set_time(::context& ctx, int year, int month, int day, int hour, int minute, double second); /** * Sets rate at which time passes. @@ -71,7 +70,7 @@ void set_time(game::context& ctx, int year, int month, int day, int hour, int mi * @param ctx Game context. * @param scale Time scale. */ -void set_time_scale(game::context& ctx, double scale); +void set_time_scale(::context& ctx, double scale); /** * Enters a ecoregion. @@ -79,9 +78,8 @@ void set_time_scale(game::context& ctx, double scale); * @param ctx Game context. * @param ecoregion Ecoregion to enter. */ -void enter_ecoregion(game::context& ctx, const ecoregion& ecoregion); +void enter_ecoregion(::context& ctx, const ecoregion& ecoregion); } // namespace menu -} // namespace game #endif // ANTKEEPER_GAME_WORLD_HPP diff --git a/src/genetics/base.cpp b/src/genetics/base.cpp deleted file mode 100644 index 883b20c..0000000 --- a/src/genetics/base.cpp +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "base.hpp" - -namespace genetics { -namespace base { - -/** - * Decodes an IUPAC degenerate base symbol into a bit mask representing the possible bases represented by the symbol. - * - * @param symbol IUPAC degenerate base symbol. - * @return Bit mask representing the possible bases represented by the symbol. - */ -static inline unsigned char decode(char symbol) -{ - static constexpr unsigned char bases[25] = - { - 0b0001, // A - 0b1110, // B - 0b0010, // C - 0b1101, // D - 0, // E - 0, // F - 0b0100, // G - 0b1011, // H - 0, // I - 0, // J - 0b1100, // K - 0, // L - 0b0011, // M - 0b1111, // N - 0, // O - 0, // P - 0, // Q - 0b0101, // R - 0b0110, // S - 0b1000, // T - 0b1000, // U - 0b0111, // V - 0b1001, // W - 0, // X - 0b1010, // Y - }; - - return (symbol < 'A' || symbol >= 'Z') ? 0 : bases[symbol - 'A']; -} - -int compare(char a, char b) -{ - static constexpr int popcount[16] = - { - 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 - }; - - return popcount[decode(a) & decode(b)]; -} - -char transcribe(char symbol) -{ - return (symbol == 'T') ? 'U' : (symbol == 'U') ? 'T' : symbol; -} - -namespace dna -{ - char complement(char symbol) - { - constexpr const char* complements = "TVGHZZCDZZMZKNZZZYSAABWZR"; - return (symbol < 'A' || symbol >= 'Z') ? 'Z' : complements[symbol - 'A']; - } -} - -namespace rna -{ - char complement(char symbol) - { - constexpr const char* complements = "UVGHZZCDZZMZKNZZZYSAABWZR"; - return (symbol < 'A' || symbol >= 'Z') ? 'Z' : complements[symbol - 'A']; - } -} - -} // namespace base -} // namespace genetics diff --git a/src/genetics/codon.cpp b/src/genetics/codon.cpp deleted file mode 100644 index 40ad09d..0000000 --- a/src/genetics/codon.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "codon.hpp" - -namespace genetics { -namespace codon { - -/** - * Returns the index of a nucleobase for use with a translation table. - * - * @param base IUPAC code of nucleobase, either `U`, `T`, `C`, `A`, or `G`. - * @return Index of the nucleobase, or a negative value if a non-standard nucleobase was supplied. - */ -static inline int base_index(char base) -{ - switch (base) - { - case 'U': - case 'T': - return 0; - case 'C': - return 1; - case 'A': - return 2; - case 'G': - return 3; - } - - return ~3; -} - -/** - * Returns the index of a codon for use with a translation table. - * - * @param base1 IUPAC code of first nucleobase, either `U`, `T`, `C`, `A`, or `G`. - * @param base2 IUPAC code of second nucleobase, either `U`, `T`, `C`, `A`, or `G`. - * @param base3 IUPAC code of third nucleobase, either `U`, `T`, `C`, `A`, or `G`. - * @return Index of codon, or a negative value if a non-standard nucleobase was supplied. - */ -static inline int codon_index(char base1, char base2, char base3) -{ - int i = base_index(base1); - int j = base_index(base2); - int k = base_index(base3); - return (i << 4) | (j << 2) | k; -} - -inline char translate(char base1, char base2, char base3, const char* aas) -{ - int index = codon_index(base1, base2, base3); - if (index < 0) - return '-'; - return aas[index]; -} - -bool is_start(char base1, char base2, char base3, const char* starts) -{ - char aa = translate(base1, base2, base3, starts); - return ((aa != '-') && (aa != '*')); -} - -bool is_stop(char base1, char base2, char base3, const char* aas) -{ - char aa = translate(base1, base2, base3, aas); - return (aa == '*'); -} - -} // namspace codon -} // namespace genetics diff --git a/src/genetics/genetics.hpp b/src/genetics/genetics.hpp deleted file mode 100644 index 0ac323f..0000000 --- a/src/genetics/genetics.hpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GENETICS_HPP -#define ANTKEEPER_GENETICS_HPP - -/// Genetic algorithms -namespace genetics {} - -#include "amino-acid.hpp" -#include "base.hpp" -#include "codon.hpp" -#include "matrix.hpp" -#include "protein.hpp" -#include "sequence.hpp" -#include "standard-code.hpp" - -#endif // ANTKEEPER_GENETICS_HPP diff --git a/src/genetics/protein.hpp b/src/genetics/protein.hpp deleted file mode 100644 index 4f8e25f..0000000 --- a/src/genetics/protein.hpp +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GENETICS_PROTEIN_HPP -#define ANTKEEPER_GENETICS_PROTEIN_HPP - -#include "amino-acid.hpp" -#include - -namespace genetics { - -/// Functions which operate on sequences of IUPAC amino acid symbols. -namespace protein { - -/** - * Returns the percent identity between two proteins. - * - * @param first1,last1 Range of IUPAC amino acids which constitute the first protein. - * @param first2 Beginning of the range of IUPAC amino acids which constitute the second protein. - * @return Percent identity between the two proteins. - */ -template -T identity(ForwardIt1 first1, ForwardIt1 last1, ForwardIt2 first2); - -/** - * Scores two proteins using a substitution matrix. - * - * @param first1,last1 Range of IUPAC amino acid codes which constitute the first protein. - * @param first2 Beginning of the range of IUPAC amino acid codes which constitute the second protein. - * @param matrix Substitution matrix. - * @return Score of the two proteins. - */ -template -typename std::remove_all_extents::type score(ForwardIt1 first1, ForwardIt1 last1, ForwardIt2 first2, const Matrix& matrix); - -/** - * Returns the percent similarity between two proteins. - * - * @param first1,last1 Range of IUPAC amino acids which constitute the first protein. - * @param first2 Beginning of the range of IUPAC amino acids which constitute the second protein. - * @return Percent similarity between the two proteins. - */ -template -typename T similarity(ForwardIt1 first1, ForwardIt1 last1, ForwardIt2 first2, const Matrix& matrix); - -template -T identity(ForwardIt1 first1, ForwardIt1 last1, ForwardIt2 first2) -{ - auto length = std::distance(first1, last1); - - T sum = 0; - for (; first1 != last1; ++first1, ++first2) - if (*first1 == *first2) - ++sum; - - return sum / static_cast(length); -} - -template -typename std::remove_all_extents::type score(ForwardIt1 first1, ForwardIt1 last1, ForwardIt2 first2, const Matrix& matrix) -{ - typename std::remove_all_extents::type result = 0; - for (; first1 != last1; ++first1, ++first2) - result += amino_acid::score(*first1, *first2, matrix); - return result; -} - -template -typename T similarity(ForwardIt1 first1, ForwardIt1 last1, ForwardIt2 first2, const Matrix& matrix) -{ - T length = static_cast(std::distance(first1, last1)); - T positive_count = T(0); - - for (; first1 != last1; ++first1, ++first2) - if (amino_acid::score(*first1, *first2, matrix) > 0) - ++positive_count; - - return positive_count / length; -} - -} // namespace protein -} // namespace genetics - -#endif // ANTKEEPER_GENETICS_PROTEIN_HPP diff --git a/src/genetics/sequence.hpp b/src/genetics/sequence.hpp deleted file mode 100644 index b8c7db5..0000000 --- a/src/genetics/sequence.hpp +++ /dev/null @@ -1,360 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GENETICS_SEQUENCE_HPP -#define ANTKEEPER_GENETICS_SEQUENCE_HPP - -#include "base.hpp" -#include "codon.hpp" -#include -#include -#include - -namespace genetics { - -/// Functions and structures related to sequences of IUPAC degenerate base symbols. -namespace sequence { - -/** - * Open reading frame (ORF), defined by a start codon and stop codon, with the distance between divisible by three. - * - * @tparam Iterator Sequence iterator type. - */ -template -struct orf -{ - /// Iterator to the first base of the start codon. - Iterator start; - - /// Iterator to the first base of the stop codon. - Iterator stop; -}; - -/** - * Exchanges elements between two ranges, starting at a random offset. - * - * @param first1,last1 First range of elements to crossover. - * @param first2 Beginning of the second range of elements to crossover. - * @param g Uniform random bit generator. - * @return Iterator to the start of the crossover in the second range. - */ -template -ForwardIt2 crossover(ForwardIt1 first1, ForwardIt1 last1, ForwardIt2 first2, URBG&& g); - -/** - * Exchanges elements between two ranges multiple times, starting at a random offset each time. - * - * @param first1,last1 First range of elements to crossover. - * @param first2 Beginning of the second range of elements to crossover. - * @param count Number of times to crossover. - * @param g Uniform random bit generator. - */ -template -void crossover_n(ForwardIt1 first1, ForwardIt1 last1, ForwardIt2 first2, Size count, URBG&& g); - -/** - * Searches a sequence for an open reading frame (ORF). - * - * @param first,last Range of elements to search. - * @param table Genetic code translation table. - * @return First ORF in the sequence, or `{last, last}` if no ORF was found. - */ -template -orf find_orf(ForwardIt first, ForwardIt last, const codon::table& table); - -/** - * Applies the given function to a randomly selected element in a range. - * - * @param first,last Range of elements to mutate. - * @param unary_op Unary operation function object that will be applied. - * @param g Uniform random bit generator. - * @return Iterator to the mutated element. - */ -template -ForwardIt mutate(ForwardIt first, ForwardIt last, UnaryOperation unary_op, URBG&& g); - -/** - * Applies the given function to a random selection of elements in a range. - * - * @param first,last Range of elements to mutate. - * @param count Number of elements to mutate. - * @param unary_op Unary operation function object that will be applied. - * @param g Uniform random bit generator. - */ -template -void mutate_n(ForwardIt first, ForwardIt last, Size count, UnaryOperation unary_op, URBG&& g); - -/** - * Searches a sequence of IUPAC base symbols for a pattern matching a search string of IUPAC degenerate base symbols. - * - * @param first,last Sequence of IUPAC base symbols to search. - * @param s_first,s_last Search string of IUPAC degenerate base symbols. - * @param stride Distance between consecutive searches. - * @return Iterator to the beginning of the first subsequence matching `[s_first, s_last)` in the sequence `[first, last)`. If no such occurrence is found, @p last is returned. - */ -template -ForwardIt1 search(ForwardIt1 first, ForwardIt1 last, ForwardIt2 s_first, ForwardIt2 s_last, typename std::iterator_traits::difference_type stride); - -/** - * Transcribes a sequence of IUPAC base symbols between DNA and RNA, swapping `T` for `U` or `U` for `T`. - * - * @param first,last Range of elements to transcribe. - * @param d_first Beginning of the destination range. - * @return Output iterator to the element past the last element transcribed. - */ -template -OutputIt transcribe(InputIt first, InputIt last, OutputIt d_first); - -/** - * Translates a sequence of codons into amino acids. - * - * @param first,last Open reading frame. - * @param d_first Beginning of destination range. - * @param table Genetic code translation table. - * @return Output iterator to the element past the last element translated. - */ -template -OutputIt translate(InputIt first, InputIt last, OutputIt d_first, const codon::table& table); - -/// Functions which operate on sequences of IUPAC degenerate **DNA** base symbols. -namespace dna -{ - /** - * Generates the complementary sequence for a sequence of IUPAC degenerate DNA base symbols. - * - * @param first,last Range of elements to complement. - * @param d_first Beginning of the destination range. - * @return Output iterator to the element past the last element complemented. - */ - template - OutputIt complement(InputIt first, InputIt last, OutputIt d_first); -} - -/// Functions which operate on sequences of IUPAC degenerate **RNA** base symbols. -namespace rna -{ - /** - * Generates the complementary sequence for a sequence of IUPAC degenerate RNA base symbols. - * - * @param first,last Range of elements to complement. - * @param d_first Beginning of the destination range. - * @return Output iterator to the element past the last element complemented. - */ - template - OutputIt complement(InputIt first, InputIt last, OutputIt d_first); -} - -template -ForwardIt2 crossover(ForwardIt1 first1, ForwardIt1 last1, ForwardIt2 first2, URBG&& g) -{ - typedef typename std::iterator_traits::difference_type difference_t; - std::uniform_int_distribution distribution(0, std::distance(first1, last1) - 1); - difference_t pos = distribution(g); - std::advance(first1, pos); - std::advance(first2, pos); - std::swap_ranges(first1, last1, first2); - return first2; -} - -template -void crossover_n(ForwardIt1 first1, ForwardIt1 last1, ForwardIt2 first2, Size count, URBG&& g) -{ - typedef typename std::iterator_traits::difference_type difference_t; - - std::uniform_int_distribution distribution(0, std::distance(first1, last1) - 1); - ForwardIt1 crossover1, crossover2; - - while (count) - { - crossover1 = first1; - crossover2 = first2; - - difference_t pos = distribution(g); - std::advance(crossover1, pos); - std::advance(crossover2, pos); - std::swap_ranges(crossover1, last1, crossover2); - - --count; - } -} - -template -orf find_orf(ForwardIt first, ForwardIt last, const codon::table& table) -{ - ForwardIt second; - ForwardIt third; - orf result; - - auto distance = std::distance(first, last); - - if (distance >= 3) - { - second = first; - ++second; - third = second; - ++third; - - do - { - if (codon::is_start(*first, *second, *third, table.starts)) - { - result.start = first; - distance -= 3; - break; - } - - first = second; - second = third; - ++third; - --distance; - } - while (third != last); - } - - for (; distance >= 3; distance -= 3) - { - first = ++third; - second = ++third; - ++third; - - if (codon::is_stop(*first, *second, *third, table.aas)) - { - result.stop = first; - return result; - } - } - - return {last, last}; -} - -template -ForwardIt mutate(ForwardIt first, ForwardIt last, UnaryOperation unary_op, URBG&& g) -{ - typedef typename std::iterator_traits::difference_type difference_t; - - if (first == last) - return first; - - std::uniform_int_distribution distribution(0, std::distance(first, last) - 1); - std::advance(first, distribution(g)); - *first = unary_op(*first); - - return first; -} - -template -void mutate_n(ForwardIt first, ForwardIt last, Size count, UnaryOperation unary_op, URBG&& g) -{ - typedef typename std::iterator_traits::difference_type difference_t; - - if (first == last) - return first; - - std::uniform_int_distribution distribution(0, std::distance(first, last) - 1); - ForwardIt mutation; - - while (count) - { - mutation = first; - std::advance(mutation, distribution(g)); - *mutation = unary_op(*mutation); - --count; - } -} - -template -ForwardIt1 search(ForwardIt1 first, ForwardIt1 last, ForwardIt2 s_first, ForwardIt2 s_last, typename std::iterator_traits::difference_type stride) -{ - for (auto distance = std::distance(first, last); distance > 0; distance -= stride) - { - ForwardIt1 it = first; - for (ForwardIt2 s_it = s_first; ; ++it, ++s_it) - { - if (s_it == s_last) - return first; - - if (it == last) - return last; - - if (!base::compare(*it, *s_it)) - break; - } - - if (distance > stride) - std::advance(first, stride); - } - - return last; -} - -template -inline OutputIt transcribe(InputIt first, InputIt last, OutputIt d_first) -{ - return std::transform(first, last, d_first, base::transcribe); -} - -template -OutputIt translate(InputIt first, InputIt last, OutputIt d_first, const codon::table& table) -{ - auto length = std::distance(first, last); - - if (length >= 3) - { - InputIt second = first; - ++second; - InputIt third = second; - ++third; - - *(d_first++) = codon::translate(*first, *second, *third, table.starts); - - for (length -= 3; length >= 3; length -= 3) - { - first = ++third; - second = ++third; - ++third; - - *(d_first++) = codon::translate(*first, *second, *third, table.aas); - } - } - - return d_first; -} - -namespace dna -{ - template - inline OutputIt complement(InputIt first, InputIt last, OutputIt d_first) - { - return std::transform(first, last, d_first, base::dna::complement); - } -} - -namespace rna -{ - template - inline OutputIt complement(InputIt first, InputIt last, OutputIt d_first) - { - return std::transform(first, last, d_first, base::rna::complement); - } -} - -} // namespace sequence -} // namespace genetics - -#endif // ANTKEEPER_GENETICS_SEQUENCE_HPP diff --git a/src/genetics/standard-code.hpp b/src/genetics/standard-code.hpp deleted file mode 100644 index 9c53f84..0000000 --- a/src/genetics/standard-code.hpp +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GENETICS_STANDARD_CODE_HPP -#define ANTKEEPER_GENETICS_STANDARD_CODE_HPP - -#include "codon.hpp" - -namespace genetics { - -/** - * Codon table for standard genetic code. - * - * @see https://www.ncbi.nlm.nih.gov/Taxonomy/Utils/wprintgc.cgi#SG1 - */ -constexpr codon::table standard_code = -{ - "FFLLSSSSYY**CC*WLLLLPPPPHHQQRRRRIIIMTTTTNNKKSSRRVVVVAAAADDEEGGGG", - "---M------**--*----M---------------M----------------------------", -}; - -} // namespace genetics - -#endif // ANTKEEPER_GENETICS_STANDARD_CODE_HPP diff --git a/src/geom/aabb.hpp b/src/geom/aabb.hpp deleted file mode 100644 index ae6b255..0000000 --- a/src/geom/aabb.hpp +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GEOM_AABB_HPP -#define ANTKEEPER_GEOM_AABB_HPP - -#include "bounding-volume.hpp" -#include "sphere.hpp" -#include "math/vector.hpp" -#include "math/matrix.hpp" -#include "math/transform-type.hpp" -#include - -namespace geom { - -/** - * Axis-aligned bounding box. - */ -template -struct aabb: public bounding_volume -{ - typedef math::vector vector_type; - typedef math::matrix matrix_type; - typedef math::transform transform_type; - - vector_type min_point; - vector_type max_point; - - /** - * Transforms an AABB. - * - * @param a AABB to be transformed. - * @param t Transform by which the AABB should be transformed. - * @return Transformed AABB. - */ - static aabb transform(const aabb& a, const transform_type& t); - - /** - * Transforms an AABB. - * - * @param a AABB to be transformed. - * @param m Matrix by which the AABB should be transformed. - * @return Transformed AABB. - */ - static aabb transform(const aabb& a, const matrix_type& m); - - aabb(const vector_type& min_point, const vector_type& max_point); - aabb(); - - virtual bounding_volume_type get_bounding_volume_type() const; - virtual bool intersects(const sphere& sphere) const; - virtual bool intersects(const aabb& aabb) const; - virtual bool contains(const sphere& sphere) const; - virtual bool contains(const aabb& aabb) const; - virtual bool contains(const vector_type& point) const; - - /** - * Returns the position of the specified corner. - * - * @param index Index of a corner. - * @return Position of the specified corner. - */ - vector_type corner(int index) const noexcept; -}; - -template -aabb aabb::transform(const aabb& a, const transform_type& t) -{ - vector_type min_point = {std::numeric_limits::infinity(), std::numeric_limits::infinity(), std::numeric_limits::infinity()}; - vector_type max_point = {-std::numeric_limits::infinity(), -std::numeric_limits::infinity(), -std::numeric_limits::infinity()}; - - for (std::size_t i = 0; i < 8; ++i) - { - vector_type transformed_corner = math::mul(t, a.corner(i)); - - for (std::size_t j = 0; j < 3; ++j) - { - min_point[j] = std::min(min_point[j], transformed_corner[j]); - max_point[j] = std::max(max_point[j], transformed_corner[j]); - } - } - - return {min_point, max_point}; -} - -template -aabb aabb::transform(const aabb& a, const matrix_type& m) -{ - vector_type min_point = {std::numeric_limits::infinity(), std::numeric_limits::infinity(), std::numeric_limits::infinity()}; - vector_type max_point = {-std::numeric_limits::infinity(), -std::numeric_limits::infinity(), -std::numeric_limits::infinity()}; - - for (std::size_t i = 0; i < 8; ++i) - { - vector_type corner = a.corner(i); - math::vector transformed_corner = math::mul(m, math::vector{corner.x(), corner.y(), corner.z(), T(1)}); - - for (std::size_t j = 0; j < 3; ++j) - { - min_point[j] = std::min(min_point[j], transformed_corner[j]); - max_point[j] = std::max(max_point[j], transformed_corner[j]); - } - } - - return {min_point, max_point}; -} - -template -aabb::aabb(const vector_type& min_point, const vector_type& max_point): - min_point(min_point), - max_point(max_point) -{} - -template -aabb::aabb() -{} - -template -inline bounding_volume_type aabb::get_bounding_volume_type() const -{ - return bounding_volume_type::aabb; -} - -template -bool aabb::intersects(const sphere& sphere) const -{ - const vector_type radius_vector = {sphere.radius, sphere.radius, sphere.radius}; - return aabb(min_point - radius_vector, max_point + radius_vector).contains(sphere.center); -} - -template -bool aabb::intersects(const aabb& aabb) const -{ - if (max_point.x() < aabb.min_point.x() || min_point.x() > aabb.max_point.x()) - return false; - if (max_point.y() < aabb.min_point.y() || min_point.y() > aabb.max_point.y()) - return false; - if (max_point.z() < aabb.min_point.z() || min_point.z() > aabb.max_point.z()) - return false; - return true; -} - -template -bool aabb::contains(const sphere& sphere) const -{ - if (sphere.center.x() - sphere.radius < min_point.x() || sphere.center.x() + sphere.radius > max_point.x()) - return false; - if (sphere.center.y() - sphere.radius < min_point.y() || sphere.center.y() + sphere.radius > max_point.y()) - return false; - if (sphere.center.z() - sphere.radius < min_point.z() || sphere.center.z() + sphere.radius > max_point.z()) - return false; - return true; -} - -template -bool aabb::contains(const aabb& aabb) const -{ - if (aabb.min_point.x() < min_point.x() || aabb.max_point.x() > max_point.x()) - return false; - if (aabb.min_point.y() < min_point.y() || aabb.max_point.y() > max_point.y()) - return false; - if (aabb.min_point.z() < min_point.z() || aabb.max_point.z() > max_point.z()) - return false; - return true; -} - -template -bool aabb::contains(const vector_type& point) const -{ - if (point.x() < min_point.x() || point.x() > max_point.x()) - return false; - if (point.y() < min_point.y() || point.y() > max_point.y()) - return false; - if (point.z() < min_point.z() || point.z() > max_point.z()) - return false; - return true; -} - -template -typename aabb::vector_type aabb::corner(int index) const noexcept -{ - return - { - ((index >> 2) & 1) ? max_point[0] : min_point[0], - ((index >> 1) & 1) ? max_point[1] : min_point[1], - ((index >> 0) & 1) ? max_point[2] : min_point[2] - }; -} - -} // namespace geom - -#endif // ANTKEEPER_GEOM_AABB_HPP - diff --git a/src/geom/bounding-volume.hpp b/src/geom/bounding-volume.hpp deleted file mode 100644 index 66a8498..0000000 --- a/src/geom/bounding-volume.hpp +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GEOM_BOUNDING_VOLUME_HPP -#define ANTKEEPER_GEOM_BOUNDING_VOLUME_HPP - -#include "math/vector.hpp" -#include - -namespace geom { - -template -struct sphere; -template -struct aabb; - -/// Enumerates bounding volume types. -enum class bounding_volume_type -{ - aabb, ///< Indicates the bounding volume is an axis-aligned bounding box. - sphere, ///< Indicates the bounding volume is a sphere. - convex_hull ///< Indicates the bounding volume is a convex hull. -}; - -/** - * Abstract base class for bounding volumes. - * - * @tparam T Scalar type. - */ -template -class bounding_volume -{ -public: - /// Returns the enumerated type of this bounding volume. - virtual bounding_volume_type get_bounding_volume_type() const = 0; - - /// Tests for intersection between this bounding volume and a bounding sphere. - virtual bool intersects(const sphere& sphere) const = 0; - - /// Tests for intersection between this bounding volume and an axis-aligned bounding box. - virtual bool intersects(const aabb& aabb) const = 0; - - /// Tests whether this bounding volume contains a sphere. - virtual bool contains(const sphere& sphere) const = 0; - - /// Tests whether this bounding volume contains an axis-aligned bounding box. - virtual bool contains(const aabb& aabb) const = 0; - - /// Tests whether this bounding volume contains a point. - virtual bool contains(const math::vector& point) const = 0; - - /// Tests for intersection between this bounding volume and another bounding volume. - bool intersects(const bounding_volume& volume) const; -}; - -/// Tests for intersection between this bounding volume and another bounding volume. -template -bool bounding_volume::intersects(const bounding_volume& volume) const -{ - const bounding_volume_type type = volume.get_bounding_volume_type(); - switch (type) - { - case bounding_volume_type::sphere: - return intersects(static_cast&>(volume)); - break; - - case bounding_volume_type::aabb: - return intersects(static_cast&>(volume)); - break; - - default: - throw std::runtime_error("unimplemented"); - break; - } -} - -} // namespace geom - -#endif // ANTKEEPER_GEOM_BOUNDING_VOLUME_HPP diff --git a/src/geom/cartesian.hpp b/src/geom/cartesian.hpp deleted file mode 100644 index 76dea9e..0000000 --- a/src/geom/cartesian.hpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GEOM_CARTESIAN_HPP -#define ANTKEEPER_GEOM_CARTESIAN_HPP - -#include "utility/fundamental-types.hpp" -#include - -namespace geom { - -/// Functions which operate on Cartesian (rectangular) coordinates. -namespace cartesian { - -/** - * Converts Cartesian (rectangular) coordinates to spherical coordinates. - * - * @param v Cartesian coordinates. - * @return Spherical coordinates, in the ISO order of radial distance, polar angle (radians), and azimuthal angle (radians). - * - * @see geom::coordinates::spherical - */ -template -math::vector3 to_spherical(const math::vector3& v); - -template -math::vector3 to_spherical(const math::vector3& v) -{ - const T xx_yy = v.x() * v.x() + v.y() * v.y(); - - return math::vector3 - { - std::sqrt(xx_yy + v.z() * v.z()), - std::atan2(v.z(), std::sqrt(xx_yy)), - std::atan2(v.y(), v.x()) - }; -} - -} // namespace cartesian -} // namespace geom - -#endif // ANTKEEPER_GEOM_CARTESIAN_HPP diff --git a/src/geom/convex-hull.hpp b/src/geom/convex-hull.hpp deleted file mode 100644 index 9f3c53a..0000000 --- a/src/geom/convex-hull.hpp +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GEOM_CONVEX_HULL_HPP -#define ANTKEEPER_GEOM_CONVEX_HULL_HPP - -#include "bounding-volume.hpp" -#include "geom/plane.hpp" -#include "geom/sphere.hpp" -#include "geom/aabb.hpp" -#include -#include - -namespace geom { - -/** - * A plane-bounded convex hull. - */ -template -struct convex_hull: public bounding_volume -{ - /// Vector of planes with descibe the bounds of the convex hull. - std::vector> planes; - - /** - * Creates a convex hull. - * - * @param size Number of planes the convex hull should accommodate. - */ - convex_hull(std::size_t size); - - /// Creates a convex hull. - convex_hull(); - - virtual bounding_volume_type get_bounding_volume_type() const; - virtual bool intersects(const sphere& sphere) const; - virtual bool intersects(const aabb& aabb) const; - virtual bool contains(const sphere& sphere) const; - virtual bool contains(const aabb& aabb) const; - virtual bool contains(const math::vector& point) const; -}; - -template -convex_hull::convex_hull(std::size_t size): - planes(size, plane()) -{} - -template -convex_hull::convex_hull() -{} - -template -inline bounding_volume_type convex_hull::get_bounding_volume_type() const -{ - return bounding_volume_type::convex_hull; -} - -template -bool convex_hull::intersects(const sphere& sphere) const -{ - for (const plane& plane: planes) - if (plane.signed_distance(sphere.center) < -sphere.radius) - return false; - return true; -} - -template -bool convex_hull::intersects(const aabb& aabb) const -{ - math::vector pv; - for (const plane& plane: planes) - { - pv.x() = (plane.normal.x() > T(0)) ? aabb.max_point.x() : aabb.min_point.x(); - pv.y() = (plane.normal.y() > T(0)) ? aabb.max_point.y() : aabb.min_point.y(); - pv.z() = (plane.normal.z() > T(0)) ? aabb.max_point.z() : aabb.min_point.z(); - if (plane.signed_distance(pv) < T(0)) - return false; - } - - return true; -} - -template -bool convex_hull::contains(const sphere& sphere) const -{ - for (const plane& plane: planes) - if (plane.signed_distance(sphere.center) < sphere.radius) - return false; - return true; -} - -template -bool convex_hull::contains(const aabb& aabb) const -{ - math::vector pv; - math::vector nv; - - for (const plane& plane: planes) - { - pv.x() = (plane.normal.x() > T(0)) ? aabb.max_point.x() : aabb.min_point.x(); - pv.y() = (plane.normal.y() > T(0)) ? aabb.max_point.y() : aabb.min_point.y(); - pv.z() = (plane.normal.z() > T(0)) ? aabb.max_point.z() : aabb.min_point.z(); - nv.x() = (plane.normal.x() < T(0)) ? aabb.max_point.x() : aabb.min_point.x(); - nv.y() = (plane.normal.y() < T(0)) ? aabb.max_point.y() : aabb.min_point.y(); - nv.z() = (plane.normal.z() < T(0)) ? aabb.max_point.z() : aabb.min_point.z(); - - if (plane.signed_distance(pv) < T(0) || plane.signed_distance(nv) < T(0)) - return false; - } - - return true; -} - -template -bool convex_hull::contains(const math::vector& point) const -{ - for (const plane& plane: planes) - if (plane.signed_distance(point) < T(0)) - return false; - - return true; -} - -} // namespace geom - -#endif // ANTKEEPER_GEOM_CONVEX_HULL_HPP - diff --git a/src/geom/csg.cpp b/src/geom/csg.cpp deleted file mode 100644 index 8272d67..0000000 --- a/src/geom/csg.cpp +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "csg.hpp" -#include - -namespace geom { -namespace csg { - -enum class polygon_classification -{ - coplanar, - front, - back, - spanning -}; - -/** - * Classifies a polygon relative to a partitioning plane. - * - * @param partition Partitioning plane relative to which the polygon should be classified. - * @param poly Polygon to be classified. - * @return Classification of the polygon, relative to the plane. - */ -static polygon_classification classify_polygon(const plane& partition, const polygon& poly) -{ - for (const float3& vertex: poly.vertices) - { - - } - - return polygon_classification::coplanar; -} - -/** - * Splits a polygon along a partitioning plane. - * - * @param poly Polygon to split. - * @param partition Partitioning plane along which the polygon should be split. - * @return List of polygons which were formed by splitting the specified polygon along the partitioning plane, along with their respective classifications relative to the partition. - */ -std::list> split_polygon(const polygon& poly, const plane& partition) -{ - return {}; -} - - -bsp_tree::bsp_tree(const std::list& polygons): - front(nullptr), - back(nullptr) -{ - //partition = polygons.front(); - - std::list front_polygons; - std::list back_polygons; - - // Classify all polygons relative to this node's partitioning plane - for (const polygon& p: polygons) - { - polygon_classification classification = classify_polygon(partition, p); - switch (classification) - { - case polygon_classification::coplanar: - coplanar_polygons.push_back(p); - break; - - case polygon_classification::front: - front_polygons.push_back(p); - break; - - case polygon_classification::back: - back_polygons.push_back(p); - break; - - case polygon_classification::spanning: - break; - } - } - - if (!front_polygons.empty()) - { - // Make subtree containing all polygons in front of this node's plane - front = new bsp_tree(front_polygons); - } - - if (!back_polygons.empty()) - { - // Make subtree containing all polygons behind this node's plane - back = new bsp_tree(back_polygons); - } -} - -bsp_tree::~bsp_tree() -{ - delete front; - delete back; -} - -} // namespace csg -} // namespace geom diff --git a/src/geom/csg.hpp b/src/geom/csg.hpp deleted file mode 100644 index 26dc29e..0000000 --- a/src/geom/csg.hpp +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GEOM_CSG_HPP -#define ANTKEEPER_GEOM_CSG_HPP - -#include "utility/fundamental-types.hpp" -#include - -namespace geom { - -/// Constructive solid geometry (CSG) -namespace csg { - -struct plane -{ - float3 normal; - float distance; -}; - -struct polygon -{ - std::list vertices; - void* shared; -}; - -/** - * 3D solid represented by a collection of polygons. - */ -typedef std::list solid; - -/** - * BSP tree node. - */ -class bsp_tree -{ -public: - /** - * Recursively constructs a BSP tree from a collection of polygons. - * - * @param polygons Collection of polygons from which to create the BSP tree. - */ - explicit bsp_tree(const std::list& polygons); - - /** - * Destroys a BSP tree. - */ - ~bsp_tree(); - -private: - /// Partition which separates the front and back polygons. - plane partition; - - /// Set of polygons which are coplanar with the partition. - std::list coplanar_polygons; - - /// Subtree containing all polygons in front of the partition. - bsp_tree* front; - - /// Subtree containing all polygons behind the partition. - bsp_tree* back; -}; - -solid op_union(const solid& a, const solid& b); -solid op_difference(const solid& a, const solid& b); -solid op_intersect(const solid& a, const solid& b); - -} // namespace csg -} // namespace geom - -#endif // ANTKEEPER_GEOM_CSG_HPP - diff --git a/src/geom/geom.hpp b/src/geom/geom.hpp deleted file mode 100644 index ef3fec5..0000000 --- a/src/geom/geom.hpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GEOM_HPP -#define ANTKEEPER_GEOM_HPP - -/// Geometry -namespace geom {} - -#include "aabb.hpp" -#include "bounding-volume.hpp" -#include "convex-hull.hpp" -#include "cartesian.hpp" -#include "csg.hpp" -#include "intersection.hpp" -#include "marching-cubes.hpp" -#include "mesh.hpp" -#include "mesh-accelerator.hpp" -#include "mesh-functions.hpp" -#include "morton.hpp" -#include "octree.hpp" -#include "plane.hpp" -#include "projection.hpp" -#include "ray.hpp" -#include "sdf.hpp" -#include "sphere.hpp" -#include "spherical.hpp" -#include "view-frustum.hpp" - -#endif // ANTKEEPER_GEOM_HPP diff --git a/src/geom/hyperoctree.hpp b/src/geom/hyperoctree.hpp deleted file mode 100644 index 9b04638..0000000 --- a/src/geom/hyperoctree.hpp +++ /dev/null @@ -1,577 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GEOM_HYPEROCTREE_HPP -#define ANTKEEPER_GEOM_HYPEROCTREE_HPP - -#include "math/compile.hpp" -#include -#include -#include -#include -#include -#include -#include -#include - -namespace geom { - -/// Orders in which hyperoctree nodes can be stored and traversed. -enum class hyperoctree_order -{ - /// Hyperoctree nodes are unordered, potentially resulting in faster insertions through the internal use of `std::unordered_set` rather than `std::set`. - unordered, - - /// Hyperoctree nodes are stored and traversed in depth-first preorder. - dfs_pre, - - /// Hyperoctree nodes are stored and traversed in breadth-first order. - bfs -}; - -/// @private -template -using hyperoctree_dfs_pre_compare = std::less; - -/// @private -template -struct hyperoctree_bfs_compare -{ - constexpr bool operator()(const T& lhs, const T& rhs) const noexcept - { - return std::rotr(lhs, DepthBits) < std::rotr(rhs, DepthBits); - } -}; - -/// @private -template -struct hyperoctree_container {}; - -/// @private -template -struct hyperoctree_container -{ - typedef std::unordered_set type; -}; - -/// @private -template -struct hyperoctree_container -{ - typedef std::set> type; -}; - -/// @private -template -struct hyperoctree_container -{ - typedef std::set> type; -}; - -/** - * Hashed linear hyperoctree. - * - * @tparam T Unsigned integral node identifier type. - * @tparam N Number of dimensions. - * @tparam Order Order in which nodes are stored and traversed. - * - * @see http://codervil.blogspot.com/2015/10/octree-node-identifiers.html - * @see https://geidav.wordpress.com/2014/08/18/advanced-octrees-2-node-representations/ - */ -template -class hyperoctree -{ -private: - /** - * Finds the maximum hyperoctree depth level from the size of the node type `T` and number of dimensions `N`. - * - * @return Maximum hyperoctree depth level. - * - * @note There is likely a more elegant formula for this. Information about the 2D and 3D cases is given below: - * - * 2D: - * 8 bit ( 1 byte) = max depth 1 ( 4 loc bits, 1 depth bits, 1 divider bit) = 6 bits - * 16 bit ( 2 byte) = max depth 5 ( 12 loc bits, 3 depth bits, 1 divider bit) = 16 bits - * 32 bit ( 4 byte) = max depth 12 ( 26 loc bits, 4 depth bits, 1 divider bit) = 31 bits - * 64 bit ( 8 byte) = max depth 28 ( 58 loc bits, 5 depth bits, 1 divider bit) = 64 bits - * 128 bit (16 byte) = max depth 59 (120 loc bits, 6 depth bits, 1 divider bit) = 127 bits - * 256 bit (32 byte) = max depth 123 (248 loc bits, 7 depth bits, 1 divider bit) = 256 bits - * - * @see https://oeis.org/A173009 - * - * 3D: - * 8 bit ( 1 byte) = max depth 1 ( 6 loc bits, 1 depth bits, 1 divider bit) = 8 bits - * 16 bit ( 2 byte) = max depth 3 ( 12 loc bits, 2 depth bits, 1 divider bit) = 15 bits - * 32 bit ( 4 byte) = max depth 8 ( 27 loc bits, 4 depth bits, 1 divider bit) = 32 bits - * 64 bit ( 8 byte) = max depth 18 ( 57 loc bits, 5 depth bits, 1 divider bit) = 63 bits - * 128 bit (16 byte) = max depth 39 (120 loc bits, 6 depth bits, 1 divider bit) = 127 bits - * 256 bit (32 byte) = max depth 81 (243 loc bits, 7 depth bits, 1 divider bit) = 251 bits - * - * @see https://oeis.org/A178420 - */ - static consteval std::size_t find_max_depth() noexcept - { - std::size_t max_depth = 0; - for (std::size_t i = 1; i <= sizeof(T) * 8; ++i) - { - const std::size_t location_bits = sizeof(T) * 8 - i; - max_depth = location_bits / N - 1; - const std::size_t depth_bits = static_cast(std::bit_width(max_depth)); - - if (depth_bits + location_bits < sizeof(T) * 8) - break; - } - return static_cast(max_depth); - } - -public: - /// Node identifier type. - typedef T node_type; - - /// Number of dimensions. - static constexpr std::size_t dimensions = N; - - /// Node storage and traversal order. - static constexpr hyperoctree_order order = Order; - - /// Maximum node depth level. - static constexpr node_type max_depth = find_max_depth(); - - /// Number of bits in the node type. - static constexpr node_type node_bits = sizeof(node_type) * 8; - - /// Number of bits required to encode the depth of a node. - static constexpr node_type depth_bits = std::bit_width(max_depth); - - /// Number of bits required to encode the Morton location code of a node. - static constexpr node_type location_bits = (max_depth + 1) * N; - - /// Number of bits separating the depth and Morton location code in a node identifier. - static constexpr node_type divider_bits = node_bits - (depth_bits + location_bits); - - /// Number of children per node. - static constexpr node_type children_per_node = math::compile::exp2(N); - - /// Number of siblings per node. - static constexpr node_type siblings_per_node = children_per_node - 1; - - /// Resolution in each dimension. - static constexpr node_type resolution = math::compile::exp2(max_depth); - - /// Number of nodes in a full hyperoctree. - static constexpr std::size_t max_node_count = (math::compile::pow(resolution * 2, N) - 1) / siblings_per_node; - - /// Node identifier of the persistent root node. - static constexpr node_type root = 0; - - /// Node container type. - typedef typename hyperoctree_container::type container_type; - - /// Iterator type. - typedef typename container_type::iterator iterator; - - /// Constant iterator type. - typedef typename container_type::const_iterator const_iterator; - - /// Reverse iterator type. - typedef std::conditional, iterator>::type reverse_iterator; - - /// Constant reverse iterator type. - typedef std::conditional, const_iterator>::type const_reverse_iterator; - - /// @name Nodes - /// @{ - /** - * Extracts the depth of a node from its identifier. - * - * @param node Node identifier. - * - * @return Depth of the node. - */ - static inline constexpr node_type depth(node_type node) noexcept - { - constexpr node_type mask = math::compile::exp2(depth_bits) - 1; - return node & mask; - } - - /** - * Extracts the Morton location code of a node from its identifier. - * - * @param node Node identifier. - * - * @return Morton location code of the node. - */ - static inline constexpr node_type location(node_type node) noexcept - { - return node >> ((node_bits - 1) - depth(node) * N); - } - - /** - * Extracts the depth and Morton location code of a node from its identifier. - * - * @param node Node identifier. - * - * @return Array containing the depth of the node, followed by the Morton location code of the node. - */ - static constexpr std::array split(node_type node) noexcept - { - const node_type depth = hyperoctree::depth(node); - const node_type location = node >> ((node_bits - 1) - depth * N); - return {depth, location}; - } - - /** - * Constructs an identifier for a node at the given depth and location. - * - * @param depth Depth level. - * @param location Morton location code. - * - * @return Identifier of a node at the given depth and location. - * - * @warning If @p depth exceeds `max_depth`, the returned node identifier is not valid. - */ - static inline constexpr node_type node(node_type depth, node_type location) noexcept - { - return (location << ((node_bits - 1) - depth * N)) | depth; - } - - /** - * Constructs an identifier for the ancestor of a node at a given depth. - * - * @param node Node identifier. - * @param depth Absolute depth of an ancestor node. - * - * @return Identifier of the ancestor of the node at the given depth. - * - * @warning If @p depth exceeds the depth of @p node, the returned node identifier is not valid. - */ - static inline constexpr node_type ancestor(node_type node, node_type depth) noexcept - { - const node_type mask = (~node_type(0)) << ((node_bits - 1) - depth * N); - return (node & mask) | depth; - } - - /** - * Constructs an identifier for the parent of a node. - * - * @param node Node identifier. - * - * @return Identifier of the parent node. - */ - static inline constexpr node_type parent(node_type node) noexcept - { - return ancestor(node, depth(node) - 1); - } - - /** - * Constructs an identifier for the nth sibling of a node. - * - * @param node Node identifier. - * @param n Offset to a sibling, automatically wrapped to `[0, siblings_per_node]`. - * - * @return Identifier of the nth sibling node. - */ - static constexpr node_type sibling(node_type node, node_type n) noexcept - { - constexpr node_type mask = (1 << N) - 1; - const auto [depth, location] = split(node); - const node_type sibling_location = (location & (~mask)) | ((location + n) & mask); - return hyperoctree::node(depth, sibling_location); - } - - /** - * Constructs an identifier for the nth child of a node. - * - * @param node Node identifier. - * @param n Offset to a sibling of the first child node, automatically wrapped to `[0, siblings_per_node]`. - * - * @return Identifier of the nth child node. - */ - static inline constexpr node_type child(node_type node, T n) noexcept - { - return sibling(node + 1, n); - } - - /** - * Constructs an identifier for the first common ancestor of two nodes - * - * @param a Identifier of the first node. - * @param b Identifier of the second node. - * - * @return Identifier of the first common ancestor of the two nodes. - */ - static constexpr node_type common_ancestor(node_type a, node_type b) noexcept - { - const node_type bits = std::min(depth(a), depth(b)) * N; - const node_type marker = (node_type(1) << (node_bits - 1)) >> bits; - const node_type depth = node_type(std::countl_zero((a ^ b) | marker) / N); - return ancestor(a, depth); - } - /// @} - - /// Constructs a hyperoctree with a single root node. - hyperoctree(): - nodes({root}) - {} - - /// @name Iterators - /// @{ - /** - * Returns an iterator to the first node, in the traversal order specified by hyperoctree::order. - * - * @note Node identifiers cannot be modified through iterators. - */ - /// @{ - inline iterator begin() noexcept - { - return nodes.begin(); - } - inline const_iterator begin() const noexcept - { - return nodes.begin(); - } - inline const_iterator cbegin() const noexcept - { - return nodes.cbegin(); - } - /// @} - - /** - * Returns an iterator to the node following the last node, in the traversal order specified by hyperoctree::order. - * - * @note Node identifiers cannot be modified through iterators. - */ - /// @{ - inline iterator end() noexcept - { - return nodes.end(); - } - inline const_iterator end() const noexcept - { - return nodes.end(); - } - inline const_iterator cend() const noexcept - { - return nodes.cend(); - } - /// @} - - /** - * Returns a reverse iterator to the first node of the revered hyperoctree, in the traversal order specified by hyperoctree::order. - * - * @note Node identifiers cannot be modified through iterators. - * @note If the hyperoctree is unordered, reverse iteration and forward iteration will be identical. - */ - /// @{ - inline reverse_iterator rbegin() noexcept - { - if constexpr (order != hyperoctree_order::unordered) - return nodes.rbegin(); - else - return nodes.begin(); - } - inline const_reverse_iterator rbegin() const noexcept - { - if constexpr (order != hyperoctree_order::unordered) - return nodes.rbegin(); - else - return nodes.begin(); - } - inline const_reverse_iterator crbegin() const noexcept - { - if constexpr (order != hyperoctree_order::unordered) - return nodes.crbegin(); - else - return nodes.cbegin(); - } - /// @} - - /** - * Returns a reverse iterator to the node following the last node of the reverse hyperoctree, in the traversal order specified by hyperoctree::order. - * - * @note Node identifiers cannot be modified through iterators. - * @note If the hyperoctree is unordered, reverse iteration and forward iteration will be identical. - */ - /// @{ - inline reverse_iterator rend() noexcept - { - if constexpr (order != hyperoctree_order::unordered) - return nodes.rend(); - else - return nodes.end(); - } - inline const_reverse_iterator rend() const noexcept - { - if constexpr (order != hyperoctree_order::unordered) - return nodes.rend(); - else - return nodes.end(); - } - inline const_reverse_iterator crend() const noexcept - { - if constexpr (order != hyperoctree_order::unordered) - return nodes.crend(); - else - return nodes.cend(); - } - /// @} - /// @} - - /// @name Capacity - /// @{ - /** - * Checks if the hyperoctree has no nodes. - * - * @return `true` if the hyperoctree is empty, `false` otherwise. - * - * @note This function should always return `false`, as the root node is persistent. - */ - inline bool empty() const noexcept - { - return nodes.empty(); - } - - /** - * Checks if the hyperoctree is full. - * - * @return `true` if the hyperoctree is full, `false` otherwise. - */ - inline bool full() const noexcept - { - return size() == max_size(); - } - - /** - * Returns the number of nodes in the hyperoctree. - * - * @return Number of nodes in the hyperoctree. - * - * @note Hyperoctree size will always be greater than or equal to one, as the root node is persistent. - */ - inline std::size_t size() const noexcept - { - return nodes.size(); - } - - /// Returns the total number of nodes the hyperoctree is capable of containing. - constexpr std::size_t max_size() const noexcept - { - return max_node_count; - } - /// @} - - /// @name Modifiers - /// @{ - /** - * Erases all nodes except the root node, which is persistent. - */ - inline void clear() - { - nodes = {root}; - } - - /** - * Inserts a node and its siblings into the hyperoctree, inserting ancestors as necessary. - * - * @param node Node to insert. - * - * @note The root node is persistent and does not need to be inserted. - */ - void insert(node_type node) - { - if (contains(node)) - return; - - // Insert node - nodes.emplace(node); - - // Insert node siblings - for (node_type i = 1; i < children_per_node; ++i) - nodes.emplace(sibling(node, i)); - - // Insert node ancestors - insert(parent(node)); - } - - /** - * Erases a node, along with its descendants, siblings, and descendants of siblings. - * - * @param node Identifier of the node to erase. - * - * @note The root node is persistent and cannot be erased. - */ - void erase(node_type node) - { - if (node == root || !contains(node)) - return; - - // Erase node and its descendants - nodes.erase(node); - erase(child(node, 0)); - - // Erase node siblings - for (node_type i = 0; i < siblings_per_node; ++i) - { - node = sibling(node, 1); - - // Erase sibling and its descendants - nodes.erase(node); - erase(child(node, 0)); - } - } - /// @} - - /// @name Lookup - /// @{ - /** - * Checks if a node is contained within the hyperoctree. - * - * @param node Identifier of the node to check for. - * - * @return `true` if the hyperoctree contains the node, `false` otherwise. - */ - inline bool contains(node_type node) const - { - return nodes.contains(node); - } - - /** - * Checks if a node has no children. - * - * @param node Node identififer. - * - * @return `true` if the node has no children, and `false` otherwise. - */ - inline bool is_leaf(node_type node) const - { - return !contains(child(node, 0)); - } - /// @} - -private: - container_type nodes; -}; - -/// Hyperoctree with unordered node storage and traversal. -template -using unordered_hyperoctree = hyperoctree; - -} // namespace geom - -#endif // ANTKEEPER_GEOM_HYPEROCTREE_HPP diff --git a/src/geom/intersection.cpp b/src/geom/intersection.cpp deleted file mode 100644 index 8cd7ef8..0000000 --- a/src/geom/intersection.cpp +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "intersection.hpp" -#include - -namespace geom { - -std::tuple ray_plane_intersection(const ray& ray, const plane& plane) -{ - float denom = math::dot(ray.direction, plane.normal); - if (denom != 0.0f) - { - float t = -(math::dot(ray.origin, plane.normal) + plane.distance) / denom; - - if (t >= 0.0f) - { - return std::make_tuple(true, t); - } - } - - return std::make_tuple(false, std::numeric_limits::infinity()); -} - -std::tuple ray_triangle_intersection(const ray& ray, const float3& a, const float3& b, const float3& c) -{ - // Find edges - float3 edge10 = b - a; - float3 edge20 = c - a; - - // Calculate determinant - float3 pv = math::cross(ray.direction, edge20); - float det = math::dot(edge10, pv); - - if (!det) - { - return std::make_tuple(false, std::numeric_limits::infinity(), 0.0f, 0.0f); - } - - float inverse_det = 1.0f / det; - - // Calculate u - float3 tv = ray.origin - a; - float u = math::dot(tv, pv) * inverse_det; - - if (u < 0.0f || u > 1.0f) - { - return std::make_tuple(false, std::numeric_limits::infinity(), 0.0f, 0.0f); - } - - // Calculate v - float3 qv = math::cross(tv, edge10); - float v = math::dot(ray.direction, qv) * inverse_det; - - if (v < 0.0f || u + v > 1.0f) - { - return std::make_tuple(false, std::numeric_limits::infinity(), 0.0f, 0.0f); - } - - // Calculate t - float t = math::dot(edge20, qv) * inverse_det; - - if (t > 0.0f) - { - return std::make_tuple(true, t, u, v); - } - - return std::make_tuple(false, std::numeric_limits::infinity(), 0.0f, 0.0f); -} - -std::tuple ray_aabb_intersection(const ray& ray, const aabb& aabb) -{ - float t0 = -std::numeric_limits::infinity(); - float t1 = std::numeric_limits::infinity(); - - for (std::size_t i = 0; i < 3; ++i) - { - if (ray.direction[i] == 0.0f) - { - if (ray.origin[i] < aabb.min_point[i] || ray.origin[i] > aabb.max_point[i]) - { - return std::make_tuple(false, std::numeric_limits::infinity(), std::numeric_limits::infinity()); - } - } - else - { - float tmin = (aabb.min_point[i] - ray.origin[i]) / ray.direction[i]; - float tmax = (aabb.max_point[i] - ray.origin[i]) / ray.direction[i]; - - t0 = std::max(t0, std::min(tmin, tmax)); - t1 = std::min(t1, std::max(tmin, tmax)); - } - } - - if (t0 > t1 || t1 < 0.0f) - { - return std::make_tuple(false, std::numeric_limits::infinity(), std::numeric_limits::infinity()); - } - - return std::make_tuple(true, t0, t1); -} - -std::tuple ray_mesh_intersection(const ray& ray, const mesh& mesh) -{ - const std::vector& triangles = mesh.get_faces(); - - bool intersection = false; - float t0 = std::numeric_limits::infinity(); - float t1 = -std::numeric_limits::infinity(); - std::size_t index0 = triangles.size(); - std::size_t index1 = triangles.size(); - - for (std::size_t i = 0; i < triangles.size(); ++i) - { - const mesh::face* triangle = triangles[i]; - - const float3& a = reinterpret_cast(triangle->edge->vertex->position); - const float3& b = reinterpret_cast(triangle->edge->next->vertex->position); - const float3& c = reinterpret_cast(triangle->edge->previous->vertex->position); - - auto result = ray_triangle_intersection(ray, a, b, c); - - if (std::get<0>(result)) - { - intersection = true; - float t = std::get<1>(result); - - if (t < t0) - { - t0 = t; - index0 = i; - } - - if (t > t1) - { - t1 = t; - index1 = i; - } - } - } - - return std::make_tuple(intersection, t0, t1, index0, index1); -} - -bool aabb_aabb_intersection(const aabb& a, const aabb& b) -{ - if (a.max_point.x() < b.min_point.x() || a.min_point.x() > b.max_point.x()) - return false; - if (a.max_point.y() < b.min_point.y() || a.min_point.y() > b.max_point.y()) - return false; - if (a.max_point.z() < b.min_point.z() || a.min_point.z() > b.max_point.z()) - return false; - return true; -} - -bool aabb_sphere_intersection(const aabb& aabb, const float3& center, float radius) -{ - float sqr_distance = 0.0f; - for (int i = 0; i < 3; ++i) - { - float v = center[i]; - if (v < aabb.min_point[i]) - sqr_distance += (aabb.min_point[i] - v) * (aabb.min_point[i] - v); - if (v > aabb.max_point[i]) - sqr_distance += (v - aabb.max_point[i]) * (v - aabb.max_point[i]); - } - - return (sqr_distance <= (radius * radius)); -} - -} // namespace geom diff --git a/src/geom/intersection.hpp b/src/geom/intersection.hpp deleted file mode 100644 index deffeeb..0000000 --- a/src/geom/intersection.hpp +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GEOM_INTERSECTION_HPP -#define ANTKEEPER_GEOM_INTERSECTION_HPP - -#include "geom/aabb.hpp" -#include "geom/mesh.hpp" -#include "geom/plane.hpp" -#include "geom/ray.hpp" -#include "geom/sphere.hpp" -#include "utility/fundamental-types.hpp" -#include - -namespace geom { - -/** - * Tests for intersection between a ray and a plane. - * - * @param ray Ray to test for intersection. - * @param plane Plane to test for intersection. - * @return Tuple containing a boolean indicating whether intersection occurred, and a float indicating the signed distance along the ray to the point of intersection. - */ -std::tuple ray_plane_intersection(const ray& ray, const plane& plane); - -std::tuple ray_triangle_intersection(const ray& ray, const float3& a, const float3& b, const float3& c); - -std::tuple ray_aabb_intersection(const ray& ray, const aabb& aabb); - -std::tuple ray_mesh_intersection(const ray& ray, const mesh& mesh); - -/** - * Ray-sphere intersection test. - */ -template -std::tuple ray_sphere_intersection(const ray& ray, const sphere& sphere) -{ - const auto d = ray.origin - sphere.center; - const T b = math::dot(d, ray.direction); - const T c = math::dot(d, d) - sphere.radius * sphere.radius; - T h = b * b - c; - - if (h < T(0)) - return {false, T(0), T(0)}; - - h = std::sqrt(h); - - return {true, -b - h, -b + h}; -} - -bool aabb_sphere_intersection(const aabb& aabb, const float3& center, float radius); - -bool aabb_aabb_intersection(const aabb& a, const aabb& b); - -} // namespace geom - -#endif // ANTKEEPER_GEOM_INTERSECTION_HPP - diff --git a/src/geom/marching-cubes.cpp b/src/geom/marching-cubes.cpp deleted file mode 100644 index 4956f26..0000000 --- a/src/geom/marching-cubes.cpp +++ /dev/null @@ -1,418 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "marching-cubes.hpp" -#include - -namespace geom { -namespace mc { - -static constexpr std::uint_fast8_t vertex_table[12][2] = -{ - {0, 1}, - {1, 2}, - {2, 3}, - {3, 0}, - {4, 5}, - {5, 6}, - {6, 7}, - {7, 4}, - {0, 4}, - {1, 5}, - {2, 6}, - {3, 7} -}; - -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 -}; - -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}, - {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1} -}; - -void polygonize(float* vertices, std::uint_fast8_t* vertex_count, std::int_fast8_t* triangles, std::uint_fast8_t* triangle_count, const float* corners, const float* distances) -{ - *vertex_count = 0; - *triangle_count = 0; - - // Calculate signed distances for each cube corner and form an edge table index. - std::uint_fast8_t edge_index = 0; - for (std::uint_fast8_t i = 0; i < 8; ++i) - edge_index |= (distances[i] < 0.0f) ? (1 << i) : 0; - - // Get edge flags from edge table - const std::uint_fast16_t edge_flags = edge_table[edge_index]; - if (!edge_flags) - return; - - // Get vertex indices of the case - const std::int_fast8_t* indices = triangle_table[edge_index]; - - // Calculate vertices and store in vertex buffer - float vertex_buffer[12 * 3]; - for (std::uint_fast16_t i = 0; i < 12; ++i) - { - // If this edge is intersected - if (edge_flags & (1 << i)) - { - // Find the two vertices which make up edge ab - std::uint_fast8_t a = vertex_table[i][0]; - std::uint_fast8_t b = vertex_table[i][1]; - const float* v_a = corners + a * 3; - const float* v_b = corners + b * 3; - float f_a = distances[a]; - float f_b = distances[b]; - - // Determine interpolation ratio - static const float epsilon = 0.00001f; - float t; - if (std::fabs(f_a) < epsilon) - t = 1.0f; - else if (std::fabs(f_b) < epsilon) - t = 0.0f; - else if (std::fabs(f_b - f_a) < epsilon) - t = 0.5f; // Paul Bourke suggested this be 1.0f? Why? - else - t = (-f_a) / (f_b - f_a); - - // Interpolate between vertices - float* v = vertex_buffer + i * 3; - v[0] = v_a[0] + t * (v_b[0] - v_a[0]); - v[1] = v_a[1] + t * (v_b[1] - v_a[1]); - v[2] = v_a[2] + t * (v_b[2] - v_a[2]); - } - } - - // Remap vertex buffer to be stored contiguously - std::int_fast8_t vertex_remap[12]; - for (std::uint_fast8_t i = 0; i < 12; ++i) - vertex_remap[i] = -1; - for (std::uint_fast8_t i = 0; indices[i] != -1; ++i) - { - if (vertex_remap[indices[i]] == -1) - { - std::int_fast8_t index = indices[i] * 3; - *(vertices++) = vertex_buffer[ index]; - *(vertices++) = vertex_buffer[++index]; - *(vertices++) = vertex_buffer[++index]; - vertex_remap[indices[i]] = (*vertex_count)++; - } - } - - // Form triangles - for (std::uint_fast8_t i = 0; indices[i] != -1;) - { - *(triangles++) = vertex_remap[indices[i++]]; - *(triangles++) = vertex_remap[indices[i++]]; - *(triangles++) = vertex_remap[indices[i++]]; - ++(*triangle_count); - } -} - -} // namespace mc -} // namespace geom diff --git a/src/geom/mesh-accelerator.cpp b/src/geom/mesh-accelerator.cpp deleted file mode 100644 index 919583f..0000000 --- a/src/geom/mesh-accelerator.cpp +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "geom/mesh-accelerator.hpp" -#include "geom/mesh-functions.hpp" -#include "geom/morton.hpp" -#include - -namespace geom { - -mesh_accelerator::mesh_accelerator() -{} - -void mesh_accelerator::build(const mesh& mesh) -{ - // Clear octree and face map - octree.clear(); - face_map.clear(); - - // Calculate mesh dimensions - aabb bounds = calculate_bounds(mesh); - float3 mesh_dimensions = bounds.max_point - bounds.min_point; - center_offset = mesh_dimensions * 0.5f - (bounds.min_point + bounds.max_point) * 0.5f; - - // Calculate node dimensions at each octree depth - for (auto i = 0; i <= octree_type::max_depth; ++i) - { - node_dimensions[i] = mesh_dimensions * static_cast((1.0f / std::pow(2, i))); - } - - // Add faces to octree - for (mesh::face* face: mesh.get_faces()) - { - // Calculate face bounds - float3 min_point = reinterpret_cast(face->edge->vertex->position); - float3 max_point = min_point; - mesh::edge* edge = face->edge; - do - { - const auto& position = edge->vertex->position; - for (int i = 0; i < 3; ++i) - { - min_point[i] = std::min(min_point[i], position[i]); - max_point[i] = std::max(max_point[i], position[i]); - } - - edge = edge->next; - } - while (edge != face->edge); - - // 1. Find max depth node of aabb min - // 2. Find max depth node of aabb max - // 3. Find common ancestor of the two nodes--that's the containing node. - typename octree_type::node_type min_node = find_node(min_point); - typename octree_type::node_type max_node = find_node(max_point); - typename octree_type::node_type containing_node = octree_type::common_ancestor(min_node, max_node); - - // Insert containing node into octree - octree.insert(containing_node); - - // Add face to face map - face_map[containing_node].push_back(face); - } -} - -std::optional mesh_accelerator::query_nearest(const ray& ray) const -{ - ray_query_result result; - result.t = std::numeric_limits::infinity(); - result.face = nullptr; - - query_nearest_recursive(result.t, result.face, octree.root, ray); - - if (result.face) - return std::optional{result}; - return std::nullopt; -} - -void mesh_accelerator::query_nearest_recursive(float& nearest_t, geom::mesh::face*& nearest_face, typename octree_type::node_type node, const ray& ray) const -{ - // Get node bounds - const aabb node_bounds = get_node_bounds(node); - - // Test for intersection with node bounds - auto aabb_intersection = ray_aabb_intersection(ray, node_bounds); - - // If ray passed through this node - if (std::get<0>(aabb_intersection)) - { - // Test all triangles in the node - if (auto it = face_map.find(node); it != face_map.end()) - { - const std::list& faces = it->second; - - for (mesh::face* face: faces) - { - // Get triangle coordinates - const float3& a = reinterpret_cast(face->edge->vertex->position); - const float3& b = reinterpret_cast(face->edge->next->vertex->position); - const float3& c = reinterpret_cast(face->edge->previous->vertex->position); - - // Test for intersection with triangle - auto triangle_intersection = ray_triangle_intersection(ray, a, b, c); - if (std::get<0>(triangle_intersection)) - { - float t = std::get<1>(triangle_intersection); - if (t < nearest_t) - { - nearest_t = t; - nearest_face = face; - } - } - } - } - - // Test all child nodes - if (!octree.is_leaf(node)) - { - for (int i = 0; i < 8; ++i) - query_nearest_recursive(nearest_t, nearest_face, octree.child(node, i), ray); - } - } -} - -aabb mesh_accelerator::get_node_bounds(typename octree_type::node_type node) const -{ - // Decode Morton location of node - std::uint32_t x, y, z; - morton::decode(octree_type::location(node), x, y, z); - float3 node_location = float3{static_cast(x), static_cast(y), static_cast(z)}; - - // Get node dimensions at node depth - const float3& dimensions = node_dimensions[octree_type::depth(node)]; - - // Calculate AABB - float3 min_point = (node_location * dimensions) - center_offset; - return aabb{min_point, min_point + dimensions}; -} - -typename mesh_accelerator::octree_type::node_type mesh_accelerator::find_node(const float3& point) const -{ - // Transform point to octree space - float3 transformed_point = (point + center_offset); - - // Account for floating-point tolerance - const float epsilon = 0.00001f; - transformed_point.x() = std::max(0.0f, std::min(node_dimensions[0].x() - epsilon, transformed_point.x())); - transformed_point.y() = std::max(0.0f, std::min(node_dimensions[0].y() - epsilon, transformed_point.y())); - transformed_point.z() = std::max(0.0f, std::min(node_dimensions[0].z() - epsilon, transformed_point.z())); - - // Transform point to max-depth node space - transformed_point = transformed_point / node_dimensions[octree_type::max_depth]; - - // Encode transformed point as a Morton location code - std::uint32_t location = morton::encode( - static_cast(transformed_point.x()), - static_cast(transformed_point.y()), - static_cast(transformed_point.z())); - - // Return max depth node at the determined location - return octree_type::node(octree_type::max_depth, location); -} - -} // namespace geom diff --git a/src/geom/mesh-accelerator.hpp b/src/geom/mesh-accelerator.hpp deleted file mode 100644 index 8c8c1b9..0000000 --- a/src/geom/mesh-accelerator.hpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GEOM_MESH_ACCELERATOR_HPP -#define ANTKEEPER_GEOM_MESH_ACCELERATOR_HPP - -#include "geom/mesh.hpp" -#include "geom/octree.hpp" -#include "geom/aabb.hpp" -#include "geom/intersection.hpp" -#include "utility/fundamental-types.hpp" -#include -#include -#include - -namespace geom { - -/** - * Acceleration structure for querying mesh geometry. - */ -class mesh_accelerator -{ -public: - struct ray_query_result - { - float t; - geom::mesh::face* face; - }; - - mesh_accelerator(); - - /** - * Builds the acceleration structure. - */ - void build(const mesh& mesh); - - /** - * 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 occurred. - */ - std::optional query_nearest(const ray& ray) const; - -private: - typedef unordered_octree32 octree_type; - - aabb get_node_bounds(typename octree_type::node_type node) const; - - void query_nearest_recursive(float& nearest_t, geom::mesh::face*& nearest_face, typename octree_type::node_type node, const ray& ray) const; - - /// Returns the max-depth node in which the point is located - typename octree_type::node_type find_node(const float3& point) const; - - octree_type octree; - float3 node_dimensions[octree_type::max_depth + 1]; - float3 center_offset; - std::unordered_map> face_map; -}; - -} // namespace geom - -#endif // ANTKEEPER_GEOM_MESH_ACCELERATOR_HPP - diff --git a/src/geom/mesh-functions.cpp b/src/geom/mesh-functions.cpp deleted file mode 100644 index 60389b5..0000000 --- a/src/geom/mesh-functions.cpp +++ /dev/null @@ -1,235 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "mesh-functions.hpp" -#include "utility/fundamental-types.hpp" -#include - -namespace geom { - -struct edge_hasher -{ - std::size_t operator()(const std::array& v) const noexcept - { - std::size_t hash = std::hash()(v[0]); - return hash ^ (std::hash()(v[1]) + 0x9e3779b9 + (hash << 6) + (hash >> 2)); - } -}; - -void create_triangle_mesh(mesh& mesh, const std::vector& vertices, const std::vector>& triangles) -{ - for (const auto& vertex: vertices) - mesh.add_vertex(vertex); - - std::unordered_map, geom::mesh::edge*, edge_hasher> edge_map; - const std::vector& mesh_vertices = mesh.get_vertices(); - std::vector loop(3); - - for (const auto& triangle: triangles) - { - geom::mesh::vertex* triangle_vertices[3] = - { - mesh_vertices[triangle[0]], - mesh_vertices[triangle[1]], - mesh_vertices[triangle[2]] - }; - - for (int j = 0; j < 3; ++j) - { - geom::mesh::vertex* start = triangle_vertices[j]; - geom::mesh::vertex* end = triangle_vertices[(j + 1) % 3]; - - if (auto it = edge_map.find({start->index, end->index}); it != edge_map.end()) - { - loop[j] = it->second; - } - else - { - loop[j] = mesh.add_edge(start, end); - edge_map[{start->index, end->index}] = loop[j]; - edge_map[{end->index, start->index}] = loop[j]->symmetric; - } - - } - - mesh.add_face(loop); - } -} - -void calculate_face_normals(float3* normals, const mesh& mesh) -{ - const std::vector& faces = mesh.get_faces(); - - for (std::size_t i = 0; i < faces.size(); ++i) - { - const mesh::face& face = *(faces[i]); - const float3& a = face.edge->vertex->position; - const float3& b = face.edge->next->vertex->position; - const float3& c = face.edge->previous->vertex->position; - - normals[i] = math::normalize(math::cross(b - a, c - a)); - } -} - -float3 calculate_face_normal(const mesh::face& face) -{ - const float3& a = face.edge->vertex->position; - const float3& b = face.edge->next->vertex->position; - const float3& c = face.edge->previous->vertex->position; - return math::normalize(math::cross(b - a, c - a)); -} - -void calculate_vertex_tangents(float4* tangents, const float2* texcoords, const float3* normals, const mesh& mesh) -{ - const std::vector& faces = mesh.get_faces(); - const std::vector& vertices = mesh.get_vertices(); - - // Allocate tangent and bitangent buffers - float3* tangent_buffer = new float3[vertices.size()]; - float3* bitangent_buffer = new float3[vertices.size()]; - for (std::size_t i = 0; i < vertices.size(); ++i) - { - tangent_buffer[i] = {0.0f, 0.0f, 0.0f}; - bitangent_buffer[i] = {0.0f, 0.0f, 0.0f}; - } - - // Accumulate tangents and bitangents - for (std::size_t i = 0; i < faces.size(); ++i) - { - const mesh::face& face = *(faces[i]); - std::size_t ia = face.edge->vertex->index; - std::size_t ib = face.edge->next->vertex->index; - std::size_t ic = face.edge->previous->vertex->index; - const float3& a = vertices[ia]->position; - const float3& b = vertices[ib]->position; - const float3& c = vertices[ic]->position; - const float2& uva = texcoords[ia]; - const float2& uvb = texcoords[ib]; - const float2& uvc = texcoords[ic]; - - float3 ba = b - a; - float3 ca = c - a; - float2 uvba = uvb - uva; - float2 uvca = uvc - uva; - - float f = 1.0f / (uvba.x() * uvca.y() - uvca.x() * uvba.y()); - float3 tangent = (ba * uvca.y() - ca * uvba.y()) * f; - float3 bitangent = (ba * -uvca.x() + ca * uvba.x()) * f; - - tangent_buffer[ia] += tangent; - tangent_buffer[ib] += tangent; - tangent_buffer[ic] += tangent; - bitangent_buffer[ia] += bitangent; - bitangent_buffer[ib] += bitangent; - bitangent_buffer[ic] += bitangent; - } - - // Orthogonalize tangents - for (std::size_t i = 0; i < vertices.size(); ++i) - { - const float3& n = normals[i]; - const float3& t = tangent_buffer[i]; - const float3& b = bitangent_buffer[i]; - - // Gram-Schmidt orthogonalize tangent - float3 tangent = math::normalize(t - n * math::dot(n, t)); - - // Calculate bitangent sign - float bitangent_sign = (math::dot(math::cross(n, t), b) < 0.0f) ? -1.0f : 1.0f; - - tangents[i] = {tangent.x(), tangent.y(), tangent.z(), bitangent_sign}; - } - - // Free faceted tangents and bitangents - delete[] tangent_buffer; - delete[] bitangent_buffer; -} - -aabb calculate_bounds(const mesh& mesh) -{ - float3 bounds_min; - float3 bounds_max; - for (int i = 0; i < 3; ++i) - { - bounds_min[i] = std::numeric_limits::infinity(); - bounds_max[i] = -std::numeric_limits::infinity(); - } - - for (const mesh::vertex* vertex: mesh.get_vertices()) - { - const auto& position = vertex->position; - for (int i = 0; i < 3; ++i) - { - bounds_min[i] = std::min(bounds_min[i], position[i]); - bounds_max[i] = std::max(bounds_max[i], position[i]); - } - } - - return aabb{bounds_min, bounds_max}; -} - -mesh::vertex* poke_face(mesh& mesh, std::size_t index) -{ - mesh::face* face = mesh.get_faces()[index]; - - // Collect face edges and sum edge vertex positions - mesh::loop loop = {face->edge}; - float3 sum_positions = face->edge->vertex->position; - for (mesh::edge* edge = face->edge->next; edge != face->edge; edge = edge->next) - { - loop.push_back(edge); - sum_positions += edge->vertex->position; - } - - if (loop.size() <= 2) - return nullptr; - - // Remove face - mesh.remove_face(face); - - // Add vertex in center - mesh::vertex* center = mesh.add_vertex(sum_positions / static_cast(loop.size())); - - // Create first triangle - geom::mesh::edge* ab = loop[0]; - geom::mesh::edge* bc = mesh.add_edge(ab->next->vertex, center); - geom::mesh::edge* ca = mesh.add_edge(center, ab->vertex); - mesh.add_face({ab, bc, ca}); - - // Save first triangle CA edge - geom::mesh::edge* first_triangle_ca = ca; - - // Create remaining triangles - for (std::size_t i = 1; i < loop.size(); ++i) - { - ab = loop[i]; - ca = bc->symmetric; - - if (i == loop.size() - 1) - bc = first_triangle_ca->symmetric; - else - bc = mesh.add_edge(ab->next->vertex, center); - - mesh.add_face({ab, bc, ca}); - } - - return center; -} - -} // namespace geom diff --git a/src/geom/mesh-functions.hpp b/src/geom/mesh-functions.hpp deleted file mode 100644 index fdad24c..0000000 --- a/src/geom/mesh-functions.hpp +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GEOM_MESH_FUNCTIONS_HPP -#define ANTKEEPER_GEOM_MESH_FUNCTIONS_HPP - -#include "geom/aabb.hpp" -#include "geom/mesh.hpp" -#include "utility/fundamental-types.hpp" -#include -#include - -namespace geom { - -/** - * Creates a triangle mesh from a list of vertices and indices. - * - * @param[out] mesh Mesh to which vertices, edges, and faces will be added. - * @param vertices Vertex positions - * @param triangles Triangle indices - * @return Newly created triangle mesh. - */ -void create_triangle_mesh(mesh& mesh, const std::vector& vertices, const std::vector>& triangles); - -/** - * Calculates normals for each face. - * - * @param[out] Array in which faceted normals will be stored. - */ -void calculate_face_normals(float3* normals, const mesh& mesh); - -float3 calculate_face_normal(const mesh::face& face); - -/** - * Calculates smooth tangents per-vertex. - * - * @param[out] tangents Array in which vertex tangents will be stored. A bitangent sign is stored in each tangent vector's fourth component. - * @param[in] texcoords Array containing vertex texture coordinates. - * @param[in] normals Array containing vertex normals. - */ -void calculate_vertex_tangents(float4* tangents, const float2* texcoords, const float3* normals, const mesh& mesh); - -/** - * Calculates the AABB bounds of a mesh. - */ -aabb calculate_bounds(const mesh& mesh); - -/** - * Triangulates a face by adding a new vertex in the center, then creating triangles between the edges of the original face and the new vertex. - * - * @param mesh Mesh containing the face to poke. - * @param index Index of the face to poke. - * @return Pointer to the newly-created vertex in the center of the face, or `nullptr` if the face could not be poked. - */ -mesh::vertex* poke_face(mesh& mesh, std::size_t index); - -} // namespace geom - -#endif // ANTKEEPER_GEOM_MESH_FUNCTIONS_HPP - diff --git a/src/geom/mesh.cpp b/src/geom/mesh.cpp deleted file mode 100644 index b335c0e..0000000 --- a/src/geom/mesh.cpp +++ /dev/null @@ -1,436 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "geom/mesh.hpp" -#include - -namespace geom { - -mesh::mesh(const mesh& other) -{ - *this = other; -} - -mesh::~mesh() -{ - clear(); -} - -mesh& mesh::operator=(const mesh& other) -{ - // Clear the mesh - clear(); - - // Resize vertices, edges, and faces - vertices.resize(other.vertices.size()); - edges.resize(other.edges.size()); - faces.resize(other.faces.size()); - - // Allocate vertices - for (std::size_t i = 0; i < vertices.size(); ++i) - vertices[i] = new vertex(); - - // Allocate edges - for (std::size_t i = 0; i < edges.size(); ++i) - { - edges[i] = new edge(); - edges[i]->symmetric = new edge(); - edges[i]->symmetric->symmetric = edges[i]; - } - - // Allocate faces - for (std::size_t i = 0; i < faces.size(); ++i) - faces[i] = new face(); - - // Copy vertices - for (std::size_t i = 0; i < vertices.size(); ++i) - { - vertex* va = vertices[i]; - const vertex* vb = other.vertices[i]; - - va->index = vb->index; - va->position = vb->position; - va->edge = nullptr; - - if (vb->edge) - { - va->edge = edges[vb->edge->index]; - if (vb->edge != other.edges[vb->edge->index]) - va->edge = va->edge->symmetric; - } - } - - // Copy edges - for (std::size_t i = 0; i < edges.size(); ++i) - { - edge* ea = edges[i]; - const edge* eb = other.edges[i]; - - for (std::size_t j = 0; j < 2; ++j) - { - ea->index = eb->index; - ea->vertex = vertices[eb->vertex->index]; - - ea->face = nullptr; - if (eb->face) - ea->face = faces[eb->face->index]; - - ea->previous = edges[eb->previous->index]; - if (eb->previous != other.edges[eb->previous->index]) - ea->previous = ea->previous->symmetric; - - ea->next = edges[eb->next->index]; - if (eb->next != other.edges[eb->next->index]) - ea->next = ea->next->symmetric; - - ea = ea->symmetric; - eb = eb->symmetric; - } - } - - // Copy faces - for (std::size_t i = 0; i < faces.size(); ++i) - { - face* fa = faces[i]; - const face* fb = other.faces[i]; - - fa->index = fb->index; - - fa->edge = edges[fb->edge->index]; - if (fb->edge != other.edges[fb->edge->index]) - fa->edge = fa->edge->symmetric; - } - - return *this; -} - -void mesh::clear() noexcept -{ - // Deallocate vertices - for (mesh::vertex* vertex: vertices) - delete vertex; - - // Deallocate edges - for (mesh::edge* edge: edges) - { - delete edge->symmetric; - delete edge; - } - - // Deallocate faces - for (mesh::face* face: faces) - delete face; - - vertices.clear(); - edges.clear(); - faces.clear(); -} - -mesh::vertex* mesh::add_vertex(const float3& position) -{ - mesh::vertex* vertex = new mesh::vertex - { - vertices.size(), - nullptr, - position - }; - - vertices.push_back(vertex); - - return vertex; -} - -mesh::edge* mesh::add_edge(mesh::vertex* a, mesh::vertex* b) -{ - mesh::edge* ab = new mesh::edge(); - mesh::edge* ba = new mesh::edge(); - - ab->index = edges.size(); - ab->vertex = a; - ab->face = nullptr; - ab->previous = ba; - ab->next = ba; - ab->symmetric = ba; - - ba->index = edges.size(); - ba->vertex = b; - ba->face = nullptr; - ba->previous = ab; - ba->next = ab; - ba->symmetric = ab; - - if (!a->edge) - { - a->edge = ab; - } - else - { - mesh::edge* a_in = find_free_incident(a); - mesh::edge* a_out = a_in->next; - a_in->next = ab; - ab->previous = a_in; - ba->next = a_out; - a_out->previous = ba; - } - - if (!b->edge) - { - b->edge = ba; - } - else - { - mesh::edge* b_in = find_free_incident(b); - mesh::edge* b_out = b_in->next; - b_in->next = ba; - ba->previous = b_in; - ab->next = b_out; - b_out->previous = ab; - } - - // Add edge - edges.push_back(ab); - - return ab; -} - -mesh::face* mesh::add_face(const loop& loop) -{ - if (loop.empty()) - { - throw std::runtime_error("Empty edge loop"); - } - - // Validate edge loop - for (std::size_t i = 0; i < loop.size(); ++i) - { - mesh::edge* current = loop[i]; - mesh::edge* next = loop[(i + 1) % loop.size()]; - - if (current->symmetric->vertex != next->vertex) - { - // Disconnected edge loop - throw std::runtime_error("Disconnected edge loop"); - } - - if (current->face) - { - // This edge already has a face - throw std::runtime_error("Non-manifold mesh 1"); - } - } - - // Make edges adjacent - for (std::size_t i = 0; i < loop.size(); ++i) - { - if (!make_adjacent(loop[i], loop[(i + 1) % loop.size()])) - { - throw std::runtime_error("Non-manifold mesh 2"); - } - } - - // Create face - mesh::face* face = new mesh::face(); - face->edge = loop[0]; - face->index = faces.size(); - - // Add face - faces.push_back(face); - - // Connect edges to the face - for (mesh::edge* edge: loop) - { - edge->face = face; - } - - return face; -} - -void mesh::remove_face(mesh::face* face) -{ - // Nullify pointers to this face - mesh::edge* edge = face->edge; - do - { - edge->face = nullptr; - edge = edge->next; - } - while (edge != face->edge); - - // Adjust indices of faces after this face - for (std::size_t i = face->index + 1; i < faces.size(); ++i) - { - --faces[i]->index; - } - - // Remove face from the faces vector - faces.erase(faces.begin() + face->index); - - // Deallocate face - delete face; -} - -void mesh::remove_edge(mesh::edge* edge) -{ - mesh::edge* ab = edge; - mesh::edge* ba = edge->symmetric; - mesh::vertex* a = ab->vertex; - mesh::edge* a_in = ab->previous; - mesh::edge* a_out = ba->next; - mesh::vertex* b = ba->vertex; - mesh::edge* b_in = ba->previous; - mesh::edge* b_out = ab->next; - - // Remove dependent faces - if (ab->face) - remove_face(ab->face); - if (ba->face) - remove_face(ba->face); - - // Re-link edges - if (a->edge == ab) - a->edge = (a_out == ab) ? nullptr : a_out; - if (b->edge == ba) - b->edge = (b_out == ba) ? nullptr : b_out; - a_in->next = a_out; - a_out->previous = a_in; - b_in->next = b_out; - b_out->previous = b_in; - - // Adjust indices of edges after this edge - for (std::size_t i = edge->index + 1; i < edges.size(); ++i) - { - --edges[i]->index; - --edges[i]->symmetric->index; - } - - // Remove edge from the edges vector - edges.erase(edges.begin() + edge->index); - - // Deallocate edge - delete edge->symmetric; - delete edge; -} - -void mesh::remove_vertex(mesh::vertex* vertex) -{ - // Remove connected edges - if (vertex->edge) - { - mesh::edge* current = nullptr; - mesh::edge* next = vertex->edge; - - do - { - current = next; - - next = next->symmetric->next; - if (next == current) - { - next = next->symmetric->next; - } - - remove_edge(current); - } - while (current != next); - } - - // Adjust indices of vertices after this vertex - for (std::size_t i = vertex->index + 1; i < vertices.size(); ++i) - { - --vertices[i]->index; - } - - // Remove vertex from the vertices vector - vertices.erase(vertices.begin() + vertex->index); - - // Deallocate vertex - delete vertex; -} - -mesh::edge* mesh::find_free_incident(mesh::vertex* vertex) const -{ - mesh::edge* begin = vertex->edge->symmetric; - mesh::edge* current = begin; - - do - { - if (!current->face) - { - return current; - } - - current = current->next->symmetric; - } - while (current != begin); - - return nullptr; -} - -mesh::edge* mesh::find_free_incident(mesh::edge* start_edge, mesh::edge* end_edge) const -{ - if (start_edge == end_edge) - { - return nullptr; - } - - mesh::edge* current = start_edge; - do - { - if (!current->face) - { - return current; - } - - current = current->next->symmetric; - } - while (current != end_edge); - - return nullptr; -} - -bool mesh::make_adjacent(mesh::edge* in, mesh::edge* out) -{ - if (in->next == out) - { - return true; - } - - mesh::edge* b = in->next; - mesh::edge* d = out->previous; - mesh::edge* g = find_free_incident(out->symmetric, in); - if (!g) - { - return false; - } - - mesh::edge* h = g->next; - - in->next = out; - out->previous = in; - - g->next = b; - b->previous = g; - - d->next = h; - h->previous = d; - - return true; -} - -} // namespace geom diff --git a/src/geom/mesh.hpp b/src/geom/mesh.hpp deleted file mode 100644 index b9d2b6d..0000000 --- a/src/geom/mesh.hpp +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GEOM_MESH_HPP -#define ANTKEEPER_GEOM_MESH_HPP - -#include "utility/fundamental-types.hpp" -#include - -namespace geom { - -/** - * Half-edge mesh. - * - * @see http://kaba.hilvi.org/homepage/blog/halfedge/halfedge.htm - */ -class mesh -{ -public: - struct vertex; - struct edge; - struct face; - - /** - * Half-edge mesh vertex, containing its index, a pointer to its parent edge, and its position vector. - */ - struct vertex - { - /// Index of this vertex. - std::size_t index; - - /// Pointer to the edge to which this vertex belongs. - mesh::edge* edge; - - /// Vertex position. - float3 position; - }; - - /** - * Half-edge mesh edge, containing its index and pointers to its starting vertex, parent face, and related edges. - */ - struct edge - { - /// Index of this edge. - std::size_t index; - - /// Pointer to the vertex at which the edge starts. - mesh::vertex* vertex; - - /// Pointer to the face on the left of this edge. - mesh::face* face; - - /// Pointer to the previous edge in the parent face. - mesh::edge* previous; - - /// Pointer to the next edge in the parent face. - mesh::edge* next; - - /// Pointer to the symmetric edge. - mesh::edge* symmetric; - }; - - /** - * Half-edge mesh face, containing its index and a pointer to its first edge. - */ - struct face - { - /// Index of this face. - std::size_t index; - - /// Pointer to the first edge in this face. - mesh::edge* edge; - }; - - /** - * List of edges which form a face. - */ - typedef std::vector loop; - - /** - * Constructs a mesh. - */ - constexpr mesh() noexcept = default; - - /** - * Copy-constructs a mesh. - */ - mesh(const mesh& other); - - /** - * Destructs a mesh. - */ - ~mesh() noexcept; - - /** - * Copies another mesh into this mesh. - * - * @param other Mesh to copy. - * - * @return Reference to this mesh. - */ - mesh& operator=(const mesh& other); - - /** - * Removes all vertices, edges, and faces from the mesh. - */ - void clear() noexcept; - - /** - * Adds a vertex to the mesh. - * - * @param position Position of the vertex. - * - * @return Pointer to the added vertex. - * - * @warning The added vertex will initially have a null edge. - */ - mesh::vertex* add_vertex(const float3& position); - - /** - * Adds two half edges to the mesh. - * - * @param a Vertex from which the edge originates. - * @param b Vertex at which the edge ends. - * - * @return Pointer to the half edge `ab`. The symmetric half edge `ba` can be accessed through `ab->symmetric`. - */ - mesh::edge* add_edge(mesh::vertex* a, mesh::vertex* b); - - /** - * Adds a face to the mesh. - * - * @param loop List of edges which form the face. - * @return Pointer to the added face. - * - * @exception std::runtime_error Empty edge loop. - * @exception std::runtime_error Disconnected edge loop. - * @exception std::runtime_error Non-manifold mesh 1. - * @exception std::runtime_error Non-manifold mesh 2. - */ - mesh::face* add_face(const loop& loop); - - /** - * Removes a face from the mesh. - * - * @param face Face to be removed. This face will be deallocated after removal. - */ - void remove_face(mesh::face* face); - - /** - * Removes an edge and all dependent faces from the mesh. - * - * @param edge Edge to be removed. This edge will be deallocated after removal. - */ - void remove_edge(mesh::edge* edge); - - /** - * Removes a vertex, all dependent edges, and all dependent faces from the mesh. - * - * @param vertex Vertex to be removed. This vertex will be deallocated after removal. - */ - void remove_vertex(mesh::vertex* vertex); - - /// Returns the mesh vertices. - const std::vector& get_vertices() const; - - /// Returns the mesh edges. - const std::vector& get_edges() const; - - /// Returns the mesh faces. - const std::vector& get_faces() const; - -private: - mesh::edge* find_free_incident(mesh::vertex* vertex) const; - mesh::edge* find_free_incident(mesh::edge* start_edge, mesh::edge* end_edge) const; - bool make_adjacent(mesh::edge* in_edge, mesh::edge* out_edge); - - std::vector vertices; - std::vector edges; - std::vector faces; -}; - -inline const std::vector& mesh::get_vertices() const -{ - return vertices; -} - -inline const std::vector& mesh::get_edges() const -{ - return edges; -} - -inline const std::vector& mesh::get_faces() const -{ - return faces; -} - -} // namespace geom - -#endif // ANTKEEPER_GEOM_MESH_HPP diff --git a/src/geom/meshes/grid.cpp b/src/geom/meshes/grid.cpp deleted file mode 100644 index e2b27bd..0000000 --- a/src/geom/meshes/grid.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "geom/meshes/grid.hpp" -#include -#include -#include - -namespace geom { -namespace meshes { - -geom::mesh* grid_xy(float length, std::size_t subdivisions_x, std::size_t subdivisions_y) -{ - // Allocate new mesh - geom::mesh* mesh = new geom::mesh(); - - // Determine vertex count and placement - std::size_t columns = subdivisions_x + 1; - std::size_t rows = subdivisions_y + 1; - float column_length = length / static_cast(columns); - float row_length = length / static_cast(rows); - - // Generate mesh vertices - float3 position; - position.z() = 0.0f; - position.y() = -length * 0.5f; - for (std::size_t i = 0; i <= rows; ++i) - { - position.x() = -length * 0.5f; - for (std::size_t j = 0; j <= columns; ++j) - { - mesh->add_vertex(position); - - position.x() += column_length; - } - - position.y() += row_length; - } - - // Function to eliminate duplicate edges - std::map, geom::mesh::edge*> edge_map; - auto add_or_find_edge = [&](geom::mesh::vertex* start, geom::mesh::vertex* end) -> geom::mesh::edge* - { - geom::mesh::edge* edge; - if (auto it = edge_map.find({start->index, end->index}); it != edge_map.end()) - { - edge = it->second; - } - else - { - edge = mesh->add_edge(start, end); - edge_map[{start->index, end->index}] = edge; - edge_map[{end->index, start->index}] = edge->symmetric; - } - - return edge; - }; - - // Connect vertices with edges and faces - const std::vector& vertices = mesh->get_vertices(); - for (std::size_t i = 0; i < rows; ++i) - { - for (std::size_t j = 0; j < columns; ++j) - { - geom::mesh::vertex* a = vertices[i * (columns + 1) + j]; - geom::mesh::vertex* b = vertices[(i + 1) * (columns + 1) + j]; - geom::mesh::vertex* c = vertices[i * (columns + 1) + j + 1]; - geom::mesh::vertex* d = vertices[(i + 1) * (columns + 1) + j + 1]; - - // a---c - // | | - // b---d - geom::mesh::edge* ab = add_or_find_edge(a, b); - geom::mesh::edge* bd = add_or_find_edge(b, d); - geom::mesh::edge* dc = add_or_find_edge(d, c); - geom::mesh::edge* ca = add_or_find_edge(c, a); - - mesh->add_face({ab, bd, dc, ca}); - } - } - - return mesh; -} - -} // namespace meshes -} // namespace geom diff --git a/src/geom/meshes/grid.hpp b/src/geom/meshes/grid.hpp deleted file mode 100644 index 358f6c9..0000000 --- a/src/geom/meshes/grid.hpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GEOM_MESHES_GRID_HPP -#define ANTKEEPER_GEOM_MESHES_GRID_HPP - -#include "geom/mesh.hpp" - -namespace geom { -namespace meshes { - -/** - * Generates a grid mesh on the XY plane. - * - * @param length Side length of the grid. - * @param subdivisions_x Number of subdivisions on the x-axis. - * @param subdivisions_y Number of subdivisions on the y-axis. - * @return Grid mesh on the XY plane. - */ -geom::mesh* grid_xy(float length, std::size_t subdivisions_x, std::size_t subdivisions_y); - -} // namespace meshes -} // namespace geom - -#endif // ANTKEEPER_GEOM_MESHES_GRID_HPP diff --git a/src/geom/octree.hpp b/src/geom/octree.hpp deleted file mode 100644 index c2c4d05..0000000 --- a/src/geom/octree.hpp +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GEOM_OCTREE_HPP -#define ANTKEEPER_GEOM_OCTREE_HPP - -#include "geom/hyperoctree.hpp" -#include - -namespace geom { - -/// An octree, or 3-dimensional hyperoctree. -template -using octree = hyperoctree; - -/// Octree with an 8-bit node type (2 depth levels). -template -using octree8 = octree; - -/// Octree with a 16-bit node type (4 depth levels). -template -using octree16 = octree; - -/// Octree with a 32-bit node type (9 depth levels). -template -using octree32 = octree; - -/// Octree with a 64-bit node type (19 depth levels). -template -using octree64 = octree; - -/// Octree with unordered node storage and traversal. -template -using unordered_octree = octree; - -/// Unordered octree with an 8-bit node type (2 depth levels). -typedef unordered_octree unordered_octree8; - -/// Unordered octree with a 16-bit node type (4 depth levels). -typedef unordered_octree unordered_octree16; - -/// Unordered octree with a 32-bit node type (9 depth levels). -typedef unordered_octree unordered_octree32; - -/// Unordered octree with a 64-bit node type (19 depth levels). -typedef unordered_octreeunordered_octree64; - -} // namespace geom - -#endif // ANTKEEPER_GEOM_OCTREE_HPP diff --git a/src/geom/plane.hpp b/src/geom/plane.hpp deleted file mode 100644 index 5f07c7c..0000000 --- a/src/geom/plane.hpp +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GEOM_PLANE_HPP -#define ANTKEEPER_GEOM_PLANE_HPP - -#include "math/vector.hpp" - -namespace geom { - -/** - * A flat 2-dimensional surface. - */ -template -struct plane -{ - typedef math::vector vector_type; - - /// Plane normal vector. - vector_type normal; - - /// Plane distance. - T distance; - - /** - * Creates a plane given a normal vector and distance. - */ - plane(const vector_type& normal, T distance); - - /** - * Creates a plane given a normal vector and offset vector. - */ - plane(const vector_type& normal, const vector_type& offset); - - /** - * Creates a plane given three points. - */ - plane(const vector_type& a, const vector_type& b, const vector_type& c); - - /** - * Creates a plane given its coefficients. - * - * @param coefficients Vector containing the plane coefficients, A, B, C and D, as x, y, z, and w, respectively. - */ - plane(const math::vector& coefficients); - - /// Creates an uninitialized plane. - plane() = default; - - /** - * Calculates the signed distance between a plane and a vector. - * - * @param v Vector. - * @return Signed distance between the plane and vector. - */ - T signed_distance(const vector_type& v) const; - - /** - * Calculates the point of intersection between three planes. - */ - static vector_type intersection(const plane& p0, const plane& p1, const plane& p2); -}; - -template -inline plane::plane(const vector_type& normal, T distance): - normal(normal), - distance(distance) -{} - -template -plane::plane(const vector_type& normal, const vector_type& offset): - normal(normal), - distance(-math::dot(normal, offset)) -{} - -template -plane::plane(const vector_type& a, const vector_type& b, const vector_type& c) -{ - normal = math::normalize(math::cross(c - b, a - b)); - distance = -(math::dot(normal, b)); -} - -template -plane::plane(const math::vector& coefficients) -{ - const vector_type abc = math::vector(coefficients); - const float inverse_length = T(1) / math::length(abc); - - normal = abc * inverse_length; - distance = coefficients[3] * inverse_length; -} - -template -inline T plane::signed_distance(const vector_type& v) const -{ - return distance + math::dot(normal, v); -} - -template -typename plane::vector_type plane::intersection(const plane& p0, const plane& p1, const plane& p2) -{ - return -(p0.distance * math::cross(p1.normal, p2.normal) + p1.distance * math::cross(p2.normal, p0.normal) + p2.distance * math::cross(p0.normal, p1.normal)) / math::dot(p0.normal, math::cross(p1.normal, p2.normal)); -} - -} // namespace geom - -#endif // ANTKEEPER_GEOM_PLANE_HPP diff --git a/src/geom/primitive/box.hpp b/src/geom/primitive/box.hpp deleted file mode 100644 index 2200fc2..0000000 --- a/src/geom/primitive/box.hpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GEOM_PRIMITIVE_BOX_HPP -#define ANTKEEPER_GEOM_PRIMITIVE_BOX_HPP - -#include "geom/primitive/hyperrectangle.hpp" - -namespace geom { -namespace primitive { - -/// 3-dimensional hyperrectangle. -template -using box = hyperrectangle; - -} // namespace primitive -} // namespace geom - -#endif // ANTKEEPER_GEOM_PRIMITIVE_BOX_HPP diff --git a/src/geom/primitive/circle.hpp b/src/geom/primitive/circle.hpp deleted file mode 100644 index 2b90016..0000000 --- a/src/geom/primitive/circle.hpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GEOM_PRIMITIVE_CIRCLE_HPP -#define ANTKEEPER_GEOM_PRIMITIVE_CIRCLE_HPP - -#include "geom/primitive/hypersphere.hpp" - -namespace geom { -namespace primitive { - -/// 2-dimensional hypersphere. -template -using circle = hypersphere; - -} // namespace primitive -} // namespace geom - -#endif // ANTKEEPER_GEOM_PRIMITIVE_CIRCLE_HPP diff --git a/src/geom/primitive/hyperplane.hpp b/src/geom/primitive/hyperplane.hpp deleted file mode 100644 index 9bd8c81..0000000 --- a/src/geom/primitive/hyperplane.hpp +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GEOM_PRIMITIVE_HYPERPLANE_HPP -#define ANTKEEPER_GEOM_PRIMITIVE_HYPERPLANE_HPP - -#include "math/vector.hpp" - -namespace geom { -namespace primitive { - -/** - * *n*-dimensional plane. - * - * @tparam T Real type. - * @tparam N Number of dimensions. - */ -template -struct hyperplane -{ - typedef math::vector vector_type; - - /// Hyperplane normal. - vector_type normal; - - /// Hyperplane constant. - T constant; - - /// Constructs a hyperplane. - constexpr hyperplane() noexcept = default; - - /** - * Constructs a hyperplane given a point and a normal. - * - * @param point Point. - * @param normal Normal. - */ - constexpr hyperplane(const vector_type& point, const vector_type& normal) noexcept: - normal(normal), - constant(-math::dot(normal, point)) - {} - - /** - * Calculates the signed distance from the hyperplane to a point. - * - * @param point Input point. - * - * @return Signed distance from the hyperplane to @p point. - */ - constexpr T distance(const vector_type& point) const noexcept - { - return math::dot(normal, point) + constant; - } -}; - -} // namespace primitive -} // namespace geom - -#endif // ANTKEEPER_GEOM_PRIMITIVE_HYPERPLANE_HPP diff --git a/src/geom/primitive/hyperrectangle.hpp b/src/geom/primitive/hyperrectangle.hpp deleted file mode 100644 index 6d1b18f..0000000 --- a/src/geom/primitive/hyperrectangle.hpp +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GEOM_PRIMITIVE_HYPERRECTANGLE_HPP -#define ANTKEEPER_GEOM_PRIMITIVE_HYPERRECTANGLE_HPP - -#include "math/vector.hpp" -#include - -namespace geom { -namespace primitive { - -/** - * *n*-dimensional axis-aligned rectangle. - * - * @tparam T Real type. - * @tparam N Number of dimensions. - */ -template -struct hyperrectangle -{ - typedef math::vector vector_type; - - /// Minimum extent of the hyperrectangle. - vector_type min; - - /// Maximum extent of the hyperrectangle. - vector_type max; - - /** - * Tests whether a point is contained within this hyperrectangle. - * - * @param point Point to test for containment. - * - * @return `true` if the point is contained within this hyperrectangle, `false` otherwise. - */ - constexpr bool contains(const vector_type& point) const noexcept - { - for (std::size_t i = 0; i < N; ++i) - if (point[i] < min[i] || point[i] > max[i]) - return false; - return true; - } - - /** - * Tests whether another hyperrectangle is contained within this hyperrectangle. - * - * @param other Hyperrectangle to test for containment. - * - * @return `true` if the hyperrectangle is contained within this hyperrectangle, `false` otherwise. - */ - constexpr bool contains(const hyperrectangle& other) const noexcept - { - for (std::size_t i = 0; i < N; ++i) - if (other.min[i] < min[i] || other.max[i] > max[i]) - return false; - return true; - } - - /// Returns the center position of the hyperrectangle. - constexpr vector_type center() const noexcept - { - return (min + max) / T{2}; - } - - /** - * Calculates the signed distance from the hyperrectangle to a point. - * - * @param point Input point. - * - * @return Signed distance from the hyperrectangle to @p point. - */ - T distance(const vector_type& point) const noexcept - { - vector_type d = math::abs(point - center()) - size() * T{0.5}; - return math::length(math::max(vector_type::zero(), d)) + std::min(T{0}, math::max(d)); - } - - /** - * Extends the hyperrectangle to include a point. - * - * @param point Point to include in the hyperrectangle. - */ - void extend(const vector_type& point) noexcept - { - min = math::min(min, point); - max = math::max(max, point); - } - - /** - * Extends the hyperrectangle to include another hyperrectangle. - * - * @param other Hyperrectangle to include in this hyperrectangle. - */ - void extend(const hyperrectangle& other) noexcept - { - min = math::min(min, other.min); - max = math::max(max, other.max); - } - - /** - * Tests whether another hyperrectangle intersects this hyperrectangle. - * - * @param other Hyperrectangle to test for intersection. - * - * @return `true` if the hyperrectangle intersects this hyperrectangle, `false` otherwise. - */ - constexpr bool intersects(const hyperrectangle& other) const noexcept - { - for (std::size_t i = 0; i < N; ++i) - if (other.min[i] > max[i] || other.max[i] < min[i]) - return false; - return true; - } - - /// Returns the size of the hyperrectangle. - constexpr vector_type size() const noexcept - { - return max - min; - } - - /** - * Returns `false` if any coordinates of `min` are greater than `max`. - */ - constexpr bool valid() const noexcept - { - for (std::size_t i = 0; i < N; ++i) - if (min[i] > max[i]) - return false; - return true; - } - - /// Calculates the volume of the hyperrectangle. - constexpr T volume() const noexcept - { - T v = max[0] - min[0]; - for (std::size_t i = 1; i < N; ++i) - v *= max[i] - min[i]; - return v; - } -}; - -} // namespace primitive -} // namespace geom - -#endif // ANTKEEPER_GEOM_PRIMITIVE_HYPERRECTANGLE_HPP diff --git a/src/geom/primitive/hypersphere.hpp b/src/geom/primitive/hypersphere.hpp deleted file mode 100644 index 4239d0a..0000000 --- a/src/geom/primitive/hypersphere.hpp +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GEOM_PRIMITIVE_HYPERSPHERE_HPP -#define ANTKEEPER_GEOM_PRIMITIVE_HYPERSPHERE_HPP - -#include "math/numbers.hpp" -#include "math/vector.hpp" - -namespace geom { -namespace primitive { - -/** - * *n*-dimensional sphere. - * - * @tparam T Real type. - * @tparam N Number of dimensions. - */ -template -struct hypersphere -{ - typedef math::vector vector_type; - - /// Hypersphere center. - vector_type center; - - /// Hypersphere radius. - T radius; - - /** - * Tests whether a point is contained within this hypersphere. - * - * @param point Point to test for containment. - * - * @return `true` if the point is contained within this hypersphere, `false` otherwise. - */ - constexpr bool contains(const vector_type& point) const noexcept - { - return math::sqr_distance(center, point) <= radius * radius; - } - - /** - * Tests whether another hypersphere is contained within this hypersphere. - * - * @param other Hypersphere to test for containment. - * - * @return `true` if the hypersphere is contained within this hypersphere, `false` otherwise. - */ - constexpr bool contains(const hypersphere& other) const noexcept - { - const T containment_radius = radius - other.radius; - if (containment_radius < T{0}) - return false; - - return math::sqr_distance(center, other.center) <= containment_radius * containment_radius; - } - - /** - * Calculates the signed distance from the hypersphere to a point. - * - * @param point Input point. - * - * @return Signed distance from the hypersphere to @p point. - */ - T distance(const vector_type& point) const noexcept - { - return math::distance(center, point) - radius; - } - - /** - * Tests whether another hypersphere intersects this hypersphere. - * - * @param other Hypersphere to test for intersection. - * - * @return `true` if the hypersphere intersects this hypersphere, `false` otherwise. - */ - constexpr bool intersects(const hypersphere& other) const noexcept - { - const T intersection_radius = radius + other.radius; - return math::sqr_distance(center, other.center) <= intersection_radius * intersection_radius; - } - - /** - * Volume calculation helper function. - * - * @tparam M Dimension. - * - * @param r Radius. - * - * @return Volume. - */ - /// @private - /// @{ - template - static constexpr T volume(T r) noexcept - { - return (math::two_pi / static_cast(M)) * r * r * volume(r); - } - - template <> - static constexpr T volume<1>(T r) noexcept - { - return r * T{2}; - } - - template <> - static constexpr T volume<0>(T r) noexcept - { - return T{1}; - } - /// @} - - /// Calculates the volume of the hypersphere. - inline constexpr T volume() const noexcept - { - return volume(radius); - } -}; - -} // namespace primitive -} // namespace geom - -#endif // ANTKEEPER_GEOM_PRIMITIVE_HYPERSPHERE_HPP diff --git a/src/geom/primitive/intersection.hpp b/src/geom/primitive/intersection.hpp deleted file mode 100644 index d3a3332..0000000 --- a/src/geom/primitive/intersection.hpp +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GEOM_PRIMITIVE_INTERSECTION_HPP -#define ANTKEEPER_GEOM_PRIMITIVE_INTERSECTION_HPP - -#include "geom/primitive/hyperplane.hpp" -#include "geom/primitive/hyperrectangle.hpp" -#include "geom/primitive/hypersphere.hpp" -#include "geom/primitive/ray.hpp" -#include -#include - -namespace geom { -namespace primitive { - -/** - * Ray-hyperplane intersection test. - * - * @param ray Ray. - * @param hyperplane Hyperplane. - * - * @return Distance along the ray to the point of intersection, or `std::nullopt` if no intersection occurred. - */ -/// @{ -template -constexpr std::optional intersection(const ray& ray, const hyperplane& hyperplane) noexcept -{ - const T cos_theta = math::dot(ray.direction, hyperplane.normal); - if (cos_theta != T{0}) - { - const T t = -hyperplane.distance(ray.origin) / cos_theta; - if (t >= T{0}) - return t; - } - - return std::nullopt; -} - -template -inline constexpr std::optional intersection(const hyperplane& hyperplane, const ray& ray) noexcept -{ - return intersection(ray, hyperplane); -} -/// @} - -/** - * Ray-hyperrectangle intersection test. - * - * @param ray Ray. - * @param hyperrectangle Hyperrectangle. - * - * @return Tuple containing the distances along the ray to the first and second points of intersection, or `std::nullopt` if no intersection occurred. - */ -/// @{ -template -constexpr std::optional> intersection(const ray& ray, const hyperrectangle& hyperrectangle) noexcept -{ - T t0 = -std::numeric_limits::infinity(); - T t1 = std::numeric_limits::infinity(); - - for (std::size_t i = 0; i < N; ++i) - { - if (!ray.direction[i]) - { - if (ray.origin[i] < hyperrectangle.min[i] || ray.origin[i] > hyperrectangle.max[i]) - return std::nullopt; - } - else - { - T min = (hyperrectangle.min[i] - ray.origin[i]) / ray.direction[i]; - T max = (hyperrectangle.max[i] - ray.origin[i]) / ray.direction[i]; - t0 = std::max(t0, std::min(min, max)); - t1 = std::min(t1, std::max(min, max)); - } - } - - if (t0 > t1 || t1 < T{0}) - return std::nullopt; - - return {t0, t1}; -} - -template -inline constexpr std::optional> intersection(const hyperrectangle& hyperrectangle, const ray& ray) noexcept -{ - return intersection(ray, hyperrectangle); -} -/// @} - -/** - * Ray-hypersphere intersection test. - * - * @param ray Ray. - * @param hypersphere Hypersphere. - * - * @return Tuple containing the distances along the ray to the first and second points of intersection, or `std::nullopt` if no intersection occurred. - */ -template -std::optional> intersection(const ray& ray, const hypersphere& hypersphere) noexcept -{ - const math::vector displacement = ray.origin - hypersphere.center; - const T b = math::dot(displacement, ray.direction); - const T c = math::sqr_length(displacement) - hypersphere.radius * hypersphere.radius; - T h = b * b - c; - - if (h < T{0}) - return std::nullopt; - - h = std::sqrt(h); - - T t0 = -b - h; - T t1 = -b + h; - if (t0 > t1) - std::swap(t0, t1); - - if (t0 < T{0}) - return std::nullopt; - - return std::tuple{t0, t1}; -} - -/** - * Hyperrectangle-hyperrectangle intersection test. - * - * @param a First hyperrectangle. - * @param b Second hyperrectangle. - * - * @return `true` if an intersection occurred, `false` otherwise. - */ -template -inline constexpr bool intersection(const hyperrectangle& a, const hyperrectangle& b) noexcept -{ - return a.intersects(b); -} - -/** - * Hyperrectangle-hypersphere intersection test. - * - * @param hyperrectangle Hyperrectangle. - * @param hypersphere Hypersphere. - * - * @return `true` if an intersection occurred, `false` otherwise. - */ -/// @{ -template -constexpr bool intersection(const hyperrectangle& hyperrectangle, const hypersphere& hypersphere) noexcept -{ - T sqr_distance{0}; - for (std::size_t i = 0; i < N; ++i) - { - if (hypersphere.center[i] < hyperrectangle.min[i]) - { - const T difference = hyperrectangle.min[i] - hypersphere.center[i]; - sqr_distance += difference * difference; - } - else if (hypersphere.center[i] > hyperrectangle.max[i]) - { - const T difference = hypersphere.center[i] - hyperrectangle.max[i]; - sqr_distance += difference * difference; - } - } - - return sqr_distance <= hypersphere.radius * hypersphere.radius; -} - -template -inline constexpr bool intersection(const hypersphere& hypersphere, const hyperrectangle& hyperrectangle) noexcept -{ - return intersection(hyperrectangle, hypersphere); -} -/// @} - -/** - * Hypersphere-hypersphere intersection test. - * - * @param a First hypersphere. - * @param b Second hypersphere. - * - * @return `true` if an intersection occurred, `false` otherwise. - */ -template -inline constexpr bool intersection(const hypersphere& a, const hypersphere& b) noexcept -{ - return a.intersects(b); -} - -} // namespace primitive -} // namespace geom - -#endif // ANTKEEPER_GEOM_PRIMITIVE_INTERSECTION_HPP diff --git a/src/geom/primitive/line-segment.hpp b/src/geom/primitive/line-segment.hpp deleted file mode 100644 index 2cb2ef6..0000000 --- a/src/geom/primitive/line-segment.hpp +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GEOM_PRIMITIVE_LINE_SEGMENT_HPP -#define ANTKEEPER_GEOM_PRIMITIVE_LINE_SEGMENT_HPP - -#include "math/vector.hpp" - -namespace geom { -namespace primitive { - -/** - * *n*-dimensional line segment. - * - * @tparam T Real type. - * @tparam N Number of dimensions. - */ -template -struct line_segment -{ - typedef math::vector vector_type; - - /// First endpoint. - vector_type a; - - /// Second endpoint. - vector_type b; - - /** - * Calculates the length of the line segment. - * - * @return Length of the line segment. - */ - T length() const noexcept - { - return math::distance(a, b); - } -}; - -} // namespace primitive -} // namespace geom - -#endif // ANTKEEPER_GEOM_PRIMITIVE_LINE_SEGMENT_HPP diff --git a/src/geom/primitive/line.hpp b/src/geom/primitive/line.hpp deleted file mode 100644 index 1b467b6..0000000 --- a/src/geom/primitive/line.hpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GEOM_PRIMITIVE_LINE_HPP -#define ANTKEEPER_GEOM_PRIMITIVE_LINE_HPP - -#include "geom/primitive/hyperplane.hpp" - -namespace geom { -namespace primitive { - -/// 2-dimensional hyperplane. -template -using line = hyperplane; - -} // namespace primitive -} // namespace geom - -#endif // ANTKEEPER_GEOM_PRIMITIVE_LINE_HPP diff --git a/src/geom/primitive/plane.hpp b/src/geom/primitive/plane.hpp deleted file mode 100644 index b973696..0000000 --- a/src/geom/primitive/plane.hpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GEOM_PRIMITIVE_PLANE_HPP -#define ANTKEEPER_GEOM_PRIMITIVE_PLANE_HPP - -#include "geom/primitive/hyperplane.hpp" - -namespace geom { -namespace primitive { - -/// 3-dimensional hyperplane. -template -using plane = hyperplane; - -} // namespace primitive -} // namespace geom - -#endif // ANTKEEPER_GEOM_PRIMITIVE_PLANE_HPP diff --git a/src/geom/primitive/ray.hpp b/src/geom/primitive/ray.hpp deleted file mode 100644 index 56bbcc7..0000000 --- a/src/geom/primitive/ray.hpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GEOM_PRIMITIVE_RAY_HPP -#define ANTKEEPER_GEOM_PRIMITIVE_RAY_HPP - -#include "math/vector.hpp" - -namespace geom { -namespace primitive { - -/** - * Half of a line proceeding from an initial point. - * - * @tparam T Real type. - * @tparam N Number of dimensions. - */ -template -struct ray -{ - typedef math::vector vector_type; - - /// Ray origin position. - vector_type origin; - - /// Ray direction vector. - vector_type direction; - - /** - * Extrapolates from the ray origin along the ray direction vector. - * - * @param distance Signed extrapolation distance. - * - * @return Extrapolated coordinates. - */ - constexpr vector_type extrapolate(T distance) const noexcept - { - return origin + direction * distance; - } -}; - -} // namespace primitive -} // namespace geom - -#endif // ANTKEEPER_GEOM_PRIMITIVE_RAY_HPP diff --git a/src/geom/primitive/rectangle.hpp b/src/geom/primitive/rectangle.hpp deleted file mode 100644 index 46980f0..0000000 --- a/src/geom/primitive/rectangle.hpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GEOM_PRIMITIVE_RECTANGLE_HPP -#define ANTKEEPER_GEOM_PRIMITIVE_RECTANGLE_HPP - -#include "geom/primitive/hyperrectangle.hpp" - -namespace geom { -namespace primitive { - -/// 2-dimensional hyperrectangle. -template -using rectangle = hyperrectangle; - -} // namespace primitive -} // namespace geom - -#endif // ANTKEEPER_GEOM_PRIMITIVE_RECTANGLE_HPP diff --git a/src/geom/primitive/sphere.hpp b/src/geom/primitive/sphere.hpp deleted file mode 100644 index 0a606cc..0000000 --- a/src/geom/primitive/sphere.hpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GEOM_PRIMITIVE_SPHERE_HPP -#define ANTKEEPER_GEOM_PRIMITIVE_SPHERE_HPP - -#include "geom/primitive/hypersphere.hpp" - -namespace geom { -namespace primitive { - -/// 3-dimensional hypersphere. -template -using sphere = hypersphere; - -} // namespace primitive -} // namespace geom - -#endif // ANTKEEPER_GEOM_PRIMITIVE_SPHERE_HPP diff --git a/src/geom/projection.hpp b/src/geom/projection.hpp deleted file mode 100644 index e1abf45..0000000 --- a/src/geom/projection.hpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GEOM_PROJECTION_HPP -#define ANTKEEPER_GEOM_PROJECTION_HPP - -#include "math/vector.hpp" - -namespace geom { - -template -math::vector project_on_plane(const math::vector& v, const math::vector& p, const math::vector& n) -{ - return v - n * math::dot(v - p, n); -} - -} // namespace geom - -#endif // ANTKEEPER_GEOM_PROJECTION_HPP diff --git a/src/geom/quadtree.hpp b/src/geom/quadtree.hpp deleted file mode 100644 index 75e30a7..0000000 --- a/src/geom/quadtree.hpp +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GEOM_QUADTREE_HPP -#define ANTKEEPER_GEOM_QUADTREE_HPP - -#include "geom/hyperoctree.hpp" -#include - -namespace geom { - -/// A quadtree, or 2-dimensional hyperoctree. -template -using quadtree = hyperoctree; - -/// Quadtree with an 8-bit node type (2 depth levels). -template -using quadtree8 = quadtree; - -/// Quadtree with a 16-bit node type (6 depth levels). -template -using quadtree16 = quadtree; - -/// Quadtree with a 32-bit node type (13 depth levels). -template -using quadtree32 = quadtree; - -/// Quadtree with a 64-bit node type (29 depth levels). -template -using quadtree64 = quadtree; - -/// Quadtree with unordered node storage and traversal. -template -using unordered_quadtree = quadtree; - -/// Unordered quadtree with an 8-bit node type (2 depth levels). -typedef unordered_quadtree unordered_quadtree8; - -/// Unordered quadtree with a 16-bit node type (6 depth levels). -typedef unordered_quadtree unordered_quadtree16; - -/// Unordered quadtree with a 32-bit node type (13 depth levels). -typedef unordered_quadtree unordered_quadtree32; - -/// Unordered quadtree with a 64-bit node type (29 depth levels). -typedef unordered_quadtreeunordered_quadtree64; - -} // namespace geom - -#endif // ANTKEEPER_GEOM_QUADTREE_HPP diff --git a/src/geom/ray.hpp b/src/geom/ray.hpp deleted file mode 100644 index 4d3d573..0000000 --- a/src/geom/ray.hpp +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GEOM_RAY_HPP -#define ANTKEEPER_GEOM_RAY_HPP - -#include "math/vector.hpp" - -namespace geom { - -/** - * Half of a line proceeding from an initial point. - */ -template -struct ray -{ - typedef math::vector vector_type; - - /// Origin of the ray. - vector_type origin; - - /// Normalized direction vector of the ray. - vector_type direction; - - /** - * Extrapolates from the ray origin along the ray direction vector. - * - * @param distance Distance to extrapolate. - * @return Extrapolated coordinates. - */ - vector_type extrapolate(T distance) const; -}; - -template -inline typename ray::vector_type ray::extrapolate(T distance) const -{ - return origin + direction * distance; -} - -} // namespace geom - -#endif // ANTKEEPER_GEOM_RAY_HPP - diff --git a/src/geom/rect-pack.hpp b/src/geom/rect-pack.hpp deleted file mode 100644 index d44ec38..0000000 --- a/src/geom/rect-pack.hpp +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GEOM_RECT_PACK_HPP -#define ANTKEEPER_GEOM_RECT_PACK_HPP - -#include "geom/rect.hpp" - -namespace geom { - -/** - * Node used in 2D rectangle packing. - * - * @see geom::rect_pack - */ -template -struct rect_pack_node -{ - /// Scalar type. - typedef T scalar_type; - - /// Rect type. - typedef rect rect_type; - - /// Creates a rect pack node. - rect_pack_node(); - - /// Destroys a rect pack node and its children. - ~rect_pack_node(); - - /// Pointers to the two children of the node, if any. - rect_pack_node* children[2]; - - /// Bounds of the node. - rect_type bounds; - - /// `true` if the node is occupied, `false` otherwise. - bool occupied; -}; - -template -rect_pack_node::rect_pack_node(): - bounds{T(0), T(0), T(0), T(0)}, - occupied(false) -{ - children[0] = nullptr; - children[1] = nullptr; -} - -template -rect_pack_node::~rect_pack_node() -{ - delete children[0]; - delete children[1]; -} - -/** - * Packs 2D rectangles. - * - * @see geom::rect_pack_node - * - * @see http://www.blackpawn.com/texts/lightmaps/ - */ -template -class rect_pack -{ -public: - /// Scalar type. - typedef T scalar_type; - - /// Node type. - typedef rect_pack_node node_type; - - /** - * Creates a rect pack and sets the bounds of the root node. - * - * @param w Width of the root node. - * @param h Height of the root node. - */ - rect_pack(scalar_type w, scalar_type h); - - /** - * Creates an empty rect pack. - */ - rect_pack(); - - /** - * Clears the pack and resizes the root node bounds. - * - * @param w New width of the root node. - * @param h New height of the root node. - * - * @see rect_pack::clear() - */ - void resize(scalar_type w, scalar_type h); - - /// Clear the pack, deallocating all nodes. - void clear(); - - /** - * Packs a rect into the rect pack. - * - * @param w Width of the rect. - * @param h Height of the rect. - * @return Pointer to the node in which the rect was packed, or `nullptr` if the rect could not be packed. - */ - const node_type* pack(scalar_type w, scalar_type h); - - /// Returns a reference to the root node. - const node_type& get_root() const; - -private: - static node_type* insert(node_type& parent, scalar_type w, scalar_type h); - - static void free(); - - node_type root; -}; - -template -rect_pack::rect_pack(scalar_type w, scalar_type h) -{ - root.bounds = {T(0), T(0), w, h}; -} - -template -rect_pack::rect_pack(): - rect_pack(0, 0) -{} - -template -void rect_pack::resize(scalar_type w, scalar_type h) -{ - clear(); - root.bounds = {T(0), T(0), w, h}; -} - -template -void rect_pack::clear() -{ - delete root.children[0]; - delete root.children[1]; - root.children[0] = nullptr; - root.children[1] = nullptr; - root.occupied = false; -} - -template -const typename rect_pack::node_type* rect_pack::pack(scalar_type w, scalar_type h) -{ - return insert(root, w, h); -} - -template -inline const typename rect_pack::node_type& rect_pack::get_root() const -{ - return root; -} - -template -typename rect_pack::node_type* rect_pack::insert(node_type& node, scalar_type w, scalar_type h) -{ - // If not a leaf node - if (node.children[0] && node.children[1]) - { - // Attempt to insert into first child - node_type* result = insert(*node.children[0], w, h); - if (result) - return result; - - // Cannot fit in first child, attempt to insert into second child - return insert(*node.children[1], w, h); - - } - - // Abort if node occupied - if (node.occupied) - return nullptr; - - // Determine node dimensions - scalar_type node_w = node.bounds.max.x() - node.bounds.min.x(); - scalar_type node_h = node.bounds.max.y() - node.bounds.min.y(); - - // Check if rect is larger than node - if (w > node_w || h > node_h) - return nullptr; - - // Check for a perfect fit - if (w == node_w && h == node_h) - { - node.occupied = true; - return &node; - } - - // Split the node - node.children[0] = new node_type(); - node.children[1] = new node_type(); - - // Determine split direction - scalar_type dw = node_w - w; - scalar_type dh = node_h - h; - if (dw > dh) - { - node.children[0]->bounds.min = node.bounds.min; - node.children[0]->bounds.max = {node.bounds.min.x() + w, node.bounds.max.y()}; - node.children[1]->bounds.min = {node.bounds.min.x() + w, node.bounds.min.y()}; - node.children[1]->bounds.max = {node.bounds.max}; - } - else - { - node.children[0]->bounds.min = node.bounds.min; - node.children[0]->bounds.max = {node.bounds.max.x(), node.bounds.min.y() + h}; - node.children[1]->bounds.min = {node.bounds.min.x(), node.bounds.min.y() + h}; - node.children[1]->bounds.max = {node.bounds.max}; - } - - // Insert into first child - return insert(*node.children[0], w, h); -} - -} // namespace geom - -#endif // ANTKEEPER_GEOM_RECT_PACK_HPP diff --git a/src/geom/rect.hpp b/src/geom/rect.hpp deleted file mode 100644 index 85aacca..0000000 --- a/src/geom/rect.hpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GEOM_RECT_HPP -#define ANTKEEPER_GEOM_RECT_HPP - -#include "math/vector.hpp" - -namespace geom { - -/** - * 2D rectangle. - */ -template -struct rect -{ - typedef math::vector vector_type; - - vector_type min; - vector_type max; -}; - -} // namespace geom - -#endif // ANTKEEPER_GEOM_RECT_HPP diff --git a/src/geom/sdf.hpp b/src/geom/sdf.hpp deleted file mode 100644 index 815764d..0000000 --- a/src/geom/sdf.hpp +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GEOM_SDF_HPP -#define ANTKEEPER_GEOM_SDF_HPP - -#include "utility/fundamental-types.hpp" -#include - -namespace geom { - -/// Signed distance functions. -namespace sdf { - -inline float3 translate(const float3& sample, const float3& offset) -{ - return sample - offset; -} - -inline float sphere(const float3& p, float r) -{ - return math::length(p) - r; -} - -inline float cylinder(const float3& p, float r, float h) -{ - float dx = std::abs(math::length(math::swizzle<0, 2>(p))) - r; - float dy = std::abs(p[1]) - h; - return std::min(std::max(dx, dy), 0.0f) + math::length(float2{std::max(dx, 0.0f), std::max(dy, 0.0f)}); -} - -inline float op_union(float a, float b) -{ - return std::min(a, b); -} - -inline float op_difference(float a, float b) -{ - return std::max(-a, b); -} - -inline float op_intersection(float a, float b) -{ - return std::max(a, b); -} - -inline float op_round(float d, float r) -{ - return d - r; -} - -} // namespace sdf -} // namespace geom - -#endif // ANTKEEPER_GEOM_SDF_HPP - diff --git a/src/geom/solid-angle.hpp b/src/geom/solid-angle.hpp deleted file mode 100644 index c8fbfac..0000000 --- a/src/geom/solid-angle.hpp +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GEOM_SOLID_ANGLE_HPP -#define ANTKEEPER_GEOM_SOLID_ANGLE_HPP - -#include "math/numbers.hpp" -#include - -namespace geom { - -/// Solid angle functions. -namespace solid_angle { - -/** - * Calculates the solid angle of a cone. - * - * @param theta Apex angle of the cone, in radians. - * - * @return Solid angle of the cone, in steradians. - */ -template -T cone(T theta) -{ - return math::two_pi * (T(1) - std::cos(theta)); -} - -} // namespace solid_angle -} // namespace geom - -#endif // ANTKEEPER_GEOM_SOLID_ANGLE_HPP diff --git a/src/geom/sphere.hpp b/src/geom/sphere.hpp deleted file mode 100644 index 9056ef7..0000000 --- a/src/geom/sphere.hpp +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GEOM_SPHERE_HPP -#define ANTKEEPER_GEOM_SPHERE_HPP - -#include "geom/bounding-volume.hpp" -#include "geom/aabb.hpp" -#include "math/vector.hpp" -#include - -namespace geom { - -/** - * Bounding sphere. - */ -template -struct sphere: public bounding_volume -{ - typedef math::vector vector_type; - - vector_type center; - T radius; - - sphere(const vector_type& center, T radius); - sphere(); - - virtual bounding_volume_type get_bounding_volume_type() const; - virtual bool intersects(const sphere& sphere) const; - virtual bool intersects(const aabb& aabb) const; - virtual bool contains(const sphere& sphere) const; - virtual bool contains(const aabb& aabb) const; - virtual bool contains(const vector_type& point) const; -}; - -template -sphere::sphere(const vector_type& center, T radius): - center(center), - radius(radius) -{} - -template -sphere::sphere() -{} - -template -inline bounding_volume_type sphere::get_bounding_volume_type() const -{ - return bounding_volume_type::sphere; -} - -template -bool sphere::intersects(const sphere& sphere) const -{ - vector_type d = center - sphere.center; - T r = radius + sphere.radius; - return (math::dot(d, d) <= r * r); -} - -template -bool sphere::intersects(const aabb& aabb) const -{ - return aabb.intersects(*this); -} - -template -bool sphere::contains(const sphere& sphere) const -{ - T containment_radius = radius - sphere.radius; - if (containment_radius < T(0)) - return false; - - vector_type d = center - sphere.center; - return (math::dot(d, d) <= containment_radius * containment_radius); -} - -template -bool sphere::contains(const aabb& aabb) const -{ - T distance = T(0); - - vector_type a = center - aabb.min_point; - vector_type b = center - aabb.max_point; - - distance += std::max(a.x() * a.x(), b.x() * b.x()); - distance += std::max(a.y() * a.y(), b.y() * b.y()); - distance += std::max(a.z() * a.z(), b.z() * b.z()); - - return (distance <= radius * radius); -} - -template -bool sphere::contains(const vector_type& point) const -{ - vector_type d = center - point; - return (math::dot(d, d) <= radius * radius); -} - -} // namespace geom - -#endif // ANTKEEPER_GEOM_SPHERE_HPP diff --git a/src/geom/spherical.hpp b/src/geom/spherical.hpp deleted file mode 100644 index 6164be8..0000000 --- a/src/geom/spherical.hpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GEOM_SPHERICAL_HPP -#define ANTKEEPER_GEOM_SPHERICAL_HPP - -#include "utility/fundamental-types.hpp" -#include - -namespace geom { - -/// Functions which operate on spherical coordinates. -namespace spherical { - -/** - * Converts spherical coordinates to Cartesian (rectangular) coordinates. - * - * @param v Spherical coordinates, in the ISO order of radial distance, polar angle (radians), and azimuthal angle (radians). - * @return Cartesian coordinates. - * - * @see geom::coordinates::cartesian - */ -template -math::vector3 to_cartesian(const math::vector3& v); - -template -math::vector3 to_cartesian(const math::vector3& v) -{ - const T x = v[0] * std::cos(v[1]); - - return math::vector3 - { - x * std::cos(v[2]), - x * std::sin(v[2]), - v[0] * std::sin(v[1]) - }; -} - -} // namespace spherical -} // namespace geom - -#endif // ANTKEEPER_GEOM_SPHERICAL_HPP diff --git a/src/geom/view-frustum.hpp b/src/geom/view-frustum.hpp deleted file mode 100644 index 23b1039..0000000 --- a/src/geom/view-frustum.hpp +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GEOM_VIEW_FRUSTUM_HPP -#define ANTKEEPER_GEOM_VIEW_FRUSTUM_HPP - -#include "geom/convex-hull.hpp" -#include "math/vector.hpp" -#include "math/matrix.hpp" -#include - -namespace geom { - -/** - * View frustum. - */ -template -class view_frustum -{ -public: - typedef math::vector vector_type; - typedef math::matrix matrix_type; - typedef plane plane_type; - - /** - * Creates a view frustum from a view-projection matrix. - * - * @param view_projection View-projection matrix. - */ - view_frustum(const matrix_type& view_projection); - - /// Creates an uninitialized view frustum. - view_frustum(); - - /** - * Recalculates the view frustum from a view-projection matrix. - * - * @param view_projection View-projection matrix. - */ - void set_matrix(const matrix_type& view_projection); - - /// Returns a convex hull which describes the bounds of the view frustum. - const convex_hull& get_bounds() const; - - /// Returns the left clipping plane. - const plane_type& get_left() const; - - /// Returns the right clipping plane. - const plane_type& get_right() const; - - /// Returns the bottom clipping plane. - const plane_type& get_bottom() const; - - /// Returns the top clipping plane. - const plane_type& get_top() const; - - /// Returns the near clipping plane. - const plane_type& get_near() const; - - /// Returns the far clipping plane. - const plane_type& get_far() const; - - /** - * Returns an array containing the corners of the view frustum bounds. - * - * @return Array containing the corners of the view frustum bounds. Corners are stored in the following order: NTL, NTR, NBL, NBR, FTL, FTR, FBL, FBR; where N is near, F is far, T is top, B is bottom, L is left, and R is right, therefore NTL refers to the corner shared between the near, top, and left clipping planes. - */ - const std::array& get_corners() const; - -private: - void recalculate_planes(const matrix_type& view_projection); - void recalculate_corners(); - - convex_hull bounds; - std::array corners; -}; - -template -view_frustum::view_frustum(const matrix_type& view_projection): - bounds(6) -{ - set_matrix(view_projection); -} - -template -view_frustum::view_frustum(): - view_frustum(math::matrix4::identity()) -{} - -template -void view_frustum::set_matrix(const matrix_type& view_projection) -{ - recalculate_planes(view_projection); - recalculate_corners(); -} - -template -inline const convex_hull& view_frustum::get_bounds() const -{ - return bounds; -} - -template -inline const typename view_frustum::plane_type& view_frustum::get_left() const -{ - return bounds.planes[0]; -} - -template -inline const typename view_frustum::plane_type& view_frustum::get_right() const -{ - return bounds.planes[1]; -} - -template -inline const typename view_frustum::plane_type& view_frustum::get_bottom() const -{ - return bounds.planes[2]; -} - -template -inline const typename view_frustum::plane_type& view_frustum::get_top() const -{ - return bounds.planes[3]; -} - -template -inline const typename view_frustum::plane_type& view_frustum::get_near() const -{ - return bounds.planes[4]; -} - -template -inline const typename view_frustum::plane_type& view_frustum::get_far() const -{ - return bounds.planes[5]; -} - -template -inline const std::array::vector_type, 8>& view_frustum::get_corners() const -{ - return corners; -} - -template -void view_frustum::recalculate_planes(const matrix_type& view_projection) -{ - matrix_type transpose = math::transpose(view_projection); - bounds.planes[0] = plane_type(transpose[3] + transpose[0]); - bounds.planes[1] = plane_type(transpose[3] - transpose[0]); - bounds.planes[2] = plane_type(transpose[3] + transpose[1]); - bounds.planes[3] = plane_type(transpose[3] - transpose[1]); - bounds.planes[4] = plane_type(transpose[3] + transpose[2]); - bounds.planes[5] = plane_type(transpose[3] - transpose[2]); -} - -template -void view_frustum::recalculate_corners() -{ - /// @TODO THESE CORNERS MAY BE INCORRECT!!!!!!!!!!! - corners[0] = plane_type::intersection(get_near(), get_top(), get_left()); - corners[1] = plane_type::intersection(get_near(), get_top(), get_right()); - corners[2] = plane_type::intersection(get_near(), get_bottom(), get_left()); - corners[3] = plane_type::intersection(get_near(), get_bottom(), get_right()); - corners[4] = plane_type::intersection(get_far(), get_top(), get_left()); - corners[5] = plane_type::intersection(get_far(), get_top(), get_right()); - corners[6] = plane_type::intersection(get_far(), get_bottom(), get_left()); - corners[7] = plane_type::intersection(get_far(), get_bottom(), get_right()); -} - -} // namespace geom - -#endif // ANTKEEPER_GEOM_VIEW_FRUSTUM_HPP - diff --git a/src/gl/framebuffer.cpp b/src/gl/framebuffer.cpp deleted file mode 100644 index 0438b8b..0000000 --- a/src/gl/framebuffer.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "gl/framebuffer.hpp" -#include "gl/texture-2d.hpp" -#include - -namespace gl { - -static constexpr GLenum attachment_lut[] = -{ - GL_COLOR_ATTACHMENT0, - GL_DEPTH_ATTACHMENT, - GL_STENCIL_ATTACHMENT -}; - -framebuffer::framebuffer(int width, int height): - gl_framebuffer_id(0), - dimensions({width, height}), - color_attachment(nullptr), - depth_attachment(nullptr), - stencil_attachment(nullptr) -{ - glGenFramebuffers(1, &gl_framebuffer_id); -} - -framebuffer::framebuffer(): - gl_framebuffer_id(0), - dimensions({0, 0}), - color_attachment(nullptr), - depth_attachment(nullptr), - stencil_attachment(nullptr) -{} - -framebuffer::~framebuffer() -{ - if (gl_framebuffer_id) - { - glDeleteFramebuffers(1, &gl_framebuffer_id); - } -} - -void framebuffer::resize(const std::array& dimensions) -{ - this->dimensions = dimensions; -} - -void framebuffer::attach(framebuffer_attachment_type attachment_type, texture_2d* texture) -{ - glBindFramebuffer(GL_FRAMEBUFFER, gl_framebuffer_id); - - GLenum gl_attachment = attachment_lut[static_cast(attachment_type)]; - glFramebufferTexture2D(GL_FRAMEBUFFER, gl_attachment, GL_TEXTURE_2D, texture->gl_texture_id, 0); - - if (attachment_type == framebuffer_attachment_type::color) - color_attachment = texture; - else if (attachment_type == framebuffer_attachment_type::depth) - depth_attachment = texture; - else if (attachment_type == framebuffer_attachment_type::stencil) - stencil_attachment = texture; - - if (!color_attachment) - { - glDrawBuffer(GL_NONE); - glReadBuffer(GL_NONE); - } - else - { - glDrawBuffer(GL_COLOR_ATTACHMENT0); - glReadBuffer(GL_COLOR_ATTACHMENT0); - } - - glBindFramebuffer(GL_FRAMEBUFFER, 0); -} - -} // namespace gl diff --git a/src/gl/gl.hpp b/src/gl/gl.hpp deleted file mode 100644 index a6d41bc..0000000 --- a/src/gl/gl.hpp +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GL_HPP -#define ANTKEEPER_GL_HPP - -/// Graphics library interface. -namespace gl {} - -#include "gl/buffer-usage.hpp" -#include "gl/color-space.hpp" -#include "gl/drawing-mode.hpp" -#include "gl/element-array-type.hpp" -#include "gl/framebuffer.hpp" -#include "gl/pixel-format.hpp" -#include "gl/pixel-type.hpp" -#include "gl/rasterizer.hpp" -#include "gl/shader-input.hpp" -#include "gl/shader-object.hpp" -#include "gl/shader-program.hpp" -#include "gl/shader-stage.hpp" -#include "gl/shader-variable-type.hpp" -#include "gl/texture-2d.hpp" -#include "gl/texture-cube.hpp" -#include "gl/texture-filter.hpp" -#include "gl/texture-wrapping.hpp" -#include "gl/vertex-array.hpp" -#include "gl/vertex-attribute.hpp" -#include "gl/vertex-buffer.hpp" - -#endif // ANTKEEPER_GL_HPP diff --git a/src/gl/rasterizer.cpp b/src/gl/rasterizer.cpp deleted file mode 100644 index d02c786..0000000 --- a/src/gl/rasterizer.cpp +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "gl/rasterizer.hpp" -#include "gl/framebuffer.hpp" -#include "gl/shader-program.hpp" -#include "gl/vertex-array.hpp" -#include - -namespace gl { - -static constexpr GLenum drawing_mode_lut[] = -{ - GL_POINTS, - GL_LINE_STRIP, - GL_LINE_LOOP, - GL_LINES, - GL_LINE_STRIP_ADJACENCY, - GL_LINES_ADJACENCY, - GL_TRIANGLE_STRIP, - GL_TRIANGLE_FAN, - GL_TRIANGLES, - GL_TRIANGLE_STRIP_ADJACENCY, - GL_TRIANGLES_ADJACENCY -}; - -static constexpr GLenum element_array_type_lut[] = -{ - GL_UNSIGNED_BYTE, - GL_UNSIGNED_SHORT, - GL_UNSIGNED_INT -}; - -rasterizer::rasterizer(): - bound_vao(nullptr), - bound_shader_program(nullptr) -{ - // Determine dimensions of default framebuffer - GLint scissor_box[4] = {0, 0, 0, 0}; - glGetIntegerv(GL_SCISSOR_BOX, scissor_box); - - // Setup default framebuffer - default_framebuffer = new framebuffer(); - default_framebuffer->gl_framebuffer_id = 0; - default_framebuffer->dimensions = {scissor_box[2], scissor_box[3]}; - - // Bind default framebuffer - bound_framebuffer = default_framebuffer; -} - -rasterizer::~rasterizer() -{ - delete default_framebuffer; -} - -void rasterizer::context_resized(int width, int height) -{ - default_framebuffer->dimensions = {width, height}; -} - -void rasterizer::use_framebuffer(const gl::framebuffer& framebuffer) -{ - if (bound_framebuffer != &framebuffer) - { - glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.gl_framebuffer_id); - bound_framebuffer = &framebuffer; - } -} - -void rasterizer::set_clear_color(float r, float g, float b, float a) -{ - glClearColor(r, g, b, a); -} - -void rasterizer::set_clear_depth(float depth) -{ - glClearDepth(depth); -} - -void rasterizer::set_clear_stencil(int s) -{ - glClearStencil(s); -} - -void rasterizer::clear_framebuffer(bool color, bool depth, bool stencil) -{ - GLbitfield mask = 0; - - if (color) - mask |= GL_COLOR_BUFFER_BIT; - if (depth) - mask |= GL_DEPTH_BUFFER_BIT; - if (stencil) - mask |= GL_STENCIL_BUFFER_BIT; - - glClear(mask); -} - -void rasterizer::set_viewport(int x, int y, int width, int height) -{ - glViewport(x, y, static_cast(width), static_cast(height)); -} - -void rasterizer::use_program(const shader_program& program) -{ - if (bound_shader_program != &program) - { - glUseProgram(program.gl_program_id); - bound_shader_program = &program; - } -} - -void rasterizer::draw_arrays(const vertex_array& vao, drawing_mode mode, std::size_t offset, std::size_t count) -{ - GLenum gl_mode = drawing_mode_lut[static_cast(mode)]; - - if (bound_vao != &vao) - { - glBindVertexArray(vao.gl_array_id); - bound_vao = &vao; - } - - glDrawArrays(gl_mode, static_cast(offset), static_cast(count)); -} - -void rasterizer::draw_arrays_instanced(const vertex_array& vao, drawing_mode mode, std::size_t offset, std::size_t count, std::size_t instance_count) -{ - GLenum gl_mode = drawing_mode_lut[static_cast(mode)]; - - if (bound_vao != &vao) - { - glBindVertexArray(vao.gl_array_id); - bound_vao = &vao; - } - - glDrawArraysInstanced(gl_mode, static_cast(offset), static_cast(count), static_cast(instance_count)); -} - -void rasterizer::draw_elements(const vertex_array& vao, drawing_mode mode, std::size_t offset, std::size_t count, element_array_type type) -{ - GLenum gl_mode = drawing_mode_lut[static_cast(mode)]; - GLenum gl_type = element_array_type_lut[static_cast(type)]; - - if (bound_vao != &vao) - { - glBindVertexArray(vao.gl_array_id); - bound_vao = &vao; - } - - glDrawElements(gl_mode, static_cast(count), gl_type, (const GLvoid*)offset); -} - -} // namespace gl diff --git a/src/gl/shader-input.cpp b/src/gl/shader-input.cpp deleted file mode 100644 index 73448db..0000000 --- a/src/gl/shader-input.cpp +++ /dev/null @@ -1,773 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "gl/shader-input.hpp" -#include "gl/texture-1d.hpp" -#include "gl/texture-2d.hpp" -#include "gl/texture-3d.hpp" -#include "gl/texture-cube.hpp" -#include - -namespace gl { - -shader_input::shader_input(shader_program* program, std::size_t input_index, int gl_uniform_location, const std::string& name, shader_variable_type data_type, std::size_t element_count, int texture_unit): - program(program), - input_index(input_index), - gl_uniform_location(gl_uniform_location), - name(name), - data_type(data_type), - element_count(element_count), - texture_unit(texture_unit) -{} - -shader_input::~shader_input() -{} - -bool shader_input::upload(const bool& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform1i(gl_uniform_location, static_cast(value)); - return true; -} - -bool shader_input::upload(const bool2& value) const -{ - if (gl_uniform_location == -1) - return false; - - const GLint values[] = {value[0], value[1]}; - glUniform2iv(gl_uniform_location, 1, values); - return true; -} - -bool shader_input::upload(const bool3& value) const -{ - if (gl_uniform_location == -1) - return false; - - const GLint values[] = {value[0], value[1], value[2]}; - glUniform3iv(gl_uniform_location, 1, values); - return true; -} - -bool shader_input::upload(const bool4& value) const -{ - if (gl_uniform_location == -1) - return false; - - const GLint values[] = {value[0], value[1], value[2], value[3]}; - glUniform4iv(gl_uniform_location, 1, values); - return true; -} - -bool shader_input::upload(const int& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform1i(gl_uniform_location, value); - return true; -} - -bool shader_input::upload(const int2& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform2iv(gl_uniform_location, 1, value.data()); - return true; -} - -bool shader_input::upload(const int3& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform3iv(gl_uniform_location, 1, value.data()); - return true; -} - -bool shader_input::upload(const int4& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform4iv(gl_uniform_location, 1, value.data()); - return true; -} - -bool shader_input::upload(const unsigned int& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform1ui(gl_uniform_location, value); - return true; -} - -bool shader_input::upload(const uint2& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform2uiv(gl_uniform_location, 1, value.data()); - return true; -} - -bool shader_input::upload(const uint3& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform3uiv(gl_uniform_location, 1, value.data()); - return true; -} - -bool shader_input::upload(const uint4& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform4uiv(gl_uniform_location, 1, value.data()); - return true; -} - -bool shader_input::upload(const float& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform1f(gl_uniform_location, value); - return true; -} - -bool shader_input::upload(const float2& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform2fv(gl_uniform_location, 1, value.data()); - return true; -} - -bool shader_input::upload(const float3& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform3fv(gl_uniform_location, 1, value.data()); - return true; -} - -bool shader_input::upload(const float4& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform4fv(gl_uniform_location, 1, value.data()); - return true; -} - -bool shader_input::upload(const float2x2& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniformMatrix2fv(gl_uniform_location, 1, GL_FALSE, value[0].data()); - return true; -} - -bool shader_input::upload(const float3x3& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniformMatrix3fv(gl_uniform_location, 1, GL_FALSE, value[0].data()); - return true; -} - -bool shader_input::upload(const float4x4& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniformMatrix4fv(gl_uniform_location, 1, GL_FALSE, value[0].data()); - return true; -} - -bool shader_input::upload(const texture_1d* value) const -{ - if (gl_uniform_location == -1) - return false; - - // Bind texture to a texture unit reserved by this shader input - glActiveTexture(GL_TEXTURE0 + texture_unit); - glBindTexture(GL_TEXTURE_1D, value->gl_texture_id); - - // Upload texture unit index to shader - glUniform1i(gl_uniform_location, texture_unit); - - return true; -} - -bool shader_input::upload(const texture_2d* value) const -{ - if (gl_uniform_location == -1) - return false; - - // Bind texture to a texture unit reserved by this shader input - glActiveTexture(GL_TEXTURE0 + texture_unit); - glBindTexture(GL_TEXTURE_2D, value->gl_texture_id); - - // Upload texture unit index to shader - glUniform1i(gl_uniform_location, texture_unit); - - return true; -} - -bool shader_input::upload(const texture_3d* value) const -{ - if (gl_uniform_location == -1) - return false; - - // Bind texture to a texture unit reserved by this shader input - glActiveTexture(GL_TEXTURE0 + texture_unit); - glBindTexture(GL_TEXTURE_3D, value->gl_texture_id); - - // Upload texture unit index to shader - glUniform1i(gl_uniform_location, texture_unit); - - return true; -} - -bool shader_input::upload(const texture_cube* value) const -{ - if (gl_uniform_location == -1) - return false; - - // Bind texture to a texture unit reserved by this shader input - glActiveTexture(GL_TEXTURE0 + texture_unit); - glBindTexture(GL_TEXTURE_CUBE_MAP, value->gl_texture_id); - - // Upload texture unit index to shader - glUniform1i(gl_uniform_location, texture_unit); - - return true; -} - -bool shader_input::upload(std::size_t index, const bool& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform1i(gl_uniform_location + static_cast(index), static_cast(value)); - return true; -} - -bool shader_input::upload(std::size_t index, const bool2& value) const -{ - if (gl_uniform_location == -1) - return false; - - const GLint values[] = {value[0], value[1]}; - glUniform2iv(gl_uniform_location + static_cast(index), 1, values); - return true; -} - -bool shader_input::upload(std::size_t index, const bool3& value) const -{ - if (gl_uniform_location == -1) - return false; - - const GLint values[] = {value[0], value[1], value[3]}; - glUniform3iv(gl_uniform_location + static_cast(index), 1, values); - return true; -} - -bool shader_input::upload(std::size_t index, const bool4& value) const -{ - if (gl_uniform_location == -1) - return false; - - const GLint values[] = {value[0], value[1], value[3], value[4]}; - glUniform4iv(gl_uniform_location + static_cast(index), 1, values); - return true; -} - -bool shader_input::upload(std::size_t index, const int& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform1i(gl_uniform_location + static_cast(index), value); - return true; -} - -bool shader_input::upload(std::size_t index, const int2& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform2iv(gl_uniform_location + static_cast(index), 1, value.data()); - return true; -} - -bool shader_input::upload(std::size_t index, const int3& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform3iv(gl_uniform_location + static_cast(index), 1, value.data()); - return true; -} - -bool shader_input::upload(std::size_t index, const int4& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform4iv(gl_uniform_location + static_cast(index), 1, value.data()); - return true; -} - -bool shader_input::upload(std::size_t index, const unsigned int& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform1ui(gl_uniform_location + static_cast(index), value); - return true; -} - -bool shader_input::upload(std::size_t index, const uint2& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform2uiv(gl_uniform_location + static_cast(index), 1, value.data()); - return true; -} - -bool shader_input::upload(std::size_t index, const uint3& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform3uiv(gl_uniform_location + static_cast(index), 1, value.data()); - return true; -} - -bool shader_input::upload(std::size_t index, const uint4& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform4uiv(gl_uniform_location + static_cast(index), 1, value.data()); - return true; -} - -bool shader_input::upload(std::size_t index, const float& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform1f(gl_uniform_location + static_cast(index), value); - return true; -} - -bool shader_input::upload(std::size_t index, const float2& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform2fv(gl_uniform_location + static_cast(index), 1, value.data()); - return true; -} - -bool shader_input::upload(std::size_t index, const float3& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform3fv(gl_uniform_location + static_cast(index), 1, value.data()); - return true; -} - -bool shader_input::upload(std::size_t index, const float4& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform4fv(gl_uniform_location + static_cast(index), 1, value.data()); - return true; -} - -bool shader_input::upload(std::size_t index, const float2x2& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniformMatrix2fv(gl_uniform_location + static_cast(index) * 2, 1, GL_FALSE, value[0].data()); - return true; -} - -bool shader_input::upload(std::size_t index, const float3x3& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniformMatrix3fv(gl_uniform_location + static_cast(index) * 3, 1, GL_FALSE, value[0].data()); - return true; -} - -bool shader_input::upload(std::size_t index, const float4x4& value) const -{ - if (gl_uniform_location == -1) - return false; - - glUniformMatrix4fv(gl_uniform_location + static_cast(index) * 4, 1, GL_FALSE, value[0].data()); - return true; -} - -bool shader_input::upload(std::size_t index, const texture_1d* value) const -{ - if (gl_uniform_location == -1) - return false; - - // Bind texture to a texture unit reserved by this shader input - glActiveTexture(GL_TEXTURE0 + texture_unit + static_cast(index)); - glBindTexture(GL_TEXTURE_1D, value->gl_texture_id); - - // Upload texture unit index to shader - glUniform1i(gl_uniform_location + static_cast(index), texture_unit + static_cast(index)); - - return true; -} - -bool shader_input::upload(std::size_t index, const texture_2d* value) const -{ - if (gl_uniform_location == -1) - return false; - - // Bind texture to a texture unit reserved by this shader input - glActiveTexture(GL_TEXTURE0 + texture_unit + static_cast(index)); - glBindTexture(GL_TEXTURE_2D, value->gl_texture_id); - - // Upload texture unit index to shader - glUniform1i(gl_uniform_location + static_cast(index), texture_unit + static_cast(index)); - - return true; -} - -bool shader_input::upload(std::size_t index, const texture_3d* value) const -{ - if (gl_uniform_location == -1) - return false; - - // Bind texture to a texture unit reserved by this shader input - glActiveTexture(GL_TEXTURE0 + texture_unit + static_cast(index)); - glBindTexture(GL_TEXTURE_3D, value->gl_texture_id); - - // Upload texture unit index to shader - glUniform1i(gl_uniform_location + static_cast(index), texture_unit + static_cast(index)); - - return true; -} - -bool shader_input::upload(std::size_t index, const texture_cube* value) const -{ - if (gl_uniform_location == -1) - return false; - - // Bind texture to a texture unit reserved by this shader input - glActiveTexture(GL_TEXTURE0 + texture_unit + static_cast(index)); - glBindTexture(GL_TEXTURE_CUBE_MAP, value->gl_texture_id); - - // Upload texture unit index to shader - glUniform1i(gl_uniform_location + static_cast(index), texture_unit + static_cast(index)); - - return true; -} - -bool shader_input::upload(std::size_t index, const bool* values, std::size_t count) const -{ - if (gl_uniform_location == -1) - return false; - - int* int_values = new int[count]; - for (std::size_t i = 0; i < count; ++i) - int_values[i] = values[i]; - glUniform1iv(gl_uniform_location + static_cast(index), static_cast(count), &(*int_values)); - delete[] int_values; - - return true; -} - -bool shader_input::upload(std::size_t index, const bool2* values, std::size_t count) const -{ - if (gl_uniform_location == -1) - return false; - - int2* int2_values = new int2[count]; - for (std::size_t i = 0; i < count; ++i) - int2_values[i] = {values[i][0], values[i][1]}; - glUniform2iv(gl_uniform_location + static_cast(index), static_cast(count), &((*int2_values)[0])); - delete[] int2_values; - - return true; -} - -bool shader_input::upload(std::size_t index, const bool3* values, std::size_t count) const -{ - if (gl_uniform_location == -1) - return false; - - int3* int3_values = new int3[count]; - for (std::size_t i = 0; i < count; ++i) - int3_values[i] = {values[i][0], values[i][1], values[i][2]}; - glUniform3iv(gl_uniform_location + static_cast(index), static_cast(count), &((*int3_values)[0])); - delete[] int3_values; - - return true; -} - -bool shader_input::upload(std::size_t index, const bool4* values, std::size_t count) const -{ - if (gl_uniform_location == -1) - return false; - - int4* int4_values = new int4[count]; - for (std::size_t i = 0; i < count; ++i) - int4_values[i] = {values[i][0], values[i][1], values[i][2], values[i][3]}; - glUniform4iv(gl_uniform_location + static_cast(index), static_cast(count), &((*int4_values)[0])); - delete[] int4_values; - - return true; -} - -bool shader_input::upload(std::size_t index, const int* values, std::size_t count) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform1iv(gl_uniform_location + static_cast(index), static_cast(count), &(*values)); - return true; -} - -bool shader_input::upload(std::size_t index, const int2* values, std::size_t count) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform2iv(gl_uniform_location + static_cast(index), static_cast(count), (*values).data()); - return true; -} - -bool shader_input::upload(std::size_t index, const int3* values, std::size_t count) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform3iv(gl_uniform_location + static_cast(index), static_cast(count), (*values).data()); - return true; -} - -bool shader_input::upload(std::size_t index, const int4* values, std::size_t count) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform4iv(gl_uniform_location + static_cast(index), static_cast(count), (*values).data()); - return true; -} - -bool shader_input::upload(std::size_t index, const unsigned int* values, std::size_t count) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform1uiv(gl_uniform_location + static_cast(index), static_cast(count), &(*values)); - return true; -} - -bool shader_input::upload(std::size_t index, const uint2* values, std::size_t count) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform2uiv(gl_uniform_location + static_cast(index), static_cast(count), (*values).data()); - return true; -} - -bool shader_input::upload(std::size_t index, const uint3* values, std::size_t count) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform3uiv(gl_uniform_location + static_cast(index), static_cast(count), (*values).data()); - return true; -} - -bool shader_input::upload(std::size_t index, const uint4* values, std::size_t count) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform4uiv(gl_uniform_location + static_cast(index), static_cast(count), (*values).data()); - return true; -} - -bool shader_input::upload(std::size_t index, const float* values, std::size_t count) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform1fv(gl_uniform_location + static_cast(index), static_cast(count), &(*values)); - return true; -} - -bool shader_input::upload(std::size_t index, const float2* values, std::size_t count) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform2fv(gl_uniform_location + static_cast(index), static_cast(count), (*values).data()); - return true; -} - -bool shader_input::upload(std::size_t index, const float3* values, std::size_t count) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform3fv(gl_uniform_location + static_cast(index), static_cast(count), (*values).data()); - return true; -} - -bool shader_input::upload(std::size_t index, const float4* values, std::size_t count) const -{ - if (gl_uniform_location == -1) - return false; - - glUniform4fv(gl_uniform_location + static_cast(index), static_cast(count), (*values).data()); - return true; -} - -bool shader_input::upload(std::size_t index, const float2x2* values, std::size_t count) const -{ - if (gl_uniform_location == -1) - return false; - - glUniformMatrix2fv(gl_uniform_location + static_cast(index) * 2, static_cast(count), GL_FALSE, (*values)[0].data()); - return true; -} - -bool shader_input::upload(std::size_t index, const float3x3* values, std::size_t count) const -{ - if (gl_uniform_location == -1) - return false; - - glUniformMatrix3fv(gl_uniform_location + static_cast(index) * 3, static_cast(count), GL_FALSE, (*values)[0].data()); - return true; -} - -bool shader_input::upload(std::size_t index, const float4x4* values, std::size_t count) const -{ - if (gl_uniform_location == -1) - return false; - - glUniformMatrix4fv(gl_uniform_location + static_cast(index) * 4, static_cast(count), GL_FALSE, (*values)[0].data()); - return true; -} - -bool shader_input::upload(std::size_t index, const texture_1d** values, std::size_t count) const -{ - if (gl_uniform_location == -1) - return false; - - for (std::size_t i = 0; i < count; ++i) - { - // Bind texture to a texture unit reserved by this shader input - glActiveTexture(GL_TEXTURE0 + texture_unit + static_cast(index + i)); - glBindTexture(GL_TEXTURE_1D, values[i]->gl_texture_id); - - // Upload texture unit index to shader - glUniform1i(gl_uniform_location + static_cast(index + i), texture_unit + static_cast(index + i)); - } - - return true; -} - -bool shader_input::upload(std::size_t index, const texture_2d** values, std::size_t count) const -{ - if (gl_uniform_location == -1) - return false; - - for (std::size_t i = 0; i < count; ++i) - { - // Bind texture to a texture unit reserved by this shader input - glActiveTexture(GL_TEXTURE0 + texture_unit + static_cast(index + i)); - glBindTexture(GL_TEXTURE_2D, values[i]->gl_texture_id); - - // Upload texture unit index to shader - glUniform1i(gl_uniform_location + static_cast(index + i), texture_unit + static_cast(index + i)); - } - - return true; -} - -bool shader_input::upload(std::size_t index, const texture_3d** values, std::size_t count) const -{ - if (gl_uniform_location == -1) - return false; - - for (std::size_t i = 0; i < count; ++i) - { - // Bind texture to a texture unit reserved by this shader input - glActiveTexture(GL_TEXTURE0 + texture_unit + static_cast(index + i)); - glBindTexture(GL_TEXTURE_3D, values[i]->gl_texture_id); - - // Upload texture unit index to shader - glUniform1i(gl_uniform_location + static_cast(index + i), texture_unit + static_cast(index + i)); - } - - return true; -} - -bool shader_input::upload(std::size_t index, const texture_cube** values, std::size_t count) const -{ - if (gl_uniform_location == -1) - return false; - - for (std::size_t i = 0; i < count; ++i) - { - // Bind texture to a texture unit reserved by this shader input - glActiveTexture(GL_TEXTURE0 + texture_unit + static_cast(index + i)); - glBindTexture(GL_TEXTURE_CUBE_MAP, values[i]->gl_texture_id); - - // Upload texture unit index to shader - glUniform1i(gl_uniform_location + static_cast(index + i), texture_unit + static_cast(index + i)); - } - - return true; -} - -} // namespace gl diff --git a/src/gl/shader-input.hpp b/src/gl/shader-input.hpp deleted file mode 100644 index a5159da..0000000 --- a/src/gl/shader-input.hpp +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GL_SHADER_INPUT_HPP -#define ANTKEEPER_GL_SHADER_INPUT_HPP - -#include "utility/fundamental-types.hpp" -#include - -namespace gl { - -class shader_program; -class texture_1d; -class texture_2d; -class texture_3d; -class texture_cube; -enum class shader_variable_type; - -/** - * Port through which data can be uploaded to shader variables. - */ -class shader_input -{ -public: - /** - * Returns the type of data which can be passed through this input. - */ - shader_variable_type get_data_type() const; - - /** - * Returns `true` if the input data is stored in an array. - */ - bool is_array() const; - - /** - * Returns the number of elements the array can contain, or `1` if the data is not stored in an array. - */ - std::size_t get_element_count() const; - - /** - * Uploads a value to the shader. - * - * @param value Value to upload. - * @return `true` if the value was uploaded successfully, `false` otherwise. - */ - ///@{ - bool upload(const bool& value) const; - bool upload(const bool2& value) const; - bool upload(const bool3& value) const; - bool upload(const bool4& value) const; - bool upload(const int& value) const; - bool upload(const int2& value) const; - bool upload(const int3& value) const; - bool upload(const int4& value) const; - bool upload(const unsigned int& value) const; - bool upload(const uint2& value) const; - bool upload(const uint3& value) const; - bool upload(const uint4& value) const; - bool upload(const float& value) const; - bool upload(const float2& value) const; - bool upload(const float3& value) const; - bool upload(const float4& value) const; - bool upload(const float2x2& value) const; - bool upload(const float3x3& value) const; - bool upload(const float4x4& value) const; - bool upload(const texture_1d* value) const; - bool upload(const texture_2d* value) const; - bool upload(const texture_3d* value) const; - bool upload(const texture_cube* value) const; - ///@} - - /** - * Uploads a single array element to the shader. - * - * @param index Index of an array element. - * @param values Value to upload. - * @return `true` if the value was uploaded successfully, `false` otherwise. - */ - ///@{ - bool upload(std::size_t index, const bool& value) const; - bool upload(std::size_t index, const bool2& value) const; - bool upload(std::size_t index, const bool3& value) const; - bool upload(std::size_t index, const bool4& value) const; - bool upload(std::size_t index, const int& value) const; - bool upload(std::size_t index, const int2& value) const; - bool upload(std::size_t index, const int3& value) const; - bool upload(std::size_t index, const int4& value) const; - bool upload(std::size_t index, const unsigned int& value) const; - bool upload(std::size_t index, const uint2& value) const; - bool upload(std::size_t index, const uint3& value) const; - bool upload(std::size_t index, const uint4& value) const; - bool upload(std::size_t index, const float& value) const; - bool upload(std::size_t index, const float2& value) const; - bool upload(std::size_t index, const float3& value) const; - bool upload(std::size_t index, const float4& value) const; - bool upload(std::size_t index, const float2x2& value) const; - bool upload(std::size_t index, const float3x3& value) const; - bool upload(std::size_t index, const float4x4& value) const; - bool upload(std::size_t index, const texture_1d* value) const; - bool upload(std::size_t index, const texture_2d* value) const; - bool upload(std::size_t index, const texture_3d* value) const; - bool upload(std::size_t index, const texture_cube* value) const; - ///@} - - /** - * Uploads a range of array elements to the shader. - * - * @param index Index of the first array element. - * @param values Pointer to an array of values. - * @param count Number of elements to upload. - * @return `true` if the value was fed successfully, `false` otherwise. - */ - ///@{ - bool upload(std::size_t index, const bool* values, std::size_t count) const; - bool upload(std::size_t index, const bool2* values, std::size_t count) const; - bool upload(std::size_t index, const bool3* values, std::size_t count) const; - bool upload(std::size_t index, const bool4* values, std::size_t count) const; - bool upload(std::size_t index, const int* values, std::size_t count) const; - bool upload(std::size_t index, const int2* values, std::size_t count) const; - bool upload(std::size_t index, const int3* values, std::size_t count) const; - bool upload(std::size_t index, const int4* values, std::size_t count) const; - bool upload(std::size_t index, const unsigned int* values, std::size_t count) const; - bool upload(std::size_t index, const uint2* values, std::size_t count) const; - bool upload(std::size_t index, const uint3* values, std::size_t count) const; - bool upload(std::size_t index, const uint4* values, std::size_t count) const; - bool upload(std::size_t index, const float* values, std::size_t count) const; - bool upload(std::size_t index, const float2* values, std::size_t count) const; - bool upload(std::size_t index, const float3* values, std::size_t count) const; - bool upload(std::size_t index, const float4* values, std::size_t count) const; - bool upload(std::size_t index, const float2x2* values, std::size_t count) const; - bool upload(std::size_t index, const float3x3* values, std::size_t count) const; - bool upload(std::size_t index, const float4x4* values, std::size_t count) const; - bool upload(std::size_t index, const texture_1d** values, std::size_t count) const; - bool upload(std::size_t index, const texture_2d** values, std::size_t count) const; - bool upload(std::size_t index, const texture_3d** values, std::size_t count) const; - bool upload(std::size_t index, const texture_cube** values, std::size_t count) const; - ///@} - -private: - friend class shader_program; - - /** - * Creates a shader input. - * - * @param program Shader program with which this input is associated. - * @param gl_uniform_location Location of the shader uniform with which this shader input is associated. - * @param name Name of the input. - * @param data_type Type of data which can be passed through this input. - * @param element_count Number of elements which the array can contain, or `0` if input data is not stored in an array. - * @param texture_unit Texture unit to which texture shader variables can be bound, or `-1` if the data type is not a texture type. - */ - shader_input(shader_program* program, std::size_t input_index, int gl_uniform_location, const std::string& name, shader_variable_type data_type, std::size_t element_count, int texture_unit); - - /** - * Destroys a shader input. - */ - ~shader_input(); - - shader_program* program; - std::size_t input_index; - int gl_uniform_location; - std::string name; - shader_variable_type data_type; - std::size_t element_count; - int texture_unit; -}; - -inline shader_variable_type shader_input::get_data_type() const -{ - return data_type; -} - -inline bool shader_input::is_array() const -{ - return (element_count > 1); -} - -inline std::size_t shader_input::get_element_count() const -{ - return element_count; -} - -} // namespace gl - -#endif // ANTKEEPER_GL_SHADER_INPUT_HPP - diff --git a/src/gl/shader-object.cpp b/src/gl/shader-object.cpp deleted file mode 100644 index 7424dc3..0000000 --- a/src/gl/shader-object.cpp +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "gl/shader-object.hpp" -#include -#include - -namespace gl { - -static constexpr GLenum gl_shader_type_lut[] = -{ - GL_VERTEX_SHADER, - GL_FRAGMENT_SHADER, - GL_GEOMETRY_SHADER -}; - -shader_object::shader_object(shader_stage stage): - gl_shader_id(0), - stage(stage), - compiled(false) -{ - // Look up OpenGL shader type enumeration that corresponds to the given stage - GLenum gl_shader_type = gl_shader_type_lut[static_cast(stage)]; - - // Create an OpenGL shader object - gl_shader_id = glCreateShader(gl_shader_type); - - // Handle OpenGL errors - if (!gl_shader_id) - { - throw std::runtime_error("An error occurred while creating an OpenGL shader object."); - } -} - -shader_object::~shader_object() -{ - // Flag the OpenGL shader object for deletion - glDeleteShader(gl_shader_id); -} - -void shader_object::source(const std::string& source_code) -{ - // Replace OpenGL shader object source code - GLint gl_length = static_cast(source_code.length()); - const GLchar* gl_string = source_code.c_str(); - glShaderSource(gl_shader_id, 1, &gl_string, &gl_length); - - // Handle OpenGL errors - switch (glGetError()) - { - case GL_INVALID_VALUE: - throw std::runtime_error("OpenGL shader object handle is not a value generated by OpenGL."); - break; - - case GL_INVALID_OPERATION: - throw std::runtime_error("OpenGL shader object handle is not a shader object."); - break; - } -} - -bool shader_object::compile() -{ - // Compile OpenGL shader object - glCompileShader(gl_shader_id); - - // Handle OpenGL errors - switch (glGetError()) - { - case GL_INVALID_VALUE: - throw std::runtime_error("OpenGL shader object handle is not a value generated by OpenGL."); - break; - - case GL_INVALID_OPERATION: - throw std::runtime_error("OpenGL shader object handle is not a shader object."); - break; - } - - // Get OpenGL shader object compilation status - GLint gl_compile_status; - glGetShaderiv(gl_shader_id, GL_COMPILE_STATUS, &gl_compile_status); - compiled = (gl_compile_status == GL_TRUE); - - // Get OpenGL shader object info log length - GLint gl_info_log_length; - glGetShaderiv(gl_shader_id, GL_INFO_LOG_LENGTH, &gl_info_log_length); - - if (gl_info_log_length > 0) - { - // Resize string to accommodate OpenGL shader object info log - info_log.resize(gl_info_log_length); - - // Read OpenGL shader object info log into string - glGetShaderInfoLog(gl_shader_id, gl_info_log_length, &gl_info_log_length, info_log.data()); - - // Remove redundant null terminator from string - info_log.pop_back(); - } - else - { - // Empty info log - info_log.clear(); - } - - // Return compilation status - return compiled; -} - -} // namespace gl diff --git a/src/gl/shader-object.hpp b/src/gl/shader-object.hpp deleted file mode 100644 index 1ce0e5f..0000000 --- a/src/gl/shader-object.hpp +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GL_SHADER_OBJECT_HPP -#define ANTKEEPER_GL_SHADER_OBJECT_HPP - -#include "gl/shader-stage.hpp" -#include -#include - -namespace gl { - -class shader_program; - -/** - * Shader object which can be compiled and linked to a shader program. - * - * @see gl::shader_program - * @see gl::shader_stage - */ -class shader_object -{ -public: - /** - * Creates an empty shader object for the specified shader stage. - * - * @param stage Shader stage in which this shader object will be used. - * - * @exception std::runtime_error An error occurred while creating an OpenGL shader object. - */ - shader_object(shader_stage stage); - - /** - * Destroys a shader object. - */ - ~shader_object(); - - /** - * Replaces the source code of the shader object. - * - * @param source_code String containing shader object source code. - * - * @exception std::runtime_error Shader object handle is not a value generated by OpenGL. - * @exception std::runtime_error Shader object handle is not a shader object. - */ - void source(const std::string& source_code); - - /** - * Compiles the shader object. - * - * @return `true` if the shader object was compiled successfully, `false` otherwise. If compilation fails, check the info log via shader_object::get_info_log() for more information. - * - * @exception std::runtime_error Shader object handle is not a value generated by OpenGL. - * @exception std::runtime_error Shader object handle is not a shader object. - * - * @see shader_object::get_info_log() - */ - bool compile(); - - /// Returns the shader stage of this shader object. - shader_stage get_stage() const; - - /// Returns the shader object info log, which is updated when the shader object is compiled. - const std::string& get_info_log() const; - - /// Returns `true` if the shader object has been successfully compiled, `false` otherwise. - bool was_compiled() const; - - shader_object(const shader_object&) = delete; - shader_object& operator=(const shader_object&) = delete; - -private: - friend class shader_program; - - unsigned int gl_shader_id; - shader_stage stage; - std::string info_log; - bool compiled; -}; - -inline shader_stage shader_object::get_stage() const -{ - return stage; -} - -inline const std::string& shader_object::get_info_log() const -{ - return info_log; -} - -inline bool shader_object::was_compiled() const -{ - return compiled; -} - -} // namespace gl - -#endif // ANTKEEPER_GL_SHADER_OBJECT_HPP diff --git a/src/gl/shader-program.cpp b/src/gl/shader-program.cpp deleted file mode 100644 index 74c2378..0000000 --- a/src/gl/shader-program.cpp +++ /dev/null @@ -1,339 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "gl/shader-program.hpp" -#include "gl/shader-object.hpp" -#include "gl/shader-variable-type.hpp" -#include "gl/shader-input.hpp" -#include -#include - -namespace gl { - -shader_program::shader_program(): - gl_program_id(0), - linked(false) -{ - // Create an OpenGL shader program - gl_program_id = glCreateProgram(); - - // Handle OpenGL errors - if (!gl_program_id) - { - throw std::runtime_error("An error occurred while creating an OpenGL shader program."); - } -} - -shader_program::~shader_program() -{ - // Delete shader inputs - free_inputs(); - - // Detach all shader objects - detach_all(); - - // Delete the OpenGL shader program - glDeleteProgram(gl_program_id); -} - -void shader_program::attach(const shader_object* object) -{ - if (attached_objects.find(object) != attached_objects.end()) - { - throw std::runtime_error("Shader object is already attached to the shader program."); - } - else - { - // Check that both the OpenGL shader program and OpenGL shader object are valid - if (glIsProgram(gl_program_id) != GL_TRUE) - throw std::runtime_error("OpenGL shader program is not a valid program object."); - if (glIsShader(object->gl_shader_id) != GL_TRUE) - throw std::runtime_error("OpenGL shader object is not a valid shader object."); - - // Attach the OpenGL shader object to the OpenGL shader program - glAttachShader(gl_program_id, object->gl_shader_id); - - // Handle OpenGL errors - switch (glGetError()) - { - case GL_INVALID_OPERATION: - throw std::runtime_error("OpenGL shader object is already attached to the shader program."); - break; - } - - // Add shader object to set of attached objects - attached_objects.insert(object); - } -} - -void shader_program::detach(const shader_object* object) -{ - if (attached_objects.find(object) == attached_objects.end()) - { - throw std::runtime_error("Shader object is not attached to the shader program."); - } - else - { - // Check that both the OpenGL shader program and OpenGL shader object are valid - if (glIsProgram(gl_program_id) != GL_TRUE) - throw std::runtime_error("OpenGL shader program is not a valid program object."); - if (glIsShader(object->gl_shader_id) != GL_TRUE) - throw std::runtime_error("OpenGL shader object is not a valid shader object."); - - // Detach the OpenGL shader object from the OpenGL shader program - glDetachShader(gl_program_id, object->gl_shader_id); - - // Handle OpenGL errors - switch (glGetError()) - { - case GL_INVALID_OPERATION: - throw std::runtime_error("OpenGL shader object is not attached to the shader program."); - break; - } - - // Remove shader object from set of attached objects - attached_objects.erase(object); - } -} - -void shader_program::detach_all() -{ - while (!attached_objects.empty()) - detach(*attached_objects.begin()); -} - -bool shader_program::link() -{ - // Check that the OpenGL shader program is valid - if (glIsProgram(gl_program_id) != GL_TRUE) - throw std::runtime_error("OpenGL shader program is not a valid program object."); - - // Link OpenGL shader program - glLinkProgram(gl_program_id); - - // Handle OpenGL errors - switch (glGetError()) - { - case GL_INVALID_OPERATION: - throw std::runtime_error("OpenGL shader program is the currently active program object and transform feedback mode is active."); - break; - } - - // Get OpenGL shader program linking status - GLint gl_link_status; - glGetProgramiv(gl_program_id, GL_LINK_STATUS, &gl_link_status); - linked = (gl_link_status == GL_TRUE); - - // Get OpenGL shader program info log length - GLint gl_info_log_length; - glGetProgramiv(gl_program_id, GL_INFO_LOG_LENGTH, &gl_info_log_length); - - if (gl_info_log_length > 0) - { - // Resize string to accommodate OpenGL shader program info log - info_log.resize(gl_info_log_length); - - // Read OpenGL shader program info log into string - glGetProgramInfoLog(gl_program_id, gl_info_log_length, &gl_info_log_length, info_log.data()); - - // Remove redundant null terminator from string - info_log.pop_back(); - } - else - { - // Empty info log - info_log.clear(); - } - - // Find shader inputs - find_inputs(); - - return linked; -} - -void shader_program::find_inputs() -{ - // Get maximum uniform name length - GLint max_uniform_name_length = 0; - glGetProgramiv(gl_program_id, GL_ACTIVE_UNIFORM_MAX_LENGTH, &max_uniform_name_length); - - // Allocate uniform name buffer - GLchar* uniform_name = new GLchar[max_uniform_name_length]; - - // Get number of active uniforms in the shader - GLint active_uniform_count = 0; - glGetProgramiv(gl_program_id, GL_ACTIVE_UNIFORMS, &active_uniform_count); - - // Set first available texture unit - int available_texture_unit = 0; - - // For each active uniform - for (GLuint uniform_index = 0; uniform_index < static_cast(active_uniform_count); ++uniform_index) - { - // Get information about uniform - GLsizei uniform_name_length; - GLint uniform_size; - GLenum uniform_type; - glGetActiveUniform(gl_program_id, uniform_index, static_cast(max_uniform_name_length), &uniform_name_length, &uniform_size, &uniform_type, &uniform_name[0]); - - // Get name without array symbols - std::string input_name = uniform_name; - std::size_t bracketPos = input_name.find_first_of("["); - if (bracketPos != std::string::npos) - { - input_name = input_name.substr(0, bracketPos); - } - - // Determine corresponding shader variable data type - shader_variable_type variable_type; - int texture_unit = -1; - bool unsupported = false; - switch (uniform_type) - { - case GL_BOOL: - variable_type = shader_variable_type::bool1; - break; - case GL_BOOL_VEC2: - variable_type = shader_variable_type::bool2; - break; - case GL_BOOL_VEC3: - variable_type = shader_variable_type::bool3; - break; - case GL_BOOL_VEC4: - variable_type = shader_variable_type::bool4; - break; - - case GL_INT: - variable_type = shader_variable_type::int1; - break; - case GL_INT_VEC2: - variable_type = shader_variable_type::int2; - break; - case GL_INT_VEC3: - variable_type = shader_variable_type::int3; - break; - case GL_INT_VEC4: - variable_type = shader_variable_type::int4; - break; - - case GL_UNSIGNED_INT: - variable_type = shader_variable_type::uint1; - break; - case GL_UNSIGNED_INT_VEC2: - variable_type = shader_variable_type::uint2; - break; - case GL_UNSIGNED_INT_VEC3: - variable_type = shader_variable_type::uint3; - break; - case GL_UNSIGNED_INT_VEC4: - variable_type = shader_variable_type::uint4; - break; - - case GL_FLOAT: - variable_type = shader_variable_type::float1; - break; - case GL_FLOAT_VEC2: - variable_type = shader_variable_type::float2; - break; - case GL_FLOAT_VEC3: - variable_type = shader_variable_type::float3; - break; - case GL_FLOAT_VEC4: - variable_type = shader_variable_type::float4; - break; - - case GL_FLOAT_MAT2: - variable_type = shader_variable_type::float2x2; - break; - case GL_FLOAT_MAT3: - variable_type = shader_variable_type::float3x3; - break; - case GL_FLOAT_MAT4: - variable_type = shader_variable_type::float4x4; - break; - - case GL_SAMPLER_1D: - case GL_SAMPLER_1D_SHADOW: - variable_type = shader_variable_type::texture_1d; - texture_unit = available_texture_unit; - available_texture_unit += uniform_size; - break; - - case GL_SAMPLER_2D: - case GL_SAMPLER_2D_SHADOW: - variable_type = shader_variable_type::texture_2d; - texture_unit = available_texture_unit; - available_texture_unit += uniform_size; - break; - - case GL_SAMPLER_3D: - variable_type = shader_variable_type::texture_3d; - texture_unit = available_texture_unit; - available_texture_unit += uniform_size; - break; - - case GL_SAMPLER_CUBE: - variable_type = shader_variable_type::texture_cube; - texture_unit = available_texture_unit; - available_texture_unit += uniform_size; - break; - - default: - unsupported = true; - break; - } - - // Check if data type is supported - if (unsupported) - { - std::string message = std::string("Shader uniform \"") + std::string(uniform_name) + std::string("\" has unsupported data type."); - throw std::runtime_error(message.c_str()); - } - - // Get uniform location - GLint uniform_location = glGetUniformLocation(gl_program_id, uniform_name); - if (uniform_location == -1) - { - //std::string message = std::string("Unable to get location for uniform \"") + std::string(uniform_name) + std::string("\""); - //throw std::runtime_error(message.c_str()); - } - else - { - // Create new shader input - shader_input* input = new shader_input(this, inputs.size(), uniform_location, input_name, variable_type, uniform_size, texture_unit); - input_map[input_name] = input; - inputs.push_back(input); - } - } - - // Free uniform name buffer - delete[] uniform_name; -} - -void shader_program::free_inputs() -{ - for (shader_input* input: inputs) - { - delete input; - } - - inputs.clear(); -} - -} // namespace gl diff --git a/src/gl/texture-1d.cpp b/src/gl/texture-1d.cpp deleted file mode 100644 index a3272c9..0000000 --- a/src/gl/texture-1d.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "gl/texture-1d.hpp" - -namespace gl { - -texture_1d::texture_1d(std::uint16_t width, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data): - texture(width, type, format, color_space, data) -{} - -texture_1d::~texture_1d() -{} - -void texture_1d::resize(std::uint16_t width, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data) -{ - texture::resize(width, type, format, color_space, data); -} - -void texture_1d::set_wrapping(gl::texture_wrapping wrap_s) -{ - texture::set_wrapping(wrap_s); -} - -} // namespace gl diff --git a/src/gl/texture-1d.hpp b/src/gl/texture-1d.hpp deleted file mode 100644 index 17548fe..0000000 --- a/src/gl/texture-1d.hpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GL_TEXTURE_1D_HPP -#define ANTKEEPER_GL_TEXTURE_1D_HPP - -#include "gl/texture.hpp" - -namespace gl { - -/** - * A 1D texture which can be uploaded to shaders via shader inputs. - */ -class texture_1d: public texture -{ -public: - /// @copydoc texture::texture(std::uint16_t, gl::pixel_type, gl::pixel_format, gl::color_space, const void*) - texture_1d(std::uint16_t width, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const void* data = nullptr); - - /// Destructs a 1D texture. - virtual ~texture_1d(); - - /// @copydoc texture::resize(std::uint16_t, gl::pixel_type, gl::pixel_format, gl::color_space, const void*) - virtual void resize(std::uint16_t width, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data); - - /// @copydoc texture::set_wrapping(gl::texture_wrapping) - virtual void set_wrapping(gl::texture_wrapping wrap_s); -}; - -} // namespace gl - -#endif // ANTKEEPER_GL_TEXTURE_1D_HPP diff --git a/src/gl/texture-2d.cpp b/src/gl/texture-2d.cpp deleted file mode 100644 index 440d8d9..0000000 --- a/src/gl/texture-2d.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "gl/texture-2d.hpp" - -namespace gl { - -texture_2d::texture_2d(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data): - texture(width, height, type, format, color_space, data) -{} - -texture_2d::~texture_2d() -{} - -void texture_2d::resize(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data) -{ - texture::resize(width, height, type, format, color_space, data); -} - -void texture_2d::resize(std::uint16_t width, std::uint16_t height, const void* data) -{ - texture::resize(width, height, get_pixel_type(), get_pixel_format(), get_color_space(), data); -} - -void texture_2d::set_wrapping(gl::texture_wrapping wrap_s, texture_wrapping wrap_t) -{ - texture::set_wrapping(wrap_s, wrap_t); -} - -} // namespace gl diff --git a/src/gl/texture-2d.hpp b/src/gl/texture-2d.hpp deleted file mode 100644 index d4bdf5a..0000000 --- a/src/gl/texture-2d.hpp +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GL_TEXTURE_2D_HPP -#define ANTKEEPER_GL_TEXTURE_2D_HPP - -#include "gl/texture.hpp" - -namespace gl { - -/** - * A 2D texture which can be uploaded to shaders via shader inputs. - */ -class texture_2d: public texture -{ -public: - /// @copydoc texture::texture(std::uint16_t, std::uint16_t, gl::pixel_type, gl::pixel_format, gl::color_space, const void*) - texture_2d(std::uint16_t width, std::uint16_t height, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const void* data = nullptr); - - /// Destructs a 2D texture. - virtual ~texture_2d(); - - /// @copydoc texture::resize(std::uint16_t, std::uint16_t, gl::pixel_type, gl::pixel_format, gl::color_space, const void*) - virtual void resize(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data); - - /** - * Resizes the texture. - * - * @param width Texture width, in pixels. - * @param height Texture height, in pixels. - * @param data Pointer to pixel data. - */ - void resize(std::uint16_t width, std::uint16_t height, const void* data); - - /// @copydoc texture::set_wrapping(gl::texture_wrapping, gl::texture_wrapping) - virtual void set_wrapping(gl::texture_wrapping wrap_s, texture_wrapping wrap_t); -}; - -} // namespace gl - -#endif // ANTKEEPER_GL_TEXTURE_2D_HPP diff --git a/src/gl/texture-3d.cpp b/src/gl/texture-3d.cpp deleted file mode 100644 index 543053a..0000000 --- a/src/gl/texture-3d.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "gl/texture-3d.hpp" - -namespace gl { - -texture_3d::texture_3d(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data): - texture(width, height, depth, type, format, color_space, data) -{} - -texture_3d::~texture_3d() -{} - -void texture_3d::resize(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data) -{ - texture::resize(width, height, depth, type, format, color_space, data); -} - -void texture_3d::set_wrapping(gl::texture_wrapping wrap_s, texture_wrapping wrap_t, texture_wrapping wrap_r) -{ - texture::set_wrapping(wrap_s, wrap_t, wrap_r); -} - -} // namespace gl diff --git a/src/gl/texture-3d.hpp b/src/gl/texture-3d.hpp deleted file mode 100644 index e12db6a..0000000 --- a/src/gl/texture-3d.hpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GL_TEXTURE_3D_HPP -#define ANTKEEPER_GL_TEXTURE_3D_HPP - -#include "gl/texture.hpp" - -namespace gl { - -/** - * A 3D texture which can be uploaded to shaders via shader inputs. - */ -class texture_3d: public texture -{ -public: - /// @copydoc texture::texture(std::uint16_t, std::uint16_t, std::uint16_t, gl::pixel_type, gl::pixel_format, gl::color_space, const void*) - texture_3d(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const void* data = nullptr); - - /// Destructs a 3D texture. - virtual ~texture_3d(); - - /// @copydoc texture::resize(std::uint16_t, std::uint16_t, std::uint16_t, gl::pixel_type, gl::pixel_format, gl::color_space, const void*) - virtual void resize(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data); - - /// @copydoc texture::set_wrapping(gl::texture_wrapping, gl::texture_wrapping, gl::texture_wrapping) - virtual void set_wrapping(gl::texture_wrapping wrap_s, texture_wrapping wrap_t, texture_wrapping wrap_r); -}; - -} // namespace gl - -#endif // ANTKEEPER_GL_TEXTURE_3D_HPP diff --git a/src/gl/texture-cube.cpp b/src/gl/texture-cube.cpp deleted file mode 100644 index 85215ff..0000000 --- a/src/gl/texture-cube.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "gl/texture-cube.hpp" -#include - -namespace gl { - -texture_cube::texture_cube(): - gl_texture_id(0), - face_size(0) -{ - glGenTextures(1, &gl_texture_id); -} - -texture_cube::~texture_cube() -{ - glDeleteTextures(1, &gl_texture_id); -} - -} // namespace gl \ No newline at end of file diff --git a/src/gl/texture.cpp b/src/gl/texture.cpp deleted file mode 100644 index dbbbe7c..0000000 --- a/src/gl/texture.cpp +++ /dev/null @@ -1,267 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "gl/texture.hpp" -#include "gl/texture-wrapping.hpp" -#include "gl/texture-filter.hpp" -#include -#include - -namespace gl { - -static constexpr GLenum pixel_format_lut[] = -{ - GL_DEPTH_COMPONENT, - GL_DEPTH_STENCIL, - GL_RED, - GL_RG, - GL_RGB, - GL_BGR, - GL_RGBA, - GL_BGRA -}; - -static constexpr GLenum pixel_type_lut[] = -{ - GL_BYTE, - GL_UNSIGNED_BYTE, - GL_SHORT, - GL_UNSIGNED_SHORT, - GL_INT, - GL_UNSIGNED_INT, - GL_HALF_FLOAT, - GL_FLOAT -}; - -static constexpr GLenum linear_internal_format_lut[][8] = -{ - {GL_NONE, GL_NONE, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT32, GL_DEPTH_COMPONENT32, GL_NONE, GL_DEPTH_COMPONENT32F}, - - // Note: GL_DEPTH32F_STENCIL8 is actually a 64-bit format, 32 depth bits, 8 stencil bits, and 24 alignment bits. - {GL_NONE, GL_NONE, GL_NONE, GL_NONE, GL_DEPTH24_STENCIL8, GL_DEPTH24_STENCIL8, GL_NONE, GL_DEPTH32F_STENCIL8}, - - {GL_R8, GL_R8, GL_R16, GL_R16, GL_R32F, GL_R32F, GL_R16F, GL_R32F}, - {GL_RG8, GL_RG8, GL_RG16, GL_RG16, GL_RG32F, GL_RG32F, GL_RG16F, GL_RG32F}, - {GL_RGB8, GL_RGB8, GL_RGB16, GL_RGB16, GL_RGB32F, GL_RGB32F, GL_RGB16F, GL_RGB32F}, - {GL_RGB8, GL_RGB8, GL_RGB16, GL_RGB16, GL_RGB32F, GL_RGB32F, GL_RGB16F, GL_RGB32F}, - {GL_RGBA8, GL_RGBA8, GL_RGBA16, GL_RGBA16, GL_RGBA32F, GL_RGBA32F, GL_RGBA16F, GL_RGBA32F}, - {GL_RGBA8, GL_RGBA8, GL_RGBA16, GL_RGBA16, GL_RGBA32F, GL_RGBA32F, GL_RGBA16F, GL_RGBA32F} -}; - -static constexpr GLenum srgb_internal_format_lut[][8] = -{ - {GL_NONE, GL_NONE, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT32, GL_DEPTH_COMPONENT32, GL_NONE, GL_DEPTH_COMPONENT32F}, - {GL_NONE, GL_NONE, GL_NONE, GL_NONE, GL_DEPTH24_STENCIL8, GL_DEPTH24_STENCIL8, GL_NONE, GL_DEPTH32F_STENCIL8}, - {GL_SRGB8, GL_SRGB8, GL_R16, GL_R16, GL_R32F, GL_R32F, GL_R16F, GL_R32F}, - {GL_SRGB8, GL_SRGB8, GL_RG16, GL_RG16, GL_RG32F, GL_RG32F, GL_RG16F, GL_RG32F}, - {GL_SRGB8, GL_SRGB8, GL_RGB16, GL_RGB16, GL_RGB32F, GL_RGB32F, GL_RGB16F, GL_RGB32F}, - {GL_SRGB8, GL_SRGB8, GL_RGB16, GL_RGB16, GL_RGB32F, GL_RGB32F, GL_RGB16F, GL_RGB32F}, - {GL_SRGB8_ALPHA8, GL_SRGB8_ALPHA8, GL_RGBA16, GL_RGBA16, GL_RGBA32F, GL_RGBA32F, GL_RGBA16F, GL_RGBA32F}, - {GL_SRGB8_ALPHA8, GL_SRGB8_ALPHA8, GL_RGBA16, GL_RGBA16, GL_RGBA32F, GL_RGBA32F, GL_RGBA16F, GL_RGBA32F} -}; - -static constexpr GLint swizzle_mask_lut[][4] = -{ - {GL_RED, GL_RED, GL_RED, GL_ONE}, - {GL_RED, GL_GREEN, GL_ZERO, GL_ONE}, - {GL_RED, GL_RED, GL_RED, GL_ONE}, - {GL_RED, GL_RED, GL_RED, GL_GREEN}, - {GL_RED, GL_GREEN, GL_BLUE, GL_ONE}, - {GL_RED, GL_GREEN, GL_BLUE, GL_ONE}, - {GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA}, - {GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA} -}; - -static constexpr GLenum wrapping_lut[] = -{ - GL_CLAMP_TO_BORDER, - GL_CLAMP_TO_EDGE, - GL_REPEAT, - GL_MIRRORED_REPEAT -}; - -static constexpr GLenum min_filter_lut[] = -{ - GL_NEAREST, - GL_LINEAR, - GL_NEAREST_MIPMAP_NEAREST, - GL_LINEAR_MIPMAP_NEAREST, - GL_NEAREST_MIPMAP_LINEAR, - GL_LINEAR_MIPMAP_LINEAR -}; - -static constexpr GLenum mag_filter_lut[] = -{ - GL_NEAREST, - GL_LINEAR -}; - -texture::texture(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data): - gl_texture_target((depth) ? GL_TEXTURE_3D : (height) ? GL_TEXTURE_2D : GL_TEXTURE_1D), - gl_texture_id(0), - dimensions({0, 0, 0}), - wrapping({texture_wrapping::repeat, texture_wrapping::repeat, texture_wrapping::repeat}), - filters({texture_min_filter::linear_mipmap_linear, texture_mag_filter::linear}), - max_anisotropy(0.0f) -{ - glGenTextures(1, &gl_texture_id); - resize(width, height, depth, type, format, color_space, data); - set_wrapping(wrapping[0], wrapping[1], wrapping[2]); - set_filters(std::get<0>(filters), std::get<1>(filters)); - set_max_anisotropy(max_anisotropy); -} - -texture::texture(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data): - texture(width, height, 0, type, format, color_space, data) -{} - -texture::texture(std::uint16_t width, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data): - texture(width, 0, 0, type, format, color_space, data) -{} - -texture::~texture() -{ - glDeleteTextures(1, &gl_texture_id); -} - -void texture::set_filters(texture_min_filter min_filter, texture_mag_filter mag_filter) -{ - filters = {min_filter, mag_filter}; - - GLenum gl_min_filter = min_filter_lut[static_cast(min_filter)]; - GLenum gl_mag_filter = mag_filter_lut[static_cast(mag_filter)]; - - glBindTexture(gl_texture_target, gl_texture_id); - glTexParameteri(gl_texture_target, GL_TEXTURE_MIN_FILTER, gl_min_filter); - glTexParameteri(gl_texture_target, GL_TEXTURE_MAG_FILTER, gl_mag_filter); -} - -void texture::set_max_anisotropy(float anisotropy) -{ - max_anisotropy = std::max(0.0f, std::min(1.0f, anisotropy)); - - // Get the maximum supported anisotropy value - float gl_max_texture_max_anisotropy; - glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &gl_max_texture_max_anisotropy); - - // Lerp between 1.0 and GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT - float gl_max_anisotropy = 1.0f + max_anisotropy * (gl_max_texture_max_anisotropy - 1.0f); - - glBindTexture(gl_texture_target, gl_texture_id); - glTexParameterf(gl_texture_target, GL_TEXTURE_MAX_ANISOTROPY_EXT, gl_max_anisotropy); -} - -void texture::set_wrapping(gl::texture_wrapping wrap_s, gl::texture_wrapping wrap_t, gl::texture_wrapping wrap_r) -{ - wrapping = {wrap_s, wrap_t, wrap_r}; - - GLenum gl_wrap_s = wrapping_lut[static_cast(wrap_s)]; - GLenum gl_wrap_t = wrapping_lut[static_cast(wrap_t)]; - GLenum gl_wrap_r = wrapping_lut[static_cast(wrap_r)]; - - glBindTexture(gl_texture_target, gl_texture_id); - glTexParameteri(gl_texture_target, GL_TEXTURE_WRAP_S, gl_wrap_s); - glTexParameteri(gl_texture_target, GL_TEXTURE_WRAP_T, gl_wrap_t); - glTexParameteri(gl_texture_target, GL_TEXTURE_WRAP_R, gl_wrap_r); -} - -void texture::set_wrapping(gl::texture_wrapping wrap_s, gl::texture_wrapping wrap_t) -{ - std::get<0>(wrapping) = wrap_s; - std::get<1>(wrapping) = wrap_t; - - GLenum gl_wrap_s = wrapping_lut[static_cast(wrap_s)]; - GLenum gl_wrap_t = wrapping_lut[static_cast(wrap_t)]; - - glBindTexture(gl_texture_target, gl_texture_id); - glTexParameteri(gl_texture_target, GL_TEXTURE_WRAP_S, gl_wrap_s); - glTexParameteri(gl_texture_target, GL_TEXTURE_WRAP_T, gl_wrap_t); -} - -void texture::set_wrapping(gl::texture_wrapping wrap_s) -{ - std::get<0>(wrapping) = wrap_s; - - GLenum gl_wrap_s = wrapping_lut[static_cast(wrap_s)]; - - glBindTexture(gl_texture_target, gl_texture_id); - glTexParameteri(gl_texture_target, GL_TEXTURE_WRAP_S, gl_wrap_s); -} - -void texture::resize(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data) -{ - dimensions = {width, height, depth}; - pixel_type = type; - pixel_format = format; - this->color_space = color_space; - - GLenum gl_internal_format; - if (color_space == gl::color_space::srgb) - { - gl_internal_format = srgb_internal_format_lut[static_cast(format)][static_cast(type)]; - } - else - { - gl_internal_format = linear_internal_format_lut[static_cast(format)][static_cast(type)]; - } - - GLenum gl_format = pixel_format_lut[static_cast(format)]; - const GLint* gl_swizzle_mask = swizzle_mask_lut[static_cast(format)]; - - GLenum gl_type = pixel_type_lut[static_cast(type)]; - - // Special cases for depth + stencil pixel formats - if (gl_internal_format == GL_DEPTH24_STENCIL8) - gl_type = GL_UNSIGNED_INT_24_8; - else if (gl_internal_format == GL_DEPTH32F_STENCIL8) - gl_type = GL_FLOAT_32_UNSIGNED_INT_24_8_REV; - - glPixelStorei(GL_PACK_ALIGNMENT, 1); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - - glBindTexture(gl_texture_target, gl_texture_id); - if (depth) - glTexImage3D(gl_texture_target, 0, gl_internal_format, width, height, depth, 0, gl_format, gl_type, data); - else if (height) - glTexImage2D(gl_texture_target, 0, gl_internal_format, width, height, 0, gl_format, gl_type, data); - else if (width) - glTexImage1D(gl_texture_target, 0, gl_internal_format, width, 0, gl_format, gl_type, data); - - glGenerateMipmap(gl_texture_target); - glTexParameteriv(gl_texture_target, GL_TEXTURE_SWIZZLE_RGBA, gl_swizzle_mask); - - /// TODO: remove this - if (format == pixel_format::d) - { - glTexParameteri(gl_texture_target, GL_TEXTURE_COMPARE_FUNC, GL_LESS); - glTexParameteri(gl_texture_target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); - } -} - -void texture::resize(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data) -{ - resize(width, height, 0, type, format, color_space, data); -} - -void texture::resize(std::uint16_t width, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data) -{ - resize(width, 0, 0, type, format, color_space, data); -} - -} // namespace gl diff --git a/src/gl/texture.hpp b/src/gl/texture.hpp deleted file mode 100644 index 4f990cb..0000000 --- a/src/gl/texture.hpp +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GL_TEXTURE_HPP -#define ANTKEEPER_GL_TEXTURE_HPP - -#include "gl/color-space.hpp" -#include "gl/pixel-format.hpp" -#include "gl/pixel-type.hpp" -#include "gl/texture-filter.hpp" -#include "gl/texture-wrapping.hpp" -#include -#include -#include - -namespace gl { - -class framebuffer; -class shader_input; - -/** - * Abstract base class for 1D, 2D, 3D, and cube textures which can be uploaded to shaders via shader inputs. - */ -class texture -{ -public: - /** - * Constructs a texture. - * - * @param width Texture width, in pixels. - * @param height Texture height, in pixels. For 2D or 3D textures. - * @param depth Texture depth, in pixels. For 3D textures only. - * @param type Pixel component data type. - * @param format Pixel format. - * @param color_space Color space of the pixel data. - * @param data Pointer to pixel data. - * - * @warning If the sRGB color space is specified, pixel data will be stored internally as 8 bits per channel, and automatically converted to linear space before reading. - */ - /// @{ - texture(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const void* data = nullptr); - texture(std::uint16_t width, std::uint16_t height, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const void* data = nullptr); - texture(std::uint16_t width, gl::pixel_type type = gl::pixel_type::uint_8, gl::pixel_format format = gl::pixel_format::rgba, gl::color_space color_space = gl::color_space::linear, const void* data = nullptr); - /// @} - - /** - * Destructs a texture. - */ - virtual ~texture() = 0; - - /** - * Sets the texture filtering modes. - * - * @param min_filter Texture minification filter mode. - * @param mag_filter Texture magnification filter mode. - */ - void set_filters(texture_min_filter min_filter, texture_mag_filter mag_filter); - - /** - * Sets the maximum anisotropy. - * - * @param level Max anisotropy on `[0.0, 1.0]`, with `0.0` indicating normal filtering, and `1.0` indicating maximum anisotropic filtering. - */ - void set_max_anisotropy(float anisotropy); - - /// Returns the dimensions of the texture, in pixels. - const std::array& get_dimensions() const; - - /// Returns the width of the texture, in pixels. - const std::uint16_t& get_width() const; - - /// Returns the height of the texture, in pixels. - const std::uint16_t& get_height() const; - - /// Returns the depth of the texture, in pixels. - const std::uint16_t& get_depth() const; - - /// Returns the pixel type enumeration. - const pixel_type& get_pixel_type() const; - - /// Returns the pixel format enumeration. - const pixel_format& get_pixel_format() const; - - /// Returns the color space enumeration. - const color_space& get_color_space() const; - - /// Returns the wrapping modes of the texture. - const std::array& get_wrapping() const; - - /// Returns the filtering modes of the texture. - const std::tuple& get_filters() const; - - /// Returns the maximum anisotropy. - float get_max_anisotropy() const; - -protected: - /** - * Sets the texture wrapping modes. - * - * @param wrap_s Wrapping mode for s-coordinates. - * @param wrap_t Wrapping mode for t-coordinates. - * @param wrap_r Wrapping mode for r-coordinates. - */ - /// @{ - virtual void set_wrapping(gl::texture_wrapping wrap_s, gl::texture_wrapping wrap_t, gl::texture_wrapping wrap_r); - virtual void set_wrapping(gl::texture_wrapping wrap_s, gl::texture_wrapping wrap_t); - virtual void set_wrapping(gl::texture_wrapping wrap_s); - /// @} - - /** - * Resizes the texture. - * - * @param width Texture width, in pixels. - * @param height Texture height, in pixels. For 2D or 3D textures. - * @param depth Texture depth, in pixels. For 3D textures only. - * @param type Pixel component data type. - * @param format Pixel format. - * @param color_space Color space of the pixel data. - * @param data Pointer to pixel data. - * - * @warning If the sRGB color space is specified, pixel data will be stored internally as 8 bits per channel, and automatically converted to linear space before reading. - */ - /// @{ - virtual void resize(std::uint16_t width, std::uint16_t height, std::uint16_t depth, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data); - virtual void resize(std::uint16_t width, std::uint16_t height, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data); - virtual void resize(std::uint16_t width, gl::pixel_type type, gl::pixel_format format, gl::color_space color_space, const void* data); - /// @} - -private: - friend class framebuffer; - friend class shader_input; - - unsigned int gl_texture_target; - unsigned int gl_texture_id; - std::array dimensions; - gl::pixel_type pixel_type; - gl::pixel_format pixel_format; - gl::color_space color_space; - std::array wrapping; - std::tuple filters; - float max_anisotropy; -}; - -inline const std::array& texture::get_dimensions() const -{ - return dimensions; -} - -inline const std::uint16_t& texture::get_width() const -{ - return dimensions[0]; -} - -inline const std::uint16_t& texture::get_height() const -{ - return dimensions[1]; -} - -inline const std::uint16_t& texture::get_depth() const -{ - return dimensions[2]; -} - -inline const pixel_type& texture::get_pixel_type() const -{ - return pixel_type; -} - -inline const pixel_format& texture::get_pixel_format() const -{ - return pixel_format; -} - -inline const color_space& texture::get_color_space() const -{ - return color_space; -} - -inline const std::array& texture::get_wrapping() const -{ - return wrapping; -} - -inline const std::tuple& texture::get_filters() const -{ - return filters; -} - -inline float texture::get_max_anisotropy() const -{ - return max_anisotropy; -} - -} // namespace gl - -#endif // ANTKEEPER_GL_TEXTURE_HPP diff --git a/src/gl/vertex-array.cpp b/src/gl/vertex-array.cpp deleted file mode 100644 index a1ed873..0000000 --- a/src/gl/vertex-array.cpp +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "gl/vertex-array.hpp" -#include "gl/vertex-buffer.hpp" -#include -#include -#include - -namespace gl { - -static constexpr GLenum vertex_attribute_type_lut[] = -{ - GL_BYTE, - GL_UNSIGNED_BYTE, - GL_SHORT, - GL_UNSIGNED_SHORT, - GL_INT, - GL_UNSIGNED_INT, - GL_HALF_FLOAT, - GL_FLOAT, - GL_DOUBLE -}; - -vertex_array::vertex_array(): - gl_array_id(0) -{ - glGenVertexArrays(1, &gl_array_id); -} - -vertex_array::~vertex_array() -{ - glDeleteVertexArrays(1, &gl_array_id); -} - -void vertex_array::bind(attribute_location_type location, const vertex_attribute& attribute) -{ - if (attribute.buffer == nullptr) - { - throw std::invalid_argument("Cannot bind vertex attribute that has a null vertex buffer."); - } - - if (attribute.components < 1 || attribute.components > 4) - { - throw std::invalid_argument("Cannot bind vertex attribute that has an unsupported number of components (" + std::to_string(attribute.components) + ")"); - } - - attributes[location] = attribute; - - GLenum gl_type = vertex_attribute_type_lut[static_cast(attribute.type)]; - glBindVertexArray(gl_array_id); - glBindBuffer(GL_ARRAY_BUFFER, attribute.buffer->gl_buffer_id); - glVertexAttribPointer( - static_cast(location), - static_cast(attribute.components), - gl_type, - GL_FALSE, - static_cast(attribute.stride), - (const GLvoid*)attribute.offset); - glEnableVertexAttribArray(static_cast(location)); -} - -void vertex_array::unbind(attribute_location_type location) -{ - if (auto it = attributes.find(location); it != attributes.end()) - { - glBindVertexArray(gl_array_id); - glDisableVertexAttribArray(static_cast(location)); - - attributes.erase(it); - } - else - { - throw std::invalid_argument("Non-existent vertex attribute cannot be unbound."); - } -} - -const typename vertex_array::attribute_map_type& vertex_array::get_attributes() const -{ - return attributes; -} - -} // namespace gl diff --git a/src/gl/vertex-array.hpp b/src/gl/vertex-array.hpp deleted file mode 100644 index 46b20ad..0000000 --- a/src/gl/vertex-array.hpp +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GL_VERTEX_ARRAY_HPP -#define ANTKEEPER_GL_VERTEX_ARRAY_HPP - -#include -#include -#include "gl/vertex-attribute.hpp" - -namespace gl { - -class rasterizer; -class vertex_buffer; - -/** - * Vertex array object (VAO), which describes how vertex attributes are stored in vertex buffer objects (VBOs). - * - * @see gl::vertex_attribute - * @see gl::vertex_buffer - */ -class vertex_array -{ -public: - /// Vertex attribute binding location type. - typedef std::uint_fast32_t attribute_location_type; - - /// Maps vertex attribute to binding locations. - typedef std::unordered_map attribute_map_type; - - /// Constructs a vertex array. - vertex_array(); - - /// Destructs a vertex array. - ~vertex_array(); - - vertex_array(const vertex_array&) = delete; - vertex_array& operator=(const vertex_array&) = delete; - - /** - * Binds a vertex attribute to the vertex array. - * - * @param location Location to which the vertex attribute should be bound. - * @param attribute Vertex attribute to bind. - * - * @except std::invalid_argument Cannot bind vertex attribute that has a null vertex buffer. - * @except std::invalid_argument Cannot bind vertex attribute that has an unsupported number of components. - */ - void bind(attribute_location_type location, const vertex_attribute& attribute); - - /** - * Unbinds a vertex attribute from the vertex array. - * - * @param location Location of the vertex attribute to unbind. - * - * @except std::invalid_argument Non-existent vertex attribute cannot be unbound. - */ - void unbind(attribute_location_type location); - - /// Returns a const reference to the map of vertex attributes bound to this vertex array. - const attribute_map_type& get_attributes() const; - -private: - friend class rasterizer; - - attribute_map_type attributes; - std::uint_fast32_t gl_array_id; -}; - -} // namespace gl - -#endif // ANTKEEPER_GL_VERTEX_ARRAY_HPP diff --git a/src/gl/vertex-buffer.cpp b/src/gl/vertex-buffer.cpp deleted file mode 100644 index 719b681..0000000 --- a/src/gl/vertex-buffer.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "gl/vertex-buffer.hpp" -#include -#include - -namespace gl { - -static constexpr GLenum buffer_usage_lut[] = -{ - GL_STREAM_DRAW, - GL_STREAM_READ, - GL_STREAM_COPY, - GL_STATIC_DRAW, - GL_STATIC_READ, - GL_STATIC_COPY, - GL_DYNAMIC_DRAW, - GL_DYNAMIC_READ, - GL_DYNAMIC_COPY -}; - -vertex_buffer::vertex_buffer(std::size_t size, const void* data, buffer_usage usage): - gl_buffer_id(0), - size(size), - usage(usage) -{ - GLenum gl_usage = buffer_usage_lut[static_cast(usage)]; - glGenBuffers(1, &gl_buffer_id); - glBindBuffer(GL_ARRAY_BUFFER, gl_buffer_id); - glBufferData(GL_ARRAY_BUFFER, static_cast(size), data, gl_usage); -} - -vertex_buffer::vertex_buffer(): - vertex_buffer(0, nullptr, buffer_usage::static_draw) -{} - -vertex_buffer::~vertex_buffer() -{ - glDeleteBuffers(1, &gl_buffer_id); -} - -void vertex_buffer::repurpose(buffer_usage usage, std::size_t size, const void* data) -{ - this->size = size; - this->usage = usage; - - GLenum gl_usage = buffer_usage_lut[static_cast(usage)]; - glBindBuffer(GL_ARRAY_BUFFER, gl_buffer_id); - glBufferData(GL_ARRAY_BUFFER, static_cast(size), data, gl_usage); -} - -void vertex_buffer::resize(std::size_t size, const void* data) -{ - repurpose(usage, size, data); -} - -void vertex_buffer::write(std::size_t offset, std::size_t size, const void* data) -{ - // Abort empty write operations - if (!size) - return; - - // Bounds check - if (offset + size > this->size) - throw std::out_of_range("Vertex buffer write operation exceeded buffer bounds."); - - glBindBuffer(GL_ARRAY_BUFFER, gl_buffer_id); - glBufferSubData(GL_ARRAY_BUFFER, static_cast(offset), static_cast(size), data); -} - -void vertex_buffer::read(std::size_t offset, std::size_t size, void* data) const -{ - // Abort empty read operations - if (!size) - return; - - // Bounds check - if (offset + size > this->size) - throw std::out_of_range("Vertex buffer read operation exceeded buffer bounds."); - - glBindBuffer(GL_ARRAY_BUFFER, gl_buffer_id); - glGetBufferSubData(GL_ARRAY_BUFFER, static_cast(offset), static_cast(size), data); -} - -} // namespace gl diff --git a/src/gl/vertex-buffer.hpp b/src/gl/vertex-buffer.hpp deleted file mode 100644 index cd23e46..0000000 --- a/src/gl/vertex-buffer.hpp +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_GL_VERTEX_BUFFER_HPP -#define ANTKEEPER_GL_VERTEX_BUFFER_HPP - -#include -#include -#include "gl/buffer-usage.hpp" - -namespace gl { - -class vertex_array; - -/** - * Vertex buffer object (VBO). - */ -class vertex_buffer -{ -public: - /** - * Creates a vertex buffer, settings its size, uploading its data, and setting its usage hint. - * - * @param size Size of the buffer's data, in bytes. - * @param data Pointer to data that will be copied into the buffer, or `nullptr` if no data is to be copied. - * @param usage Buffer usage hint. - */ - explicit vertex_buffer(std::size_t size, const void* data = nullptr, buffer_usage usage = buffer_usage::static_draw); - - /// Creates an empty vertex buffer. - vertex_buffer(); - - /// Destroys a vertex buffer. - ~vertex_buffer(); - - vertex_buffer(const vertex_buffer&) = delete; - vertex_buffer& operator=(const vertex_buffer&) = delete; - - /** - * Repurposes a vertex buffer, changing its usage hint, its size, and replacing its data. - * - * @param usage New usage hint for the buffer. - * @param size New size of the buffer's data, in bytes. - * @param data Pointer to data that will be copied into the buffer, or `nullptr` if no data is to be copied. - */ - void repurpose(buffer_usage usage, std::size_t size, const void* data = nullptr); - - /** - * Resizes the vertex buffer. - * - * @param size New size of the buffer's data, in bytes. - * @param data Pointer to data that will be copied into the buffer, or `nullptr` if no data is to be copied. - */ - void resize(std::size_t size, const void* data = nullptr); - - /** - * Writes data into the vertex buffer. - * - * @param offset Offset into the buffer's data, in bytes, where writing will begin. - * @param size Size of the write operation, in bytes. - * @param data Pointer to the data that will be written. - * - * @except std::out_of_range Vertex buffer write operation exceeded buffer bounds. - */ - void write(std::size_t offset, std::size_t size, const void* data); - - /** - * Reads a subset of the buffer's data from the GL and returns it to the application. - * - * @param offset Offset into the buffer's data, in bytes, where reading will begin. - * @param size Size of the data to read, in bytes. - * @param data Pointer to where the read bytes will be stored. - * - * @except std::out_of_range Vertex buffer read operation exceeded buffer bounds. - */ - void read(std::size_t offset, std::size_t size, void* data) const; - - /// Returns the size of the buffer's data, in bytes. - std::size_t get_size() const; - - /// Return's the buffer's usage hint. - buffer_usage get_usage() const; - -private: - friend class vertex_array; - - std::uint_fast32_t gl_buffer_id; - std::size_t size; - buffer_usage usage; -}; - -inline std::size_t vertex_buffer::get_size() const -{ - return size; -} - -inline buffer_usage vertex_buffer::get_usage() const -{ - return usage; -} - -} // namespace gl - -#endif // ANTKEEPER_GL_VERTEX_BUFFER_HPP diff --git a/src/i18n/string-map.cpp b/src/i18n/string-map.cpp deleted file mode 100644 index 36b8a4f..0000000 --- a/src/i18n/string-map.cpp +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "i18n/string-map.hpp" -#include "utility/hash/fnv1a.hpp" -#include "resources/serializer.hpp" -#include "resources/serialize-error.hpp" -#include "resources/deserializer.hpp" -#include "resources/deserialize-error.hpp" -#include - -/** - * Serializes a string map. - * - * @param[in] map String map to serialize. - * @param[in,out] ctx Serialize context. - * - * @throw serialize_error Write error. - */ -template <> -void serializer::serialize(const i18n::string_map& map, serialize_context& ctx) -{ - // Write number of entries - std::uint32_t size = static_cast(map.size()); - ctx.write32(reinterpret_cast(&size), 1); - - // Write entries - for (const auto& [key, value]: map) - { - // Write key - ctx.write32(reinterpret_cast(&key), 1); - - // Write string length - std::uint32_t length = static_cast(value.length()); - ctx.write32(reinterpret_cast(&length), 1); - - // Write string - ctx.write8(reinterpret_cast(value.data()), length); - } -} - -/** - * Deserializes a string map. - * - * @param[out] map String map to deserialize. - * @param[in,out] ctx Deserialize context. - * - * @throw deserialize_error Read error. - */ -template <> -void deserializer::deserialize(i18n::string_map& map, deserialize_context& ctx) -{ - map.clear(); - - // Read number of entries - std::uint32_t size = 0; - ctx.read32(reinterpret_cast(&size), 1); - - // Read entries - for (std::uint32_t i = 0; i < size; ++i) - { - // Read key - std::uint32_t key = 0; - ctx.read32(reinterpret_cast(&key), 1); - - // Read string length - std::uint32_t length = 0; - ctx.read32(reinterpret_cast(&length), 1); - - // Insert empty string into map - auto [iterator, inserted] = map.emplace - ( - std::piecewise_construct, - std::forward_as_tuple(key), - std::forward_as_tuple(static_cast(length), '\0') - ); - - // Read string - ctx.read8(reinterpret_cast(iterator->second.data()), length); - } -} diff --git a/src/input/action-map.cpp b/src/input/action-map.cpp deleted file mode 100644 index 422830c..0000000 --- a/src/input/action-map.cpp +++ /dev/null @@ -1,401 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "input/action-map.hpp" -#include -#include -#include -#include - -namespace input { - -void action_map::connect(::event::queue& queue) -{ - if (subscriptions.empty()) - { - subscriptions.emplace_back(queue.subscribe(std::bind_front(&action_map::handle_gamepad_axis_moved, this))); - subscriptions.emplace_back(queue.subscribe(std::bind_front(&action_map::handle_gamepad_button_pressed, this))); - subscriptions.emplace_back(queue.subscribe(std::bind_front(&action_map::handle_gamepad_button_released, this))); - subscriptions.emplace_back(queue.subscribe(std::bind_front(&action_map::handle_key_pressed, this))); - subscriptions.emplace_back(queue.subscribe(std::bind_front(&action_map::handle_key_released, this))); - subscriptions.emplace_back(queue.subscribe(std::bind_front(&action_map::handle_mouse_button_pressed, this))); - subscriptions.emplace_back(queue.subscribe(std::bind_front(&action_map::handle_mouse_button_released, this))); - subscriptions.emplace_back(queue.subscribe(std::bind_front(&action_map::handle_mouse_moved, this))); - subscriptions.emplace_back(queue.subscribe(std::bind_front(&action_map::handle_mouse_scrolled, this))); - } -} - -void action_map::disconnect() -{ - subscriptions.clear(); -} - -void action_map::add_mapping(action& action, const mapping& mapping) -{ - switch (mapping.get_mapping_type()) - { - case mapping_type::gamepad_axis: - add_gamepad_axis_mapping(action, static_cast(mapping)); - break; - - case mapping_type::gamepad_button: - add_gamepad_button_mapping(action, static_cast(mapping)); - break; - - case mapping_type::key: - add_key_mapping(action, static_cast(mapping)); - break; - - case mapping_type::mouse_button: - add_mouse_button_mapping(action, static_cast(mapping)); - break; - - case mapping_type::mouse_motion: - add_mouse_motion_mapping(action, static_cast(mapping)); - break; - - case mapping_type::mouse_scroll: - add_mouse_scroll_mapping(action, static_cast(mapping)); - break; - - default: - //std::unreachable(); - break; - } -} - -void action_map::add_gamepad_axis_mapping(action& action, gamepad_axis_mapping mapping) -{ - gamepad_axis_mappings.emplace_back(&action, std::move(mapping)); -} - -void action_map::add_gamepad_button_mapping(action& action, gamepad_button_mapping mapping) -{ - gamepad_button_mappings.emplace_back(&action, std::move(mapping)); -} - -void action_map::add_key_mapping(action& action, key_mapping mapping) -{ - key_mappings.emplace_back(&action, std::move(mapping)); -} - -void action_map::add_mouse_button_mapping(action& action, mouse_button_mapping mapping) -{ - mouse_button_mappings.emplace_back(&action, std::move(mapping)); -} - -void action_map::add_mouse_motion_mapping(action& action, mouse_motion_mapping mapping) -{ - mouse_motion_mappings.emplace_back(&action, std::move(mapping)); -} - -void action_map::add_mouse_scroll_mapping(action& action, mouse_scroll_mapping mapping) -{ - mouse_scroll_mappings.emplace_back(&action, std::move(mapping)); -} - -void action_map::remove_mappings(const action& action, mapping_type type) -{ - auto predicate = [&](const auto& tuple) -> bool - { - return std::get<0>(tuple) == &action; - }; - - switch (type) - { - case mapping_type::gamepad_axis: - std::erase_if(gamepad_axis_mappings, predicate); - break; - - case mapping_type::gamepad_button: - std::erase_if(gamepad_button_mappings, predicate); - break; - - case mapping_type::key: - std::erase_if(key_mappings, predicate); - break; - - case mapping_type::mouse_button: - std::erase_if(mouse_button_mappings, predicate); - break; - - case mapping_type::mouse_motion: - std::erase_if(mouse_motion_mappings, predicate); - break; - - case mapping_type::mouse_scroll: - std::erase_if(mouse_scroll_mappings, predicate); - break; - - default: - //std::unreachable(); - break; - } -} - -void action_map::remove_mappings(const action& action) -{ - auto predicate = [&](const auto& tuple) -> bool - { - return std::get<0>(tuple) == &action; - }; - - std::erase_if(gamepad_axis_mappings, predicate); - std::erase_if(gamepad_button_mappings, predicate); - std::erase_if(key_mappings, predicate); - std::erase_if(mouse_button_mappings, predicate); - std::erase_if(mouse_motion_mappings, predicate); - std::erase_if(mouse_scroll_mappings, predicate); -} - -void action_map::remove_mappings() -{ - gamepad_axis_mappings.clear(); - gamepad_button_mappings.clear(); - key_mappings.clear(); - mouse_button_mappings.clear(); - mouse_motion_mappings.clear(); - mouse_scroll_mappings.clear(); -} - -void action_map::handle_gamepad_axis_moved(const gamepad_axis_moved_event& event) -{ - for (const auto& [action, mapping]: gamepad_axis_mappings) - { - if (mapping.axis == event.axis && - (!mapping.gamepad || mapping.gamepad == event.gamepad)) - { - if (std::signbit(event.position) == mapping.direction) - { - action->evaluate(std::abs(event.position)); - } - else - { - action->evaluate(0.0f); - } - } - } -} - -void action_map::handle_gamepad_button_pressed(const gamepad_button_pressed_event& event) -{ - for (const auto& [action, mapping]: gamepad_button_mappings) - { - if (mapping.button == event.button && - (!mapping.gamepad || mapping.gamepad == event.gamepad)) - { - action->evaluate(1.0f); - } - } -} - -void action_map::handle_gamepad_button_released(const gamepad_button_released_event& event) -{ - for (const auto& [action, mapping]: gamepad_button_mappings) - { - if (mapping.button == event.button && - (!mapping.gamepad || mapping.gamepad == event.gamepad)) - { - action->evaluate(0.0f); - } - } -} - -void action_map::handle_key_pressed(const key_pressed_event& event) -{ - for (const auto& [action, mapping]: key_mappings) - { - if (mapping.scancode == event.scancode && - (!mapping.keyboard || mapping.keyboard == event.keyboard) && - (!mapping.modifiers || (mapping.modifiers & event.modifiers))) - { - if (!event.repeat) - { - action->evaluate(1.0f); - } - else if (mapping.repeat) - { - action->evaluate(0.0f); - action->evaluate(1.0f); - } - } - } -} - -void action_map::handle_key_released(const key_released_event& event) -{ - for (const auto& [action, mapping]: key_mappings) - { - if (mapping.scancode == event.scancode && - (!mapping.keyboard || mapping.keyboard == event.keyboard)) - { - action->evaluate(0.0f); - } - } -} - -void action_map::handle_mouse_moved(const mouse_moved_event& event) -{ - for (const auto& [action, mapping]: mouse_motion_mappings) - { - if (!mapping.mouse || mapping.mouse == event.mouse) - { - const float difference = static_cast(event.difference[static_cast>(mapping.axis)]); - - if (difference && std::signbit(difference) == mapping.direction) - { - action->evaluate(std::abs(difference)); - action->evaluate(0.0f); - } - } - } -} - -void action_map::handle_mouse_scrolled(const mouse_scrolled_event& event) -{ - for (const auto& [action, mapping]: mouse_scroll_mappings) - { - if (!mapping.mouse || mapping.mouse == event.mouse) - { - const auto velocity = event.velocity[static_cast>(mapping.axis)]; - - if (velocity && std::signbit(velocity) == mapping.direction) - { - action->evaluate(std::abs(velocity)); - action->evaluate(0.0f); - } - } - } -} - -void action_map::handle_mouse_button_pressed(const mouse_button_pressed_event& event) -{ - for (const auto& [action, mapping]: mouse_button_mappings) - { - if (mapping.button == event.button && - (!mapping.mouse || mapping.mouse == event.mouse)) - { - action->evaluate(1.0f); - } - } -} - -void action_map::handle_mouse_button_released(const mouse_button_released_event& event) -{ - for (const auto& [action, mapping]: mouse_button_mappings) - { - if (mapping.button == event.button && - (!mapping.mouse || mapping.mouse == event.mouse)) - { - action->evaluate(0.0f); - } - } -} - -std::vector action_map::get_gamepad_axis_mappings(const action& action) const -{ - std::vector mappings; - - for (const auto& [mapped_action, mapping]: gamepad_axis_mappings) - { - if (mapped_action == &action) - { - mappings.emplace_back(mapping); - } - } - - return mappings; -} - -std::vector action_map::get_gamepad_button_mappings(const action& action) const -{ - std::vector mappings; - - for (const auto& [mapped_action, mapping]: gamepad_button_mappings) - { - if (mapped_action == &action) - { - mappings.emplace_back(mapping); - } - } - - return mappings; -} - -std::vector action_map::get_key_mappings(const action& action) const -{ - std::vector mappings; - - for (const auto& [mapped_action, mapping]: key_mappings) - { - if (mapped_action == &action) - { - mappings.emplace_back(mapping); - } - } - - return mappings; -} - -std::vector action_map::get_mouse_button_mappings(const action& action) const -{ - std::vector mappings; - - for (const auto& [mapped_action, mapping]: mouse_button_mappings) - { - if (mapped_action == &action) - { - mappings.emplace_back(mapping); - } - } - - return mappings; -} - -std::vector action_map::get_mouse_motion_mappings(const action& action) const -{ - std::vector mappings; - - for (const auto& [mapped_action, mapping]: mouse_motion_mappings) - { - if (mapped_action == &action) - { - mappings.emplace_back(mapping); - } - } - - return mappings; -} - -std::vector action_map::get_mouse_scroll_mappings(const action& action) const -{ - std::vector mappings; - - for (const auto& [mapped_action, mapping]: mouse_scroll_mappings) - { - if (mapped_action == &action) - { - mappings.emplace_back(mapping); - } - } - - return mappings; -} - -} // namespace input diff --git a/src/input/action-map.hpp b/src/input/action-map.hpp deleted file mode 100644 index 4128e6d..0000000 --- a/src/input/action-map.hpp +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEER_INPUT_ACTION_MAP_HPP -#define ANTKEEER_INPUT_ACTION_MAP_HPP - -#include "event/subscription.hpp" -#include "event/queue.hpp" -#include "input/action.hpp" -#include "input/gamepad-events.hpp" -#include "input/keyboard-events.hpp" -#include "input/mouse-events.hpp" -#include "input/mapping.hpp" -#include -#include -#include -#include - -namespace input { - -/** - * Maps input to a set of contextually-related actions. - */ -class action_map -{ -public: - /** - * Connects the input event signals of an event queue to the action map. - * - * @param queue Event queue to connect. - */ - void connect(::event::queue& queue); - - /** - * Disconnects all input event signals from the action map. - */ - void disconnect(); - - /** - * Maps input to an action. - * - * @param action Action to which input will be mapped. - * @param mapping Input mapping to add. - */ - /// @{ - void add_mapping(action& action, const mapping& mapping); - void add_gamepad_axis_mapping(action& action, gamepad_axis_mapping mapping); - void add_gamepad_button_mapping(action& action, gamepad_button_mapping mapping); - void add_key_mapping(action& action, key_mapping mapping); - void add_mouse_button_mapping(action& action, mouse_button_mapping mapping); - void add_mouse_motion_mapping(action& action, mouse_motion_mapping mapping); - void add_mouse_scroll_mapping(action& action, mouse_scroll_mapping mapping); - /// @} - - /** - * Unmaps input from an action. - * - * @param action Action from which input will be unmapped. - * @param type Type of input mapping to remove. - */ - void remove_mappings(const action& action, mapping_type type); - - /** - * Unmaps all input from an action. - * - * @param action Action from which input will be unmapped. - */ - void remove_mappings(const action& action); - - /** - * Unmaps all input from all actions in the action map. - */ - void remove_mappings(); - - /** - * Returns all of the gamepad axis mappings associated with an action. - * - * @param action Action with which associated mappings will be returned. - */ - std::vector get_gamepad_axis_mappings(const action& action) const; - - /** - * Returns all of the gamepad button mappings associated with an action. - * - * @param action Action with which associated mappings will be returned. - */ - std::vector get_gamepad_button_mappings(const action& action) const; - - /** - * Returns all of the key mappings associated with an action. - * - * @param action Action with which associated mappings will be returned. - */ - std::vector get_key_mappings(const action& action) const; - - /** - * Returns all of the mouse button mappings associated with an action. - * - * @param action Action with which associated mappings will be returned. - */ - std::vector get_mouse_button_mappings(const action& action) const; - - /** - * Returns all of the mouse motion mappings associated with an action. - * - * @param action Action with which associated mappings will be returned. - */ - std::vector get_mouse_motion_mappings(const action& action) const; - - /** - * Returns all of the mouse scroll associated with an action. - * - * @param action Action with which associated mappings will be returned. - */ - std::vector get_mouse_scroll_mappings(const action& action) const; - -private: - void handle_gamepad_axis_moved(const gamepad_axis_moved_event& event); - void handle_gamepad_button_pressed(const gamepad_button_pressed_event& event); - void handle_gamepad_button_released(const gamepad_button_released_event& event); - void handle_key_pressed(const key_pressed_event& event); - void handle_key_released(const key_released_event& event); - void handle_mouse_button_pressed(const mouse_button_pressed_event& event); - void handle_mouse_button_released(const mouse_button_released_event& event); - void handle_mouse_moved(const mouse_moved_event& event); - void handle_mouse_scrolled(const mouse_scrolled_event& event); - - std::vector> subscriptions; - std::vector> gamepad_axis_mappings; - std::vector> gamepad_button_mappings; - std::vector> key_mappings; - std::vector> mouse_button_mappings; - std::vector> mouse_motion_mappings; - std::vector> mouse_scroll_mappings; -}; - -} // namespace input - -#endif // ANTKEEER_INPUT_ACTION_MAP_HPP diff --git a/src/input/action.cpp b/src/input/action.cpp deleted file mode 100644 index 03f1ab3..0000000 --- a/src/input/action.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "input/action.hpp" - -namespace input { - -static bool default_threshold_function(float x) noexcept -{ - return x > 0.0f; -} - -action::action(): - threshold_function(default_threshold_function), - active(false), - activated_event{this}, - active_event{this, 0.0f}, - deactivated_event{this} -{} - -void action::set_threshold_function(const threshold_function_type& function) -{ - threshold_function = function; -} - -void action::evaluate(float value) -{ - // Store activation state - const bool was_active = active; - - // Re-evaluate activation state - active = threshold_function(value); - - // Emit events - if (active) - { - if (!was_active) - { - activated_publisher.publish(activated_event); - } - - active_event.input_value = value; - active_publisher.publish(active_event); - } - else - { - if (was_active) - { - deactivated_publisher.publish(deactivated_event); - } - } -} - -void action::reset() -{ - active = false; -} - -} // namespace input diff --git a/src/input/action.hpp b/src/input/action.hpp deleted file mode 100644 index af68cc6..0000000 --- a/src/input/action.hpp +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_INPUT_ACTION_HPP -#define ANTKEEPER_INPUT_ACTION_HPP - -#include "input/action-events.hpp" -#include "event/publisher.hpp" -#include - -namespace input { - -/** - * Evaluates an activation state given input values and publishes events on activation state changes. - */ -class action -{ -public: - /** - * Threshold function type. - * - * Given an input value, returns `true` if the action should be considered active, and `false` otherwise. - */ - typedef std::function threshold_function_type; - - /// Constructs an action. - action(); - - /** - * Sets the threshold function. - * - * @param function Threshold function. - */ - void set_threshold_function(const threshold_function_type& function); - - /** - * Evaluates the activation state of the action, according to its threshold function and an input value. - * - * @param value Input value. - */ - void evaluate(float value); - - /** - * Resets the activation state of the action without publishing any events. - */ - void reset(); - - /// Returns the threshold function. - [[nodiscard]] inline const threshold_function_type& get_threshold_function() const noexcept - { - return threshold_function; - } - - /// Returns `true` if the action is active, `false` otherwise. - [[nodiscard]] inline bool is_active() const noexcept - { - return active; - } - - /// Returns the channel through which action activated events are published. - [[nodiscard]] inline ::event::channel& get_activated_channel() noexcept - { - return activated_publisher.channel(); - } - - /// Returns the channel through which action active events are published. - [[nodiscard]] inline ::event::channel& get_active_channel() noexcept - { - return active_publisher.channel(); - } - - /// Returns the channel through which action deactivated events are published. - [[nodiscard]] inline ::event::channel& get_deactivated_channel() noexcept - { - return deactivated_publisher.channel(); - } - -private: - threshold_function_type threshold_function; - bool active; - - action_activated_event activated_event; - action_active_event active_event; - action_deactivated_event deactivated_event; - - ::event::publisher activated_publisher; - ::event::publisher active_publisher; - ::event::publisher deactivated_publisher; -}; - -} // namespace input - -#endif // ANTKEEPER_INPUT_ACTION_HPP diff --git a/src/input/device.cpp b/src/input/device.cpp deleted file mode 100644 index ca98295..0000000 --- a/src/input/device.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "input/device.hpp" - -namespace input { - -device::device(): - connected(true) -{} - -void device::connect() -{ - connected = true; - connected_publisher.publish({this}); -} - -void device::disconnect() -{ - connected = false; - disconnected_publisher.publish({this}); -} - -void device::set_uuid(const ::uuid& id) -{ - uuid = id; -} - -} // namespace input diff --git a/src/input/device.hpp b/src/input/device.hpp deleted file mode 100644 index 630352b..0000000 --- a/src/input/device.hpp +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_INPUT_DEVICE_HPP -#define ANTKEEPER_INPUT_DEVICE_HPP - -#include "input/device-type.hpp" -#include "input/device-events.hpp" -#include "event/publisher.hpp" -#include "utility/uuid.hpp" -#include -#include - -namespace input { - -/** - * Abstract base class for virtual devices that generate input events. - */ -class device -{ -public: - /// Constructs an input device. - device(); - - /// Destructs an input device. - virtual ~device() = default; - - /** - * Simulates the device being connected. - */ - void connect(); - - /** - * Simulates the device being disconnected. - * - * @note Disconnected devices can still generate input events. - */ - void disconnect(); - - /// Returns `true` if the device is currently connected. - [[nodiscard]] inline bool is_connected() const noexcept - { - return connected; - } - - /** - * Sets the universally unique identifier (UUID) of this input device. - * - * @param id UUID. - */ - void set_uuid(const ::uuid& id); - - /// Returns the universally unique identifier (UUID) of this input device. - [[nodiscard]] inline const ::uuid& get_uuid() const noexcept - { - return uuid; - } - - /// Returns the channel through which device connected events are published. - [[nodiscard]] inline ::event::channel& get_connected_channel() noexcept - { - return connected_publisher.channel(); - } - - /// Returns the channel through which device disconnected events are published. - [[nodiscard]] inline ::event::channel& get_disconnected_channel() noexcept - { - return disconnected_publisher.channel(); - } - - /// Returns the input device type. - [[nodiscard]] virtual constexpr device_type get_device_type() const noexcept = 0; - -private: - ::uuid uuid; - bool connected; - - ::event::publisher connected_publisher; - ::event::publisher disconnected_publisher; -}; - -} // namespace input - -#endif // ANTKEEPER_INPUT_DEVICE_HPP diff --git a/src/input/gamepad-events.hpp b/src/input/gamepad-events.hpp deleted file mode 100644 index b300d3f..0000000 --- a/src/input/gamepad-events.hpp +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_INPUT_GAMEPAD_EVENTS_HPP -#define ANTKEEPER_INPUT_GAMEPAD_EVENTS_HPP - -#include "input/gamepad-axis.hpp" -#include "input/gamepad-button.hpp" - -namespace input { - -class gamepad; - -/** - * Event generated when a gamepad button has been pressed. - */ -struct gamepad_button_pressed_event -{ - /// Gamepad that generated the event. - gamepad* gamepad; - - /// Gamepad button being pressed. - gamepad_button button; -}; - -/** - * Event generated when a gamepad button has been released. - */ -struct gamepad_button_released_event -{ - /// Gamepad that generated the event. - gamepad* gamepad; - - /// Gamepad button being released. - gamepad_button button; -}; - -/** - * Event generated when a gamepad axis has been moved. - */ -struct gamepad_axis_moved_event -{ - /// Gamepad that generated the event. - gamepad* gamepad; - - /// Gamepad axis being moved. - gamepad_axis axis; - - /// Position of the gamepad axis, on `[-1, 1]`. - float position; -}; - -} // namespace input - -#endif // ANTKEEPER_INPUT_GAMEPAD_EVENTS_HPP diff --git a/src/input/gamepad.cpp b/src/input/gamepad.cpp deleted file mode 100644 index 714a81d..0000000 --- a/src/input/gamepad.cpp +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "input/gamepad.hpp" -#include "math/map.hpp" -#include -#include -#include - -namespace input { - -gamepad::gamepad(): - left_deadzone_cross(true), - right_deadzone_cross(true), - left_deadzone_roundness(0.0f), - right_deadzone_roundness(0.0f) -{ - for (int i = 0; i < 6; ++i) - { - axis_positions[i] = 0.0f; - axis_activation_min[i] = 0.15f; - axis_activation_max[i] = 0.98f; - axis_response_curves[i] = gamepad_response_curve::linear; - } -} - -void gamepad::set_activation_threshold(gamepad_axis axis, float min, float max) -{ - axis_activation_min[static_cast>(axis)] = min; - axis_activation_max[static_cast>(axis)] = max; -} - -void gamepad::set_response_curve(gamepad_axis axis, gamepad_response_curve curve) -{ - axis_response_curves[static_cast>(axis)] = curve; -} - -void gamepad::set_left_deadzone_cross(bool cross) -{ - left_deadzone_cross = cross; -} - -void gamepad::set_right_deadzone_cross(bool cross) -{ - right_deadzone_cross = cross; -} - -void gamepad::set_left_deadzone_roundness(float roundness) -{ - left_deadzone_roundness = roundness; -} - -void gamepad::set_right_deadzone_roundness(float roundness) -{ - right_deadzone_roundness = roundness; -} - -void gamepad::press(gamepad_button button) -{ - button_pressed_publisher.publish({this, button}); -} - -void gamepad::release(gamepad_button button) -{ - button_released_publisher.publish({this, button}); -} - -void gamepad::move(gamepad_axis axis, float position) -{ - const auto axis_index = static_cast>(axis); - - /// @TODO Support arbitrary number of gamepad axes. - if (axis_index >= 6) - return; - - // Update axis position - axis_positions[axis_index] = position; - - switch (axis) - { - case gamepad_axis::left_stick_x: - case gamepad_axis::left_stick_y: - if (left_deadzone_cross) - handle_axial_motion(axis); - else - handle_biaxial_motion(gamepad_axis::left_stick_x, gamepad_axis::left_stick_y); - break; - - case gamepad_axis::right_stick_x: - case gamepad_axis::right_stick_y: - if (right_deadzone_cross) - handle_axial_motion(axis); - else - handle_biaxial_motion(gamepad_axis::right_stick_x, gamepad_axis::right_stick_y); - break; - - case gamepad_axis::left_trigger: - case gamepad_axis::right_trigger: - default: - handle_axial_motion(axis); - break; - } -} - -void gamepad::handle_axial_motion(gamepad_axis axis) -{ - const auto axis_index = static_cast>(axis); - - // Get axis parameters - const float activation_min = axis_activation_min[axis_index]; - const float activation_max = axis_activation_max[axis_index]; - const float axis_position = axis_positions[axis_index]; - const gamepad_response_curve response_curve = axis_response_curves[axis_index]; - - // Remap axis position - float remapped_position = 0.0f; - if (std::abs(axis_position) > activation_min) - { - // Remap position according to activation thresholds and clamp to `[0, 1]`. - float response = math::map(std::abs(axis_position), activation_min, activation_max, 0.0f, 1.0f); - response = std::clamp(response, 0.0f, 1.0f); - - // Remap position according to axis response curve - response = curve_response(axis, response); - - // Restore sign of axis motion - response = (axis_position < 0.0f) ? -response : response; - - remapped_position = response; - } - - axis_moved_publisher.publish({this, axis, remapped_position}); -} - -void gamepad::handle_biaxial_motion(gamepad_axis axis_x, gamepad_axis axis_y) -{ - // Get axis parameters - const int x_axis_index = static_cast>(axis_x); - const int y_axis_index = static_cast>(axis_y); - const float x_activation_min = axis_activation_min[x_axis_index]; - const float x_activation_max = axis_activation_max[x_axis_index]; - const float y_activation_min = axis_activation_min[y_axis_index]; - const float y_activation_max = axis_activation_max[y_axis_index]; - const float x_axis_position = axis_positions[x_axis_index]; - const float y_axis_position = axis_positions[y_axis_index]; - const gamepad_response_curve x_response_curve = axis_response_curves[x_axis_index]; - const gamepad_response_curve y_response_curve = axis_response_curves[y_axis_index]; - const float deadzone_roundness = (axis_x == gamepad_axis::left_stick_x) ? left_deadzone_roundness : right_deadzone_roundness; - - const float radius = std::min(x_activation_min, y_activation_min) * deadzone_roundness; - const float dx = std::max(0.0f, std::abs(x_axis_position) - x_activation_min + radius); - const float dy = std::max(0.0f, std::abs(y_axis_position) - y_activation_min + radius); - const float distance = std::sqrt(dx * dx + dy * dy) - radius; - - if (distance > 0.0f) - { - const float nx = std::abs(x_axis_position) / distance; - const float ny = std::abs(y_axis_position) / distance; - const float ndx = (distance - x_activation_min) / (x_activation_max - x_activation_min); - const float ndy = (distance - y_activation_min) / (y_activation_max - y_activation_min); - - float response_x = std::clamp(nx * ndx, 0.0f, 1.0f); - float response_y = std::clamp(ny * ndy, 0.0f, 1.0f); - - response_x = curve_response(axis_x, response_x); - response_y = curve_response(axis_y, response_y); - - // Restore signs of axis motions - response_x = (x_axis_position < 0.0f) ? -response_x : response_x; - response_y = (y_axis_position < 0.0f) ? -response_y : response_y; - - axis_moved_publisher.publish({this, axis_x, response_x}); - axis_moved_publisher.publish({this, axis_y, response_y}); - } - else - { - axis_moved_publisher.publish({this, axis_x, 0.0f}); - axis_moved_publisher.publish({this, axis_y, 0.0f}); - } -} - -float gamepad::curve_response(gamepad_axis axis, float response) const -{ - const auto axis_index = static_cast>(axis); - const gamepad_response_curve response_curve = axis_response_curves[axis_index]; - - switch (response_curve) - { - case gamepad_response_curve::linear: - break; - - case gamepad_response_curve::square: - response = response * response; - break; - - case gamepad_response_curve::cube: - response = response * response * response; - break; - } - - return response; -} - -} // namespace input diff --git a/src/input/gamepad.hpp b/src/input/gamepad.hpp deleted file mode 100644 index 6758de6..0000000 --- a/src/input/gamepad.hpp +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_INPUT_GAMEPAD_HPP -#define ANTKEEPER_INPUT_GAMEPAD_HPP - -#include "input/device.hpp" -#include "input/gamepad-events.hpp" -#include "input/gamepad-axis.hpp" -#include "input/gamepad-button.hpp" -#include "event/publisher.hpp" - -namespace input { - -/// Gamepad axis activation response curves. -enum class gamepad_response_curve -{ - /// Linear response curve. - linear, - - /// Squared response curve. - square, - - /// Cubed response curve. - cube -}; - -/** - * A virtual gamepad which generates gamepad-related input events. - */ -class gamepad: public device -{ -public: - /** - * Constructs a gamepad input device. - */ - gamepad(); - - /// Destructs a gamepad input device. - virtual ~gamepad() = default; - - /** - * Sets the activation threshold for a gamepad axis. - * - * @param axis Gamepad axis. - * @param min Axis minimum activation threshold. - * @param max Axis maximum activation threshold. - */ - void set_activation_threshold(gamepad_axis axis, float min, float max); - - /** - * Sets the activation response curve of an axis. - * - * @param axis Gamepad axis. - * @param curve Activation response curve. - */ - void set_response_curve(gamepad_axis axis, gamepad_response_curve curve); - - /** - * Sets the type of deadzone shape for the axes on the left stick. - * - * @param cross If `true`, the x and y axes are independently activated, if `false`, activation of the x and y axes are dependent on their combined magnitude. - */ - void set_left_deadzone_cross(bool cross); - - /** - * Sets the type of deadzone shape for the axes on the right stick. - * - * @param cross If `true`, the x and y axes are independently activated, if `false`, activation of the x and y axes are dependent on their combined magnitude. - */ - void set_right_deadzone_cross(bool cross); - - /** - * Sets the roundness of the deadzone for the axes on the left stick. - * - * @param roundness Roundness of the deadzone shape for non-cross deadzones. A value of `0.0` results in a square deadzone, while a value of `1.0` results in a circular deadzone. Values between `0.0` and `1.0` result in a rounded rectangle deadzone. - */ - void set_left_deadzone_roundness(float roundness); - - /** - * Sets the roundness of the deadzone for the axes on the right stick. - * - * @param roundness Roundness of the deadzone shape for non-cross deadzones. A value of `0.0` results in a square deadzone, while a value of `1.0` results in a circular deadzone. Values between `0.0` and `1.0` result in a rounded rectangle deadzone. - */ - void set_right_deadzone_roundness(float roundness); - - /** - * Simulates a gamepad button press. - * - * @param button Button to press. - */ - void press(gamepad_button button); - - /** - * Simulates a gamepad button release. - * - * @param button Button to release. - */ - void release(gamepad_button button); - - /** - * Simulates a gamepad axis movement. - * - * @param axis Gamepad axis. - * @param position Position on the axis, on `[-1, 1]`. - */ - void move(gamepad_axis axis, float position); - - /// Returns the channel through which gamepad button pressed events are published. - [[nodiscard]] inline ::event::channel& get_button_pressed_channel() noexcept - { - return button_pressed_publisher.channel(); - } - - /// Returns the channel through which gamepad button released events are published. - [[nodiscard]] inline ::event::channel& get_button_released_channel() noexcept - { - return button_released_publisher.channel(); - } - - /// Returns the channel through which gamepad axis moved events are published. - [[nodiscard]] inline ::event::channel& get_axis_moved_channel() noexcept - { - return axis_moved_publisher.channel(); - } - - /// Returns device_type::gamepad. - [[nodiscard]] inline virtual constexpr device_type get_device_type() const noexcept - { - return device_type::gamepad; - } - -private: - void handle_axial_motion(gamepad_axis axis); - void handle_biaxial_motion(gamepad_axis axis_x, gamepad_axis axis_y); - float curve_response(gamepad_axis axis, float response) const; - - float axis_positions[6]; - float axis_activation_min[6]; - float axis_activation_max[6]; - gamepad_response_curve axis_response_curves[6]; - bool left_deadzone_cross; - bool right_deadzone_cross; - float left_deadzone_roundness; - float right_deadzone_roundness; - - ::event::publisher button_pressed_publisher; - ::event::publisher button_released_publisher; - ::event::publisher axis_moved_publisher; -}; - -} // namespace input - -#endif // ANTKEEPER_INPUT_GAMEPAD_HPP diff --git a/src/input/keyboard-events.hpp b/src/input/keyboard-events.hpp deleted file mode 100644 index 8d84bb3..0000000 --- a/src/input/keyboard-events.hpp +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_INPUT_KEYBOARD_EVENTS_HPP -#define ANTKEEPER_INPUT_KEYBOARD_EVENTS_HPP - -#include "input/scancode.hpp" -#include "input/modifier-key.hpp" -#include - -namespace input { - -class keyboard; - -/** - * Event generated when a keyboard key has been pressed. - */ -struct key_pressed_event -{ - /// Keyboard that generated the event. - keyboard* keyboard; - - /// Scancode of the key being pressed. - scancode scancode; - - /// Bit mask containing the active modifier keys. - std::uint16_t modifiers; - - /// `true` if the key press was generated by a key repeat, `false` otherwise. - bool repeat; -}; - -/** - * Event generated when a keyboard key has been released. - */ -struct key_released_event -{ - /// Keyboard that generated the event. - keyboard* keyboard; - - /// Scancode of the key being released. - scancode scancode; - - /// Bit mask containing the active modifier keys. - std::uint16_t modifiers; -}; - -} // namespace input - -#endif // ANTKEEPER_INPUT_KEYBOARD_EVENTS_HPP diff --git a/src/input/keyboard.cpp b/src/input/keyboard.cpp deleted file mode 100644 index bd0a275..0000000 --- a/src/input/keyboard.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "input/keyboard.hpp" -#include "input/scancode.hpp" - -namespace input { - -void keyboard::press(scancode scancode, std::uint16_t modifiers, bool repeat) -{ - key_pressed_publisher.publish({this, scancode, modifiers, repeat}); -} - -void keyboard::release(scancode scancode, std::uint16_t modifiers) -{ - key_released_publisher.publish({this, scancode, modifiers}); -} - -} // namespace input diff --git a/src/input/keyboard.hpp b/src/input/keyboard.hpp deleted file mode 100644 index bca7f7f..0000000 --- a/src/input/keyboard.hpp +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_INPUT_KEYBOARD_HPP -#define ANTKEEPER_INPUT_KEYBOARD_HPP - -#include "input/device.hpp" -#include "input/keyboard-events.hpp" -#include "input/scancode.hpp" -#include "input/modifier-key.hpp" -#include "event/publisher.hpp" - -namespace input { - -/** - * A virtual keyboard which generates keyboard-related input events. - */ -class keyboard: public device -{ -public: - /** - * Constructs a keyboard input device. - */ - keyboard() = default; - - /// Destructs a keyboard input device. - virtual ~keyboard() = default; - - /** - * Simulates a key press. - * - * @param scancode Scancode of the key to press. - * @param modifiers Bit mask containing the active modifier keys. - * @param repeat `true` if the key press is from a key repeat, `false` otherwise. - */ - void press(scancode scancode, std::uint16_t modifiers = modifier_key::none, bool repeat = false); - - /** - * Simulates a key release. - * - * @param scancode Scancode of the key to release. - * @param modifiers Bit mask containing the active modifier keys. - */ - void release(scancode scancode, std::uint16_t modifiers = modifier_key::none); - - /// Returns the channel through which key pressed events are published. - [[nodiscard]] inline ::event::channel& get_key_pressed_channel() noexcept - { - return key_pressed_publisher.channel(); - } - - /// Returns the channel through which key released events are published. - [[nodiscard]] inline ::event::channel& get_key_released_channel() noexcept - { - return key_released_publisher.channel(); - } - - /// Returns device_type::keyboard. - [[nodiscard]] inline virtual constexpr device_type get_device_type() const noexcept - { - return device_type::keyboard; - } - -private: - ::event::publisher key_pressed_publisher; - ::event::publisher key_released_publisher; -}; - -} // namespace input - -#endif // ANTKEEPER_INPUT_KEYBOARD_HPP diff --git a/src/input/mapper.cpp b/src/input/mapper.cpp deleted file mode 100644 index 1d8110b..0000000 --- a/src/input/mapper.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "input/mapper.hpp" -#include - -namespace input { - -void mapper::connect(::event::queue& queue) -{ - subscriptions.emplace_back(queue.subscribe(std::bind_front(&mapper::handle_gamepad_axis_moved, this))); - subscriptions.emplace_back(queue.subscribe(std::bind_front(&mapper::handle_gamepad_button_pressed, this))); - subscriptions.emplace_back(queue.subscribe(std::bind_front(&mapper::handle_key_pressed, this))); - subscriptions.emplace_back(queue.subscribe(std::bind_front(&mapper::handle_mouse_button_pressed, this))); - subscriptions.emplace_back(queue.subscribe(std::bind_front(&mapper::handle_mouse_moved, this))); - subscriptions.emplace_back(queue.subscribe(std::bind_front(&mapper::handle_mouse_scrolled, this))); -} - -void mapper::disconnect() -{ - subscriptions.clear(); -} - -void mapper::handle_gamepad_axis_moved(const gamepad_axis_moved_event& event) -{ - if (std::abs(event.position) > 0.5f) - { - gamepad_axis_mapped_publisher.publish({gamepad_axis_mapping(event.gamepad, event.axis, std::signbit(event.position))}); - } -} - -void mapper::handle_gamepad_button_pressed(const gamepad_button_pressed_event& event) -{ - gamepad_button_mapped_publisher.publish({gamepad_button_mapping(event.gamepad, event.button)}); -} - -void mapper::handle_key_pressed(const key_pressed_event& event) -{ - if (!event.repeat) - { - key_mapped_publisher.publish({key_mapping(event.keyboard, event.scancode)}); - } -} - -void mapper::handle_mouse_button_pressed(const mouse_button_pressed_event& event) -{ - mouse_button_mapped_publisher.publish({mouse_button_mapping(event.mouse, event.button)}); -} - -void mapper::handle_mouse_moved(const mouse_moved_event& event) -{ - if (event.difference.x()) - { - mouse_motion_mapped_publisher.publish({mouse_motion_mapping(event.mouse, mouse_motion_axis::x, std::signbit(static_cast(event.difference.x())))}); - } - - if (event.difference.y()) - { - mouse_motion_mapped_publisher.publish({mouse_motion_mapping(event.mouse, mouse_motion_axis::y, std::signbit(static_cast(event.difference.y())))}); - } -} - -void mapper::handle_mouse_scrolled(const mouse_scrolled_event& event) -{ - if (event.velocity.x()) - { - mouse_scroll_mapped_publisher.publish({mouse_scroll_mapping(event.mouse, mouse_scroll_axis::x, std::signbit(event.velocity.x()))}); - } - - if (event.velocity.y()) - { - mouse_scroll_mapped_publisher.publish({mouse_scroll_mapping(event.mouse, mouse_scroll_axis::y, std::signbit(event.velocity.y()))}); - } -} - -} // namespace input diff --git a/src/input/mapper.hpp b/src/input/mapper.hpp deleted file mode 100644 index 094e17f..0000000 --- a/src/input/mapper.hpp +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_INPUT_MAPPER_HPP -#define ANTKEEPER_INPUT_MAPPER_HPP - -#include "event/subscription.hpp" -#include "event/queue.hpp" -#include "event/publisher.hpp" -#include "input/gamepad-events.hpp" -#include "input/keyboard-events.hpp" -#include "input/mouse-events.hpp" -#include "input/mapping-events.hpp" -#include -#include - -namespace input { - -/** - * Listens for input events and generates corresponding input mappings. - */ -class mapper -{ -public: - /** - * Connects the input event signals of an event queue to the mapper. - * - * @param queue Event queue to connect. - */ - void connect(::event::queue& queue); - - /** - * Disconnects all input event signals from the mapper. - */ - void disconnect(); - - /// Returns the channel through which gamepad axis mapped events are published. - [[nodiscard]] inline ::event::channel& get_gamepad_axis_mapped_channel() noexcept - { - return gamepad_axis_mapped_publisher.channel(); - } - - /// Returns the channel through which gamepad button mapped events are published. - [[nodiscard]] inline ::event::channel& get_gamepad_button_mapped_channel() noexcept - { - return gamepad_button_mapped_publisher.channel(); - } - - /// Returns the channel through which key mapped events are published. - [[nodiscard]] inline ::event::channel& get_key_mapped_channel() noexcept - { - return key_mapped_publisher.channel(); - } - - /// Returns the channel through which mouse button mapped events are published. - [[nodiscard]] inline ::event::channel& get_mouse_button_mapped_channel() noexcept - { - return mouse_button_mapped_publisher.channel(); - } - - /// Returns the channel through which mouse motion mapped events are published. - [[nodiscard]] inline ::event::channel& get_mouse_motion_mapped_channel() noexcept - { - return mouse_motion_mapped_publisher.channel(); - } - - /// Returns the channel through which mouse scroll mapped events are published. - [[nodiscard]] inline ::event::channel& get_mouse_scroll_mapped_channel() noexcept - { - return mouse_scroll_mapped_publisher.channel(); - } - -private: - void handle_gamepad_axis_moved(const gamepad_axis_moved_event& event); - void handle_gamepad_button_pressed(const gamepad_button_pressed_event& event); - void handle_key_pressed(const key_pressed_event& event); - void handle_mouse_button_pressed(const mouse_button_pressed_event& event); - void handle_mouse_moved(const mouse_moved_event& event); - void handle_mouse_scrolled(const mouse_scrolled_event& event); - - std::vector> subscriptions; - ::event::publisher gamepad_axis_mapped_publisher; - ::event::publisher gamepad_button_mapped_publisher; - ::event::publisher key_mapped_publisher; - ::event::publisher mouse_button_mapped_publisher; - ::event::publisher mouse_motion_mapped_publisher; - ::event::publisher mouse_scroll_mapped_publisher; -}; - -} // namespace input - -#endif // ANTKEEPER_INPUT_MAPPER_HPP diff --git a/src/input/mapping-events.hpp b/src/input/mapping-events.hpp deleted file mode 100644 index 64a6be8..0000000 --- a/src/input/mapping-events.hpp +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_INPUT_MAPPING_EVENTS_HPP -#define ANTKEEPER_INPUT_MAPPING_EVENTS_HPP - -#include "input/mapping.hpp" - -namespace input { - -/** - * Event generated when a gamepad axis mapping has been generated. - */ -struct gamepad_axis_mapped_event -{ - /// Gamepad axis mapping that was generated. - gamepad_axis_mapping mapping; -}; - -/** - * Event generated when a gamepad button mapping has been generated. - */ -struct gamepad_button_mapped_event -{ - /// Gamepad button mapping that was generated. - gamepad_button_mapping mapping; -}; - -/** - * Event generated when a key mapping has been generated. - */ -struct key_mapped_event -{ - /// Key mapping that was generated. - key_mapping mapping; -}; - -/** - * Event generated when a mouse button mapping has been generated. - */ -struct mouse_button_mapped_event -{ - /// Mouse button mapping that was generated. - mouse_button_mapping mapping; -}; - -/** - * Event generated when a mouse motion mapping has been generated. - */ -struct mouse_motion_mapped_event -{ - /// Mouse motion mapping that was generated. - mouse_motion_mapping mapping; -}; - -/** - * Event generated when a mouse scroll mapping has been generated. - */ -struct mouse_scroll_mapped_event -{ - /// Mouse scroll mapping that was generated. - mouse_scroll_mapping mapping; -}; - -} // namespace input - -#endif // ANTKEEPER_INPUT_MAPPING_EVENTS_HPP diff --git a/src/input/mapping.cpp b/src/input/mapping.cpp deleted file mode 100644 index 9e9ee11..0000000 --- a/src/input/mapping.cpp +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "input/mapping.hpp" -#include "resources/serializer.hpp" -#include "resources/serialize-error.hpp" -#include "resources/deserializer.hpp" -#include "resources/deserialize-error.hpp" - -namespace input { - -gamepad_axis_mapping::gamepad_axis_mapping(input::gamepad* gamepad, gamepad_axis axis, bool direction): - gamepad(gamepad), - axis(axis), - direction(direction) -{} - -gamepad_button_mapping::gamepad_button_mapping(input::gamepad* gamepad, gamepad_button button): - gamepad(gamepad), - button(button) -{} - -key_mapping::key_mapping(input::keyboard* keyboard, input::scancode scancode, std::uint16_t modifiers, bool repeat): - keyboard(keyboard), - scancode(scancode), - repeat(repeat), - modifiers(modifiers) -{} - -mouse_button_mapping::mouse_button_mapping(input::mouse* mouse, mouse_button button): - mouse(mouse), - button(button) -{} - -mouse_motion_mapping::mouse_motion_mapping(input::mouse* mouse, mouse_motion_axis axis, bool direction): - mouse(mouse), - axis(axis), - direction(direction) -{} - -mouse_scroll_mapping::mouse_scroll_mapping(input::mouse* mouse, mouse_scroll_axis axis, bool direction): - mouse(mouse), - axis(axis), - direction(direction) -{} - -} // namespace input - -/** - * Serializes an input mapping. - * - * @param[in] mapping Input mapping to serialize. - * @param[in,out] ctx Serialize context. - * - * @throw serialize_error Write error. - */ -/// @{ -template <> -void serializer::serialize(const input::gamepad_axis_mapping& mapping, serialize_context& ctx) -{ - ctx.write8(reinterpret_cast(&mapping.axis), 1); - const std::uint8_t direction = mapping.direction; - ctx.write8(reinterpret_cast(&direction), 1); -} - -template <> -void serializer::serialize(const input::gamepad_button_mapping& mapping, serialize_context& ctx) -{ - ctx.write8(reinterpret_cast(&mapping.button), 1); -} - -template <> -void serializer::serialize(const input::key_mapping& mapping, serialize_context& ctx) -{ - ctx.write16(reinterpret_cast(&mapping.scancode), 1); - ctx.write16(reinterpret_cast(&mapping.modifiers), 1); - const std::uint8_t repeat = mapping.repeat; - ctx.write8(reinterpret_cast(&repeat), 1); -} - -template <> -void serializer::serialize(const input::mouse_button_mapping& mapping, serialize_context& ctx) -{ - ctx.write8(reinterpret_cast(&mapping.button), 1); -} - -template <> -void serializer::serialize(const input::mouse_motion_mapping& mapping, serialize_context& ctx) -{ - ctx.write8(reinterpret_cast(&mapping.axis), 1); - const std::uint8_t direction = mapping.direction; - ctx.write8(reinterpret_cast(&direction), 1); -} - -template <> -void serializer::serialize(const input::mouse_scroll_mapping& mapping, serialize_context& ctx) -{ - ctx.write8(reinterpret_cast(&mapping.axis), 1); - const std::uint8_t direction = mapping.direction; - ctx.write8(reinterpret_cast(&direction), 1); -} -/// @} - -/** - * Deserializes an input mapping. - * - * @param[out] mapping Input mapping to deserialize. - * @param[in,out] ctx Deserialize context. - * - * @throw deserialize_error Read error. - */ -/// @{ -template <> -void deserializer::deserialize(input::gamepad_axis_mapping& mapping, deserialize_context& ctx) -{ - mapping.gamepad = nullptr; - - ctx.read8(reinterpret_cast(&mapping.axis), 1); - std::uint8_t direction = 0; - ctx.read8(reinterpret_cast(&direction), 1); - mapping.direction = direction; -} - -template <> -void deserializer::deserialize(input::gamepad_button_mapping& mapping, deserialize_context& ctx) -{ - mapping.gamepad = nullptr; - - ctx.read8(reinterpret_cast(&mapping.button), 1); -} - -template <> -void deserializer::deserialize(input::key_mapping& mapping, deserialize_context& ctx) -{ - mapping.keyboard = nullptr; - - ctx.read16(reinterpret_cast(&mapping.scancode), 1); - ctx.read16(reinterpret_cast(&mapping.modifiers), 1); - std::uint8_t repeat = 0; - ctx.read8(reinterpret_cast(&repeat), 1); - mapping.repeat = repeat; -} - -template <> -void deserializer::deserialize(input::mouse_button_mapping& mapping, deserialize_context& ctx) -{ - mapping.mouse = nullptr; - - ctx.read8(reinterpret_cast(&mapping.button), 1); -} - -template <> -void deserializer::deserialize(input::mouse_motion_mapping& mapping, deserialize_context& ctx) -{ - mapping.mouse = nullptr; - - ctx.read8(reinterpret_cast(&mapping.axis), 1); - std::uint8_t direction = 0; - ctx.read8(reinterpret_cast(&direction), 1); - mapping.direction = direction; -} - -template <> -void deserializer::deserialize(input::mouse_scroll_mapping& mapping, deserialize_context& ctx) -{ - mapping.mouse = nullptr; - - ctx.read8(reinterpret_cast(&mapping.axis), 1); - std::uint8_t direction = 0; - ctx.read8(reinterpret_cast(&direction), 1); - mapping.direction = direction; -} -/// @} diff --git a/src/input/mapping.hpp b/src/input/mapping.hpp deleted file mode 100644 index 02b4f5e..0000000 --- a/src/input/mapping.hpp +++ /dev/null @@ -1,278 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_INPUT_MAPPING_HPP -#define ANTKEEPER_INPUT_MAPPING_HPP - -#include "input/mapping-type.hpp" -#include "input/gamepad-axis.hpp" -#include "input/gamepad-button.hpp" -#include "input/mouse-button.hpp" -#include "input/mouse-motion-axis.hpp" -#include "input/mouse-scroll-axis.hpp" -#include "input/scancode.hpp" -#include - -namespace input { - -class control; -class gamepad; -class keyboard; -class mouse; - -/** - * Abstract base class for input mappings. - */ -class mapping -{ -public: - /** - * Constructs an input mapping. - */ - mapping() = default; - - /// Destructs an input mapping. - virtual ~mapping() = default; - - /// Returns the input mapping type. - [[nodiscard]] virtual constexpr mapping_type get_mapping_type() const noexcept = 0; -}; - -/** - * Maps a direction along a gamepad axis to a control input value. - */ -class gamepad_axis_mapping: public mapping -{ -public: - /** - * Constructs a gamepad axis mapping. - * - * @param gamepad Pointer to the gamepad to map, or `nullptr` if input from any gamepad will be mapped. - * @param axis Gamepad axis to map. - * @param direction Sign bit of the direction to map. - */ - /// @{ - gamepad_axis_mapping(input::gamepad* gamepad, gamepad_axis axis, bool direction); - gamepad_axis_mapping() = default; - /// @} - - /// Destructs a gamepad axis mapping. - virtual ~gamepad_axis_mapping() = default; - - /// Returns mapping_type::gamepad_axis. - [[nodiscard]] inline virtual constexpr mapping_type get_mapping_type() const noexcept - { - return mapping_type::gamepad_axis; - } - - /// Pointer to the mapped gamepad, or `nullptr` if input from any gamepad is accepted. - input::gamepad* gamepad; - - /// Mapped gamepad axis. - gamepad_axis axis; - - /// Sign bit of the mapped direction. - bool direction; -}; - -/** - * Maps a gamepad button to a control input value. - */ -class gamepad_button_mapping: public mapping -{ -public: - /** - * Constructs a gamepad button mapping. - * - * @param gamepad Pointer to the gamepad to map, or `nullptr` if input from any gamepad will be mapped. - * @param button Gamepad button to map. - */ - /// @{ - gamepad_button_mapping(input::gamepad* gamepad, gamepad_button button); - gamepad_button_mapping() = default; - /// @} - - /// Destructs a gamepad button mapping. - virtual ~gamepad_button_mapping() = default; - - /// Returns mapping_type::gamepad_button. - [[nodiscard]] inline virtual constexpr mapping_type get_mapping_type() const noexcept - { - return mapping_type::gamepad_button; - } - - /// Pointer to the mapped gamepad, or `nullptr` if input from any gamepad is accepted. - input::gamepad* gamepad; - - /// Mapped gamepad button. - gamepad_button button; -}; - -/** - * Maps a keyboard key to a control input value. - */ -class key_mapping: public mapping -{ -public: - /** - * Constructs a key mapping. - * - * @param keyboard Pointer to the keyboard to map, or `nullptr` if input from any keyboard will be mapped. - * @param scancode Scancode of the key to map. - * @param repeat `false` if the mapping should ignore key repeats, `true` otherwise. - * @param modifiers Modifier keys bitmask. - */ - /// @{ - key_mapping(input::keyboard* keyboard, input::scancode scancode, std::uint16_t modifiers = 0, bool repeat = false); - key_mapping() = default; - /// @} - - /// Destructs a keyboard key mapping. - virtual ~key_mapping() = default; - - /// Returns mapping_type::key. - [[nodiscard]] inline virtual constexpr mapping_type get_mapping_type() const noexcept - { - return mapping_type::key; - } - - /// Pointer to the mapped keyboard, or `nullptr` if input from any keyboard is accepted. - input::keyboard* keyboard; - - /// Scancode of the mapped key. - scancode scancode; - - /// Modifier keys bitbask. - std::uint16_t modifiers; - - /// `false` if the mapping ignores key repeats, `true` otherwise. - bool repeat; -}; - -/** - * Maps a mouse button to a control input value. - */ -class mouse_button_mapping: public mapping -{ -public: - /** - * Constructs a mouse button mapping. - * - * @param mouse Pointer to the mouse to map, or `nullptr` if input from any mouse will be mapped. - * @param button Mouse button to map. - */ - /// @{ - mouse_button_mapping(input::mouse* mouse, mouse_button button); - mouse_button_mapping() = default; - /// @} - - /// Destructs a mouse button mapping. - virtual ~mouse_button_mapping() = default; - - /// Returns mapping_type::mouse_button. - [[nodiscard]] inline virtual constexpr mapping_type get_mapping_type() const noexcept - { - return mapping_type::mouse_button; - } - - /// Pointer to the mapped mouse, or `nullptr` if input from any mouse is accepted. - input::mouse* mouse; - - /// Mapped mouse button. - mouse_button button; -}; - -/** - * Maps a direction along a mouse motion axis to a control input value. - */ -class mouse_motion_mapping: public mapping -{ -public: - /** - * Constructs a mouse motion mapping. - * - * @param mouse Pointer to the mouse to map, or `nullptr` if input from any mouse will be mapped. - * @param axis Mouse motion axis to map. - * @param direction Sign bit of the direction to map. - */ - /// @{ - mouse_motion_mapping(input::mouse* mouse, mouse_motion_axis axis, bool direction); - mouse_motion_mapping() = default; - /// @} - - /// Destructs a mouse motion mapping. - virtual ~mouse_motion_mapping() = default; - - /// Returns mapping_type::mouse_motion. - [[nodiscard]] inline virtual constexpr mapping_type get_mapping_type() const noexcept - { - return mapping_type::mouse_motion; - } - - /// Pointer to the mapped mouse, or `nullptr` if input from any mouse is accepted. - input::mouse* mouse; - - /// Mapped mouse motion axis. - mouse_motion_axis axis; - - /// Sign bit of the mapped direction. - bool direction; -}; - -/** - * Maps a direction along a mouse scroll axis to a control input value. - */ -class mouse_scroll_mapping: public mapping -{ -public: - /** - * Constructs a mouse scroll mapping. - * - * @param control Control to which input will be mapped. - * @param mouse Pointer to the mouse to map, or `nullptr` if input from any mouse will be mapped. - * @param axis Mouse scroll axis to map. - * @param direction Sign bit of the direction to map. - */ - /// @{ - mouse_scroll_mapping(input::mouse* mouse, mouse_scroll_axis axis, bool direction); - mouse_scroll_mapping() = default; - /// @} - - /// Destructs a mouse scroll mapping. - virtual ~mouse_scroll_mapping() = default; - - /// Returns mapping_type::mouse_scroll. - [[nodiscard]] inline virtual constexpr mapping_type get_mapping_type() const noexcept - { - return mapping_type::mouse_scroll; - } - - /// Pointer to the mapped mouse, or `nullptr` if input from any mouse is accepted. - input::mouse* mouse; - - /// Mapped mouse scroll axis. - mouse_scroll_axis axis; - - /// Sign bit of the mapped direction. - bool direction; -}; - -} // namespace input - -#endif // ANTKEEPER_INPUT_MAPPING_HPP diff --git a/src/input/mouse-events.hpp b/src/input/mouse-events.hpp deleted file mode 100644 index c256775..0000000 --- a/src/input/mouse-events.hpp +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_INPUT_MOUSE_EVENTS_HPP -#define ANTKEEPER_INPUT_MOUSE_EVENTS_HPP - -#include "input/mouse-button.hpp" -#include "input/mouse-motion-axis.hpp" -#include "input/mouse-scroll-axis.hpp" -#include "math/vector.hpp" -#include - -namespace input { - -class mouse; - -/** - * Event generated when a mouse has been moved. - */ -struct mouse_moved_event -{ - /// Mouse that generated the event. - mouse* mouse; - - /// Mouse position, in pixels, relative to the window. - math::vector position; - - /// Relative movement of the mouse, in pixels. - math::vector difference; -}; - -/** - * Event generated when a mouse button has been pressed. - */ -struct mouse_button_pressed_event -{ - /// Mouse that generated the event. - mouse* mouse; - - /// Mouse position, in pixels, relative to the window, when the button was pressed. - math::vector position; - - /// Mouse button being pressed. - mouse_button button; -}; - -/** - * Event generated when a mouse button has been released. - */ -struct mouse_button_released_event -{ - /// Mouse that generated the event. - mouse* mouse; - - /// Mouse position, in pixels, relative to the window, when the button was released. - math::vector position; - - /// Mouse button being released. - mouse_button button; -}; - -/** - * Event generated when a mouse has been scrolled. - */ -struct mouse_scrolled_event -{ - /// Mouse that generated the event. - mouse* mouse; - - /// Mouse position, in pixels, relative to the window, when the mouse was scrolled. - math::vector position; - - /// Scroll velocity. - math::vector velocity; -}; - -} // namespace input - -#endif // ANTKEEPER_INPUT_MOUSE_EVENTS_HPP diff --git a/src/input/mouse.cpp b/src/input/mouse.cpp deleted file mode 100644 index 2121867..0000000 --- a/src/input/mouse.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "input/mouse.hpp" - -namespace input { - -void mouse::press(mouse_button button) -{ - button_pressed_publisher.publish({this, position, button}); -} - -void mouse::release(mouse_button button) -{ - button_released_publisher.publish({this, position, button}); -} - -void mouse::move(const math::vector& position, const math::vector& difference) -{ - this->position = position; - moved_publisher.publish({this, position, difference}); -} - -void mouse::scroll(const math::vector& velocity) -{ - scrolled_publisher.publish({this, position, velocity}); -} - -} // namespace input diff --git a/src/input/mouse.hpp b/src/input/mouse.hpp deleted file mode 100644 index f633e57..0000000 --- a/src/input/mouse.hpp +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_INPUT_MOUSE_HPP -#define ANTKEEPER_INPUT_MOUSE_HPP - -#include "input/device.hpp" -#include "input/mouse-events.hpp" -#include "input/mouse-button.hpp" -#include "event/publisher.hpp" -#include "math/vector.hpp" -#include - -namespace input { - -/** - * A virtual mouse which generates mouse-related input events. - */ -class mouse: public device -{ -public: - /** - * Constructs a mouse input device. - */ - mouse() = default; - - /// Destructs a mouse input device. - virtual ~mouse() = default; - - /** - * Simulates a mouse button press. - * - * @param button Button to press. - */ - void press(mouse_button button); - - /** - * Simulates a mouse button release. - * - * @param button Button to release. - */ - void release(mouse_button button); - - /** - * Simulates mouse movement. - * - * @param position Mouse position, in pixels, relative to the window. - * @param difference Relative movement of the mouse, in pixels. - */ - void move(const math::vector& position, const math::vector& difference); - - /** - * Simulates mouse scrolling. - * - * @param velocity Scroll velocity. - */ - void scroll(const math::vector& velocity); - - /// Returns the current mouse position, in pixels, relative to the window. - [[nodiscard]] inline const math::vector& get_position() const noexcept - { - return position; - } - - /// Returns the channel through which mouse button pressed events are published. - [[nodiscard]] inline ::event::channel& get_button_pressed_channel() noexcept - { - return button_pressed_publisher.channel(); - } - - /// Returns the channel through which mouse button released events are published. - [[nodiscard]] inline ::event::channel& get_button_released_channel() noexcept - { - return button_released_publisher.channel(); - } - - /// Returns the channel through which mouse moved events are published. - [[nodiscard]] inline ::event::channel& get_moved_channel() noexcept - { - return moved_publisher.channel(); - } - - /// Returns the channel through which mouse scrolled events are published. - [[nodiscard]] inline ::event::channel& get_scrolled_channel() noexcept - { - return scrolled_publisher.channel(); - } - - /// Returns device_type::mouse. - [[nodiscard]] inline virtual constexpr device_type get_device_type() const noexcept - { - return device_type::mouse; - } - -private: - math::vector position; - - ::event::publisher button_pressed_publisher; - ::event::publisher button_released_publisher; - ::event::publisher moved_publisher; - ::event::publisher scrolled_publisher; -}; - -} // namespace input - -#endif // ANTKEEPER_INPUT_MOUSE_HPP diff --git a/src/main.cpp b/src/main.cpp deleted file mode 100644 index bd6faa0..0000000 --- a/src/main.cpp +++ /dev/null @@ -1,225 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "config.hpp" -#include "debug/console.hpp" -#include "debug/log.hpp" -#include "game/context.hpp" -#include "game/state/boot.hpp" -#include "utility/ansi.hpp" -#include "utility/paths.hpp" -#include -#include -#include -#include -#include -#include -#include - -int main(int argc, char* argv[]) -{ - // Get time at which the application launched - const auto launch_time = std::chrono::system_clock::now(); - - // Enable console UTF-8 output and VT100 sequences (for colored text) - debug::console::enable_utf8(); - debug::console::enable_vt100(); - - // Subscribe log to cout function to message logged events - auto log_to_cout_subscription = debug::log::default_logger().get_message_logged_channel().subscribe - ( - [&launch_time](const auto& event) - { - static const char* severities[] = - { - "trace", - "debug", - "info", - "warning", - "error", - "fatal" - }; - - static const std::string colors[] = - { - std::format("{}", ansi::fg_white), - std::format("{}", ansi::fg_bright_blue), - std::format("{}", ansi::fg_bright_green), - std::format("{}", ansi::fg_yellow), - std::format("{}", ansi::fg_red), - std::format("{}{}", ansi::fg_white, ansi::bg_bright_red) - }; - - std::osyncstream(std::cout) << std::format - ( - "[{:8.03f}] {}{}: {}:{}:{}: {}{}\n", - std::chrono::duration(event.time - launch_time).count(), - colors[static_cast(event.severity)], - //severities[static_cast(event.severity)], - static_cast(event.severity), - std::filesystem::path(event.location.file_name()).filename().string(), - event.location.line(), - event.location.column(), - event.message, - ansi::reset - ); - } - ); - - // Determine path to log archive - const std::filesystem::path log_archive_path = get_shared_config_path() / config::application_name / "logs"; - - // Set up log archive - bool log_archive_exists = false; - try - { - // Create log archive if it doesn't exist - if (std::filesystem::create_directories(log_archive_path)) - { - debug::log::debug("Created log archive \"{}\"", log_archive_path.string()); - } - else - { - // Clean pre-existing log archive - try - { - // Detect and sort archived logs - std::set log_archive; - for (const auto& entry: std::filesystem::directory_iterator{log_archive_path}) - { - if (entry.is_regular_file() && - entry.path().extension() == ".log") - { - log_archive.emplace(entry.path()); - } - } - - debug::log::debug("Detected {} archived log{} at \"{}\"", log_archive.size(), log_archive.size() != 1 ? "s" : "", log_archive_path.string()); - - // Delete expired logs - if (!log_archive.empty()) - { - for (std::size_t i = log_archive.size() + 1; i > config::debug_log_archive_capacity; --i) - { - std::filesystem::remove(*log_archive.begin()); - debug::log::debug("Deleted expired log file \"{}\"", log_archive.begin()->string()); - log_archive.erase(log_archive.begin()); - } - } - } - catch (const std::filesystem::filesystem_error& e) - { - debug::log::error("An error occured while cleaning the log archive \"{}\": {}", log_archive_path.string(), e.what()); - } - } - - log_archive_exists = true; - } - catch (const std::filesystem::filesystem_error& e) - { - debug::log::error("Failed to create log archive at \"{}\": {}", log_archive_path.string(), e.what()); - } - - // Set up logging to file - std::shared_ptr log_to_file_subscription; - std::filesystem::path log_filepath; - if (config::debug_log_archive_capacity && log_archive_exists) - { - // Determine log filename - const auto time = std::chrono::floor(launch_time); - const std::string log_filename = std::format("{0}-{1:%Y%m%d}T{1:%H%M%S}Z.log", config::application_slug, time); - - // Open log file - log_filepath = log_archive_path / log_filename; - const std::string log_filepath_string = log_filepath.string(); - auto log_filestream = std::make_shared(log_filepath); - - if (log_filestream->is_open()) - { - debug::log::debug("Opened log file \"{}\"", log_filepath_string); - - // Write log file header - (*log_filestream) << "time\tfile\tline\tcolumn\tseverity\tmessage"; - - if (log_filestream->good()) - { - // Subscribe log to file function to message logged events - log_to_file_subscription = debug::log::default_logger().get_message_logged_channel().subscribe - ( - [&launch_time, log_filestream](const auto& event) - { - std::osyncstream(*log_filestream) << std::format - ( - "\n{:.03f}\t{}\t{}\t{}\t{}\t{}", - std::chrono::duration(event.time - launch_time).count(), - std::filesystem::path(event.location.file_name()).filename().string(), - event.location.line(), - event.location.column(), - static_cast(event.severity), - event.message - ); - } - ); - - // Unsubscribe log to cout function from message logged events on release builds - #if defined(NDEBUG) - log_to_cout_subscription->unsubscribe(); - #endif - } - else - { - debug::log::error("Failed to write to log file \"{}\"", log_filepath_string); - } - } - else - { - debug::log::error("Failed to open log file \"{}\"", log_filepath_string); - } - } - - // Log application name and version string, followed by launch time - debug::log::info("{0} {1}; {2:%Y%m%d}T{2:%H%M%S}Z", config::application_name, config::application_version_string, std::chrono::floor(launch_time)); - - // Start marker - debug::log::debug("Hi! 🐜"); - - try - { - // Allocate game context - game::context ctx; - - // Enter boot state - ctx.state_machine.emplace(new game::state::boot(ctx, argc, argv)); - } - catch (const std::exception& e) - { - debug::log::fatal("Unhandled exception: {}", e.what()); - - #if defined(NDEBUG) - SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", std::format("Unhandled exception: {}", e.what()).c_str(), nullptr); - #endif - - return EXIT_FAILURE; - } - - // Clean exit marker - debug::log::debug("Bye! 🐜"); - - return EXIT_SUCCESS; -} diff --git a/src/math/angles.hpp b/src/math/angles.hpp deleted file mode 100644 index 83c9ccc..0000000 --- a/src/math/angles.hpp +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_MATH_ANGLES_HPP -#define ANTKEEPER_MATH_ANGLES_HPP - -#include "math/numbers.hpp" -#include - -namespace math { - -/** - * Converts an angle from radians to degrees. - * - * @param radians Angle in radians. - * - * @return Angle in degrees. - */ -template -[[nodiscard]] inline constexpr T degrees(T radians) noexcept -{ - return radians * rad2deg; -} - -/** - * Converts an angle given in degrees to radians. - * - * @param radians Angle in radians. - * @return Angle in degrees. - */ -template -[[nodiscard]] inline constexpr T radians(T degrees) noexcept -{ - return degrees * deg2rad; -} - -/** - * Wraps an angle to [-180, 180]. - * - * @param degrees Angle in degrees. - * @return Wrapped angle. - */ -template -[[nodiscard]] inline constexpr T wrap_degrees(T degrees) -{ - return std::remainder(degrees, T(360)); -} - -/** - * Wraps an angle to [-pi, pi]. - * - * @param radians Angle in radians. - * @return Wrapped angle. - */ -template -[[nodiscard]] inline constexpr T wrap_radians(T radians) -{ - return std::remainder(radians, two_pi); -} - -} // namespace math - -#endif // ANTKEEPER_MATH_ANGLES_HPP diff --git a/src/math/glsl.hpp b/src/math/glsl.hpp deleted file mode 100644 index 683a6aa..0000000 --- a/src/math/glsl.hpp +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_MATH_GLSL_HPP -#define ANTKEEPER_MATH_GLSL_HPP - -#include "math/vector.hpp" -#include "math/matrix.hpp" -#include "math/quaternion.hpp" - -namespace math { -namespace glsl { - -/** - * Linear algebra data types with GLSL naming conventions. - */ -namespace types -{ - /// @name Vector types - /// @{ - - /** - * *n*-dimensional vector of booleans. - * - * @tparam N Number of elements - */ - /// @{ - template - using bvec = math::vector; - using bvec2 = bvec<2>; - using bvec3 = bvec<3>; - using bvec4 = bvec<4>; - /// @} - - /** - * *n*-dimensional vector of signed integers. - * - * @tparam N Number of elements - */ - /// @{ - template - using ivec = math::vector; - using ivec2 = ivec<2>; - using ivec3 = ivec<3>; - using ivec4 = ivec<4>; - /// @} - - /** - * *n*-dimensional vector of unsigned integers. - * - * @tparam N Number of elements - */ - /// @{ - template - using uvec = math::vector; - using uvec2 = uvec<2>; - using uvec3 = uvec<3>; - using uvec4 = uvec<4>; - /// @} - - /** - * *n*-dimensional vector of floating-point numbers. - * - * @tparam N Number of elements - */ - /// @{ - template - using fvec = math::vector; - using fvec2 = fvec<2>; - using fvec3 = fvec<3>; - using fvec4 = fvec<4>; - using vec2 = fvec2; - using vec3 = fvec3; - using vec4 = fvec4; - /// @} - - /** - * *n*-dimensional vector of double-precision floating-point numbers. - * - * @tparam N Number of elements - */ - /// @{ - template - using dvec = math::vector; - using dvec2 = dvec<2>; - using dvec3 = dvec<3>; - using dvec4 = dvec<4>; - /// @} - - /// @} - - /// @name Matrix types - /// @{ - - /** - * *n* by *m* matrix of floating-point numbers. - * - * @tparam N Number of columns. - * @tparam M Number of rows. - */ - /// @{ - template - using fmat = math::matrix; - using fmat2x2 = fmat<2, 2>; - using fmat2x3 = fmat<2, 3>; - using fmat2x4 = fmat<2, 4>; - using fmat3x2 = fmat<3, 2>; - using fmat3x3 = fmat<3, 3>; - using fmat3x4 = fmat<3, 4>; - using fmat4x2 = fmat<4, 2>; - using fmat4x3 = fmat<4, 3>; - using fmat4x4 = fmat<4, 4>; - using mat2x2 = fmat2x2; - using mat2x3 = fmat2x3; - using mat2x4 = fmat2x4; - using mat3x2 = fmat3x2; - using mat3x3 = fmat3x3; - using mat3x4 = fmat3x4; - using mat4x2 = fmat4x2; - using mat4x3 = fmat4x3; - using mat4x4 = fmat4x4; - /// @} - - /** - * *n* by *n* square matrix of floating-point numbers. - * - * @tparam N Number of columns and rows. - */ - /// @{ - using fmat2 = fmat2x2; - using fmat3 = fmat3x3; - using fmat4 = fmat4x4; - using mat2 = fmat2; - using mat3 = fmat3; - using mat4 = fmat4; - /// @} - - /** - * *n* by *m* matrix of double-precision floating-point numbers. - * - * @tparam N Number of columns. - * @tparam M Number of rows. - */ - /// @{ - template - using dmat = math::matrix; - using dmat2x2 = dmat<2, 2>; - using dmat2x3 = dmat<2, 3>; - using dmat2x4 = dmat<2, 4>; - using dmat3x2 = dmat<3, 2>; - using dmat3x3 = dmat<3, 3>; - using dmat3x4 = dmat<3, 4>; - using dmat4x2 = dmat<4, 2>; - using dmat4x3 = dmat<4, 3>; - using dmat4x4 = dmat<4, 4>; - /// @} - - /** - * *n* by *n* square matrix of double-precision floating-point numbers. - * - * @tparam N Number of columns and rows. - */ - /// @{ - using dmat2 = dmat2x2; - using dmat3 = dmat3x3; - using dmat4 = dmat4x4; - /// @} - - /// @} - - /// @name Quaternion types - /// @{ - - /** - * Quaternion with floating-point scalars. - * - * @tparam T Scalar type. - */ - /// @{ - using fquat = math::quaternion; - using quat = fquat; - /// @} - - /** - * Quaternion with double-precision floating-point scalars. - * - * @tparam T Scalar type. - */ - using dquat = math::quaternion; - - /// @} - -} // namespace types - -// Bring GLSL types into glsl namespace -using namespace types; - -} // namespace glsl -} // namespace math - -#endif // ANTKEEPER_MATH_GLSL_HPP diff --git a/src/math/hash/hash.hpp b/src/math/hash/hash.hpp deleted file mode 100644 index dba2550..0000000 --- a/src/math/hash/hash.hpp +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_MATH_HASH_HPP -#define ANTKEEPER_MATH_HASH_HPP - -namespace math { - -/// Hash functions. -namespace hash {} - -} // namespace math - -#include "math/hash/make-uint.hpp" -#include "math/hash/pcg.hpp" - -#endif // ANTKEEPER_MATH_HASH_HPP diff --git a/src/math/hash/pcg.hpp b/src/math/hash/pcg.hpp deleted file mode 100644 index 71f841e..0000000 --- a/src/math/hash/pcg.hpp +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_MATH_HASH_PCG_HPP -#define ANTKEEPER_MATH_HASH_PCG_HPP - -#include "math/vector.hpp" -#include "math/hash/make-uint.hpp" -#include -#include - -namespace math { -namespace hash { - -/// @private -template -constexpr T pcg_multiplier = 0; -template <> -constexpr std::uint8_t pcg_multiplier = 141U; -template <> -constexpr std::uint16_t pcg_multiplier = 12829U; -template <> -constexpr std::uint32_t pcg_multiplier = 747796405UL; -template <> -constexpr std::uint64_t pcg_multiplier = 6364136223846793005ULL; - -/// @private -template -constexpr T pcg_increment = 0; -template <> -constexpr std::uint8_t pcg_increment = 77U; -template <> -constexpr std::uint16_t pcg_increment = 47989U; -template <> -constexpr std::uint32_t pcg_increment = 2891336453UL; -template <> -constexpr std::uint64_t pcg_increment = 1442695040888963407ULL; - -/// @private -template -constexpr T mcg_multiplier = 0; -template <> -constexpr std::uint8_t mcg_multiplier = 217U; -template <> -constexpr std::uint16_t mcg_multiplier = 62169U; -template <> -constexpr std::uint32_t mcg_multiplier = 277803737UL; -template <> -constexpr std::uint64_t mcg_multiplier = 12605985483714917081ULL; - -/// @private -template -[[nodiscard]] constexpr T pcg_uint(T x) noexcept -{ - static_assert(std::is_integral::value && std::is_unsigned::value); - static_assert(sizeof(T) <= 8); - - x = x * pcg_multiplier + pcg_increment; - x = (x ^ (x >> ((x >> ((sizeof(T) * 8) - (sizeof(T) + 1))) + (sizeof(T) + 1)))) * mcg_multiplier; - return x ^ (x >> ((sizeof(T) * 16 + 2) / 3)); -} - -/// @private -template -[[nodiscard]] inline constexpr vector pcg_uvec1(vector x) noexcept -{ - static_assert(std::is_integral::value && std::is_unsigned::value); - static_assert(sizeof(T) <= 8); - - x[0] = T(x[0]); - - return x; -} - -/// @private -template -[[nodiscard]] constexpr vector pcg_uvec2(vector x) noexcept -{ - static_assert(std::is_integral::value && std::is_unsigned::value); - static_assert(sizeof(T) <= 8); - - x = x * pcg_multiplier + pcg_increment; - - x[0] += x[1] * pcg_multiplier; - x[1] += x[0] * pcg_multiplier; - - x[0] ^= x[0] >> (sizeof(T) * 4); - x[1] ^= x[1] >> (sizeof(T) * 4); - - x[0] += x[1] * pcg_multiplier; - x[1] += x[0] * pcg_multiplier; - - x[0] ^= x[0] >> (sizeof(T) * 4); - x[1] ^= x[1] >> (sizeof(T) * 4); - - return x; -} - -/// @private -template -[[nodiscard]] constexpr vector pcg_uvec3(vector x) noexcept -{ - static_assert(std::is_integral::value && std::is_unsigned::value); - static_assert(sizeof(T) <= 8); - - x = x * pcg_multiplier + pcg_increment; - - x[0] += x[1] * x[2]; - x[1] += x[2] * x[0]; - x[2] += x[0] * x[1]; - - x[0] ^= x[0] >> (sizeof(T) * 4); - x[1] ^= x[1] >> (sizeof(T) * 4); - x[2] ^= x[2] >> (sizeof(T) * 4); - - x[0] += x[1] * x[2]; - x[1] += x[2] * x[0]; - x[2] += x[0] * x[1]; - - return x; -} - -/// @private -template -[[nodiscard]] constexpr vector pcg_uvec4(vector x) noexcept -{ - static_assert(std::is_integral::value && std::is_unsigned::value); - static_assert(sizeof(T) <= 8); - - x = x * pcg_multiplier + pcg_increment; - - x[0] += x[1] * x[3]; - x[1] += x[2] * x[0]; - x[2] += x[0] * x[1]; - x[3] += x[1] * x[2]; - - x[0] ^= x[0] >> (sizeof(T) * 4); - x[1] ^= x[1] >> (sizeof(T) * 4); - x[2] ^= x[2] >> (sizeof(T) * 4); - x[3] ^= x[3] >> (sizeof(T) * 4); - - x[0] += x[1] * x[3]; - x[1] += x[2] * x[0]; - x[2] += x[0] * x[1]; - x[3] += x[1] * x[2]; - - return x; -} - -/** - * PCG hash function. - * - * @param x Input value. - * - * @return Unsigned pseudorandom output value. - * - * @warning Floating point and signed input values will be converted to unsigned integers via `static_cast`. - * @warning Vectors with more than 4 elements are not supported. - * - * @see https://en.wikipedia.org/wiki/Permuted_congruential_generator - * @see O'Neill, M.E. (2014). PCG : A Family of Simple Fast Space-Efficient Statistically Good Algorithms for Random Number Generation. - * @see Mark Jarzynski and Marc Olano, Hash Functions for GPU Rendering, Journal of Computer Graphics Techniques (JCGT), vol. 9, no. 3, 21-38, 2020. - */ -/// @{ -[[nodiscard]] inline constexpr std::uint8_t pcg(std::uint8_t x) noexcept -{ - return pcg_uint(x); -} - -[[nodiscard]] inline constexpr std::uint16_t pcg(std::uint16_t x) noexcept -{ - return pcg_uint(x); -} - -[[nodiscard]] inline constexpr std::uint32_t pcg(std::uint32_t x) noexcept -{ - return pcg_uint(x); -} - -[[nodiscard]] inline constexpr std::uint64_t pcg(std::uint64_t x) noexcept -{ - return pcg_uint(x); -} - -[[nodiscard]] inline constexpr std::uint8_t pcg(std::int8_t x) noexcept -{ - return pcg_uint(static_cast(x)); -} - -[[nodiscard]] inline constexpr std::uint16_t pcg(std::int16_t x) noexcept -{ - return pcg_uint(static_cast(x)); -} - -[[nodiscard]] inline constexpr std::uint32_t pcg(std::int32_t x) noexcept -{ - return pcg_uint(static_cast(x)); -} - -[[nodiscard]] inline constexpr std::uint64_t pcg(std::int64_t x) noexcept -{ - return pcg_uint(static_cast(x)); -} - -[[nodiscard]] inline constexpr std::uint32_t pcg(float x) noexcept -{ - return pcg_uint(static_cast(x)); -} - -[[nodiscard]] inline constexpr std::uint64_t pcg(double x) noexcept -{ - return pcg_uint(static_cast(x)); -} - -template -[[nodiscard]] inline constexpr vector, N> pcg(const vector& x) noexcept -{ - static_assert(N > 0 && N < 5, "PCG hash only supports vectors with 1-4 elements."); - - if constexpr (N == 1) - return pcg_uvec1>(vector, N>(x)); - else if constexpr (N == 2) - return pcg_uvec2>(vector, N>(x)); - else if constexpr (N == 3) - return pcg_uvec3>(vector, N>(x)); - else - return pcg_uvec4>(vector, N>(x)); -} -/// @} - -} // namespace hash -} // namespace math - -#endif // ANTKEEPER_MATH_HASH_PCG_HPP diff --git a/src/math/interpolation.hpp b/src/math/interpolation.hpp deleted file mode 100644 index d7846fe..0000000 --- a/src/math/interpolation.hpp +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_MATH_INTERPOLATION_HPP -#define ANTKEEPER_MATH_INTERPOLATION_HPP - -#include "math/angles.hpp" -#include - -namespace math { - -/** - * Linearly interpolates between @p x and @p y. - * - * Requires the following operators to be defined: - * - * T operator+(const T&, const T&); - * T operator-(const T&, const T&); - * T operator*(const T&, S); - * - * @tparam T Value type. - * @tparam S Scalar type. - */ -template -[[nodiscard]] constexpr T lerp(const T& x, const T& y, S a) -{ - return x + (y - x) * a; -} - -/** - * Linearly interpolates between two angles, @p x and @p y. - * - * @tparam T Value type. - * @param x Start angle, in radians. - * @param y End angle, in radians. - * @param a Interpolation ratio. - * @return Interpolated angle, in radians. - */ -template -[[nodiscard]] constexpr T lerp_angle(T x, T y, T a) -{ - return wrap_radians(x + wrap_radians(y - x) * a); -} - -/** - * Logarithmically interpolates between @p x and @p y. - * - * @warning Undefined behavior when @p x is zero. - * - * @tparam T Value type. - * @tparam S Scalar type. - */ -template -[[nodiscard]] T log_lerp(const T& x, const T& y, S a) -{ - //return std::exp(linear(std::log(x), std::log(y), a)); - return x * std::pow(y / x, a); -} - -} // namespace math - -#endif // ANTKEEPER_MATH_INTERPOLATION_HPP diff --git a/src/math/linear-algebra.hpp b/src/math/linear-algebra.hpp deleted file mode 100644 index f7e2962..0000000 --- a/src/math/linear-algebra.hpp +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_MATH_LINEAR_ALGEBRA_HPP -#define ANTKEEPER_MATH_LINEAR_ALGEBRA_HPP - -#include "math/matrix.hpp" -#include "math/quaternion.hpp" -#include "math/vector.hpp" - -#endif // ANTKEEPER_MATH_LINEAR_ALGEBRA_HPP diff --git a/src/math/math.hpp b/src/math/math.hpp deleted file mode 100644 index 29bdd93..0000000 --- a/src/math/math.hpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_MATH_HPP -#define ANTKEEPER_MATH_HPP - -/// Mathematical functions and data types. -namespace math {} - -#include "math/vector.hpp" -#include "math/matrix.hpp" -#include "math/quaternion.hpp" - -#include "math/se3.hpp" -#include "math/transform-type.hpp" -#include "math/transform-functions.hpp" -#include "math/transform-operators.hpp" - -#include "math/angles.hpp" -#include "math/numbers.hpp" -#include "math/quadrature.hpp" -#include "math/interpolation.hpp" -#include "math/map.hpp" -#include "math/projection.hpp" - -#endif // ANTKEEPER_MATH_HPP diff --git a/src/math/matrix.hpp b/src/math/matrix.hpp deleted file mode 100644 index b5e282f..0000000 --- a/src/math/matrix.hpp +++ /dev/null @@ -1,1381 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_MATH_MATRIX_HPP -#define ANTKEEPER_MATH_MATRIX_HPP - -#include "math/vector.hpp" -#include -#include -#include -#include -#include -#include - -namespace math { - -/** - * *n* by *m* column-major matrix. - * - * @tparam T Element type. - * @tparam N Number of columns. - * @tparam M Number of rows. - * - * @see https://en.wikipedia.org/wiki/Row-_and_column-major_order - */ -template -struct matrix -{ - /// Element type. - typedef T element_type; - - /// Number of columns. - static constexpr std::size_t column_count = N; - - /// Number of rows. - static constexpr std::size_t row_count = M; - - /// Number of elements. - static constexpr std::size_t element_count = column_count * row_count; - - /// Matrix column vector data type. - typedef vector column_vector_type; - - /// Matrix row vector data type. - typedef vector row_vector_type; - - /// Array of matrix column vectors. - column_vector_type columns[column_count]; - - /// @name Conversion - /// @{ - - /// @private - template - [[nodiscard]] inline constexpr matrix type_cast(std::index_sequence) const noexcept - { - return {vector(columns[I])...}; - } - - /** - * Type-casts the elements of this matrix using `static_cast`. - * - * @tparam U Target element type. - * - * @return Matrix containing the type-casted elements. - */ - template - [[nodiscard]] inline constexpr explicit operator matrix() const noexcept - { - return type_cast(std::make_index_sequence{}); - } - - /// @private - template - [[nodiscard]] inline constexpr matrix size_cast(std::index_sequence) const noexcept - { - if constexpr (O == M) - return {((I < N) ? columns[I] : matrix::identity()[I]) ...}; - else - return {((I < N) ? vector(columns[I]) : matrix::identity()[I]) ...}; - } - - /** - * Size-casts this matrix to a matrix with different dimensions. Casting to greater dimensions causes new elements to be set to identity matrix elements. - * - * @tparam P Target number of columns. - * @tparam O Target number of rows. - * - * @return *p* by *o* matrix. - */ - template - [[nodiscard]] inline constexpr explicit operator matrix() const noexcept - { - return size_cast(std::make_index_sequence

{}); - } - - /// @} - - /// @name Column access - /// @{ - - /** - * Returns a reference to the column vector at a given index. - * - * @param i Index of a column vector. - * - * @return Reference to the column vector at index @p i. - */ - /// @{ - [[nodiscard]] inline constexpr column_vector_type& operator[](std::size_t i) noexcept - { - return columns[i]; - } - [[nodiscard]] inline constexpr const column_vector_type& operator[](std::size_t i) const noexcept - { - return columns[i]; - } - [[nodiscard]] inline constexpr column_vector_type& column(std::size_t i) noexcept - { - return columns[i]; - } - [[nodiscard]] inline constexpr const column_vector_type& column(std::size_t i) const noexcept - { - return columns[i]; - } - /// @} - - /** - * Returns a reference to the first column vector. - */ - /// @{ - [[nodiscard]] inline constexpr column_vector_type& front() noexcept - { - return columns[0]; - } - [[nodiscard]] inline constexpr const column_vector_type& front() const noexcept - { - return columns[0]; - } - /// @} - - /** - * Returns a reference to the last column vector. - */ - /// @{ - [[nodiscard]] inline constexpr column_vector_type& back() noexcept - { - return columns[column_count - 1]; - } - [[nodiscard]] inline constexpr const column_vector_type& back() const noexcept - { - return columns[column_count - 1]; - } - /// @} - - /// @} - - /// @name Element access - /// @{ - - /** - * Returns a reference to the element at a given column-major index. - * - * @param i Column-major index of a matrix element. - * - * @return Reference to the element at column-major index @p i. - */ - /// @{ - [[nodiscard]] inline constexpr T& element(std::size_t i) noexcept - { - return columns[i / row_count][i % row_count]; - } - [[nodiscard]] inline constexpr const T& element(std::size_t i) const noexcept - { - return columns[i / row_count][i % row_count]; - } - /// @} - - /** - * Returns a pointer to the first element. - * - * @warning If matrix::element_type is not a POD type, elements may not be stored contiguously. - */ - /// @{ - [[nodiscard]] inline constexpr element_type* data() noexcept - { - return &columns[0][0]; - }; - [[nodiscard]] inline constexpr const element_type* data() const noexcept - { - return &columns[0][0]; - }; - /// @} - - /// @} - - /// @name Iterators - /// @{ - - /** - * Returns an iterator to the first column vector. - */ - /// @{ - [[nodiscard]] inline constexpr column_vector_type* begin() noexcept - { - return columns; - } - [[nodiscard]] inline constexpr const column_vector_type* begin() const noexcept - { - return columns; - } - [[nodiscard]] inline constexpr const column_vector_type* cbegin() const noexcept - { - return columns; - } - /// @} - - /** - * Returns an iterator to the column vector following the last column vector. - */ - /// @{ - [[nodiscard]] inline constexpr column_vector_type* end() noexcept - { - return columns + column_count; - } - [[nodiscard]] inline constexpr const column_vector_type* end() const noexcept - { - return columns + column_count; - } - [[nodiscard]] inline constexpr const column_vector_type* cend() const noexcept - { - return columns + column_count; - } - /// @} - - /** - * Returns a reverse iterator to the first column vector of the reversed matrix. - */ - /// @{ - [[nodiscard]] inline constexpr std::reverse_iterator rbegin() noexcept - { - return std::reverse_iterator(columns + column_count); - } - [[nodiscard]] inline constexpr std::reverse_iterator rbegin() const noexcept - { - return std::reverse_iterator(columns + column_count); - } - [[nodiscard]] inline constexpr std::reverse_iterator crbegin() const noexcept - { - return std::reverse_iterator(columns + column_count); - } - /// @} - - /** - * Returns a reverse iterator to the column vector following the last column vector of the reversed matrix. - */ - /// @{ - [[nodiscard]] inline constexpr std::reverse_iterator rend() noexcept - { - return std::reverse_iterator(columns); - } - [[nodiscard]] inline constexpr std::reverse_iterator rend() const noexcept - { - return std::reverse_iterator(columns); - } - [[nodiscard]] inline constexpr std::reverse_iterator crend() const noexcept - { - return std::reverse_iterator(columns); - } - /// @} - - /// @} - - /// @name Capacity - /// @{ - - /** - * Returns the number of elements in the matrix. - */ - [[nodiscard]] inline constexpr std::size_t size() const noexcept - { - return element_count; - }; - - /// @} - - /// @name Constants - /// @{ - - /** - * Returns a zero matrix, where every element is equal to zero. - */ - [[nodiscard]] static constexpr matrix zero() noexcept - { - return {}; - } - - /// @private - template - [[nodiscard]] static inline constexpr matrix one(std::index_sequence) noexcept - { - //return {column_vector_type::one() ...}; - - // MSVC bug workaround (I must be referenced for parameter pack expansion) - return {(I ? column_vector_type::one() : column_vector_type::one()) ...}; - } - - /** - * Returns a matrix of ones, where every element is equal to one. - */ - [[nodiscard]] static constexpr matrix one() noexcept - { - return one(std::make_index_sequence{}); - } - - /// @private - template - [[nodiscard]] static inline constexpr column_vector_type identity_column(std::size_t i, std::index_sequence) noexcept - { - return {(I == i ? T{1} : T{0}) ...}; - } - - /// @private - template - [[nodiscard]] static inline constexpr matrix identity(std::index_sequence) noexcept - { - return {identity_column(I, std::make_index_sequence{}) ...}; - } - - /** - * Returns an identity matrix, with ones on the main diagonal and zeros elsewhere. - */ - [[nodiscard]] static constexpr matrix identity() noexcept - { - return identity(std::make_index_sequence{}); - } - - /// @} -}; - -/// 2x2 matrix. -template -using matrix2 = matrix; - -/// 2x2 matrix. -template -using matrix2x2 = matrix; - -/// 3x3 matrix. -template -using matrix3 = matrix; - -/// 3x3 matrix. -template -using matrix3x3 = matrix; - -/// 4x4 matrix. -template -using matrix4 = matrix; - -/// 4x4 matrix. -template -using matrix4x4 = matrix; - -/** - * Adds two matrices. - * - * @param a First matrix. - * @param b Second matrix. - * - * @return Sum of the two matrices. - */ -template -[[nodiscard]] constexpr matrix add(const matrix& a, const matrix& b) noexcept; - -/** - * Adds a matrix and a scalar. - * - * @param a Matrix. - * @param b scalar. - * - * @return Sum of the matrix and scalar. - */ -template -[[nodiscard]] constexpr matrix add(const matrix& a, T b) noexcept; - -/** - * Calculates the determinant of a square matrix. - * - * @param m Matrix of which to take the determinant. - * - * @return Determinant of @p m. - * - * @warning Currently only implemented for 2x2, 3x3, and 4x4 matrices. - */ -template -[[nodiscard]] constexpr T determinant(const matrix& m) noexcept; - -/** - * Performs a component-wise multiplication of two matrices. - * - * @param x First matrix multiplicand. - * @param y Second matrix multiplicand. - * - * @return Product of the component-wise multiplcation. - */ -template -[[nodiscard]] constexpr matrix componentwise_mul(const matrix& a, const matrix& b) noexcept; - -/** - * Divides a matrix by a matrix. - * - * @param a First matrix. - * @param b Second matrix. - * - * @return Result of the division. - */ -template -[[nodiscard]] constexpr matrix div(const matrix& a, const matrix& b) noexcept; - -/** - * Divides a matrix by a scalar. - * - * @param a Matrix. - * @param b Scalar. - * - * @return Result of the division. - */ -template -[[nodiscard]] constexpr matrix div(const matrix& a, T b) noexcept; - -/** - * Divides a scalar by a matrix. - * - * @param a Scalar. - * @param b Matrix. - * - * @return Result of the division. - */ -template -[[nodiscard]] constexpr matrix div(T a, const matrix& b) noexcept; - -/** - * Extracts the Ith column from a matrix. - * - * @tparam I Index of a column. - * @tparam T Element type. - * @tparam N Number of columns. - * @tparam M Number of rows. - * - * @param m Matrix from which to extract a column. - * - * @return Reference to the Ith column of @p m. - */ -/// @{ -template -[[nodiscard]] constexpr typename matrix::column_vector_type& get(math::matrix& m) noexcept; -template -[[nodiscard]] constexpr typename matrix::column_vector_type&& get(math::matrix&& m) noexcept; -template -[[nodiscard]] constexpr const typename matrix::column_vector_type& get(const math::matrix& m) noexcept; -template -[[nodiscard]] constexpr const typename matrix::column_vector_type&& get(const math::matrix&& m) noexcept; -/// @} - -/** - * Calculates the inverse of a square matrix. - * - * @param m Square matrix. - * - * @return Inverse of matrix @p m. - * - * @warning Currently only implemented for 2x2, 3x3, and 4x4 matrices. - */ -template -[[nodiscard]] constexpr matrix inverse(const matrix& m) noexcept; - -/** - * Creates a viewing transformation matrix. - * - * @param position Position of the view point. - * @param target Position of the target. - * @param up Normalized direction of the up vector. - * - * @return Viewing transformation matrix. - */ -template -[[nodiscard]] constexpr matrix look_at(const vector& position, const vector& target, vector up); - -/** - * Multiplies two matrices - * - * @tparam T Matrix element type. - * @tparam N Number of columns in matrix @p a, and rows in matrix @p b. - * @tparam M Number of rows in matrix @p a. - * @tparam P Number of columns in matrix @p b. - * - * @param a First matrix. - * @param b Second matrix. - * - * @return Product of `a * b`. - */ -template -[[nodiscard]] constexpr matrix mul(const matrix& a, const matrix& b) noexcept; - -/** - * Multiplies a matrix by a scalar. - * - * @param a Matrix. - * @param b Scalar. - * - * @return Product of the matrix and the scalar. - */ -template -[[nodiscard]] constexpr matrix mul(const matrix& a, T b) noexcept; - -/** - * Calculates the product of a matrix and a row vector. - * - * @param a Matrix. - * @param b Row vector - * - * @return Product of the matrix and the row vector. - */ -template -[[nodiscard]] constexpr typename matrix::column_vector_type mul(const matrix& a, const typename matrix::row_vector_type& b) noexcept; - -/** - * Calculates the product of a column vector and a matrix. - * - * @param a Column vector. - * @param b Matrix. - * - * @return Product of the column vector and the matrix. - */ -template -[[nodiscard]] constexpr typename matrix::row_vector_type mul(const typename matrix::column_vector_type& a, const matrix& b) noexcept; - -/** - * Constructs a rotation matrix. - * - * @param angle Angle of rotation, in radians. - * @param axis Axis of rotation. - * - * @return Rotation matrix. - */ -template -[[nodiscard]] matrix rotate(T angle, const vector& axis); - -/** - * Produces a matrix which rotates Cartesian coordinates about the x-axis by a given angle. - * - * @param angle Angle of rotation, in radians. - * - * @return Rotation matrix. - */ -template -[[nodiscard]] matrix3 rotate_x(T angle); - -/** - * Produces a matrix which rotates Cartesian coordinates about the y-axis by a given angle. - * - * @param angle Angle of rotation, in radians. - * - * @return Rotation matrix. - */ -template -[[nodiscard]] matrix3 rotate_y(T angle); - -/** - * Produces a matrix which rotates Cartesian coordinates about the z-axis by a given angle. - * - * @param angle Angle of rotation, in radians. - * - * @return Rotation matrix. - */ -template -[[nodiscard]] matrix3 rotate_z(T angle); - -/** - * Scales a matrix. - * - * @param m Matrix to scale. - * @param v Scale vector. - * - * @return Scaled matrix. - */ -template -[[nodiscard]] constexpr matrix scale(const matrix& m, const vector& v); - -/** - * Subtracts a matrix from another matrix. - * - * @param a First matrix. - * @param b Second matrix. - * - * @return Difference between the two matrices. - */ -template -[[nodiscard]] constexpr matrix sub(const matrix& a, const matrix& b) noexcept; - -/** - * Subtracts a scalar from matrix. - * - * @param a Matrix. - * @param b Scalar. - * - * @return Difference between the matrix and scalar. - */ -template -[[nodiscard]] constexpr matrix sub(const matrix& a, T b) noexcept; - -/** - * Subtracts a matrix from a scalar. - * - * @param a Scalar. - * @param b Matrix. - * - * @return Difference between the scalar and matrix. - */ -template -[[nodiscard]] constexpr matrix sub(T a, const matrix& b) noexcept; - -/** - * Calculates the trace of a square matrix. - * - * @param m Square matrix. - * - * @return Sum of elements on the main diagonal. - */ -template -[[nodiscard]] constexpr T trace(const matrix& m) noexcept; - -/** - * Translates a matrix. - * - * @param m Matrix to translate. - * @param v Translation vector. - * - * @return Translated matrix. - */ -template -[[nodiscard]] constexpr matrix translate(const matrix& m, const vector& v); - -/** - * Calculates the transpose of a matrix. - * - * @param m Matrix to transpose. - * - * @return Transposed matrix. - */ -template -[[nodiscard]] constexpr matrix transpose(const matrix& m) noexcept; - -/// @private -template -inline constexpr matrix add(const matrix& a, const matrix& b, std::index_sequence) noexcept -{ - return {(a[I] + b[I]) ...}; -} - -template -constexpr matrix add(const matrix& a, const matrix& b) noexcept -{ - return add(a, b, std::make_index_sequence{}); -} - -/// @private -template -inline constexpr matrix add(const matrix& a, T b, std::index_sequence) noexcept -{ - return {(a[I] + b) ...}; -} - -template -constexpr matrix add(const matrix& a, T b) noexcept -{ - return add(a, b, std::make_index_sequence{}); -} - -/// @private -template -constexpr T determinant(const matrix& m) noexcept -{ - return - m[0][0] * m[1][1] - - m[0][1] * m[1][0]; -} - -/// @private -template -constexpr T determinant(const matrix& m) noexcept -{ - return - m[0][0] * m[1][1] * m[2][2] + - m[0][1] * m[1][2] * m[2][0] + - m[0][2] * m[1][0] * m[2][1] - - m[0][0] * m[1][2] * m[2][1] - - m[0][1] * m[1][0] * m[2][2] - - m[0][2] * m[1][1] * m[2][0]; -} - -/// @private -template -constexpr T determinant(const matrix& m) noexcept -{ - return - m[0][3] * m[1][2] * m[2][1] * m[3][0] - m[0][2] * m[1][3] * m[2][1] * m[3][0] - - m[0][3] * m[1][1] * m[2][2] * m[3][0] + m[0][1] * m[1][3] * m[2][2] * m[3][0] + - m[0][2] * m[1][1] * m[2][3] * m[3][0] - m[0][1] * m[1][2] * m[2][3] * m[3][0] - - m[0][3] * m[1][2] * m[2][0] * m[3][1] + m[0][2] * m[1][3] * m[2][0] * m[3][1] + - m[0][3] * m[1][0] * m[2][2] * m[3][1] - m[0][0] * m[1][3] * m[2][2] * m[3][1] - - m[0][2] * m[1][0] * m[2][3] * m[3][1] + m[0][0] * m[1][2] * m[2][3] * m[3][1] + - m[0][3] * m[1][1] * m[2][0] * m[3][2] - m[0][1] * m[1][3] * m[2][0] * m[3][2] - - m[0][3] * m[1][0] * m[2][1] * m[3][2] + m[0][0] * m[1][3] * m[2][1] * m[3][2] + - m[0][1] * m[1][0] * m[2][3] * m[3][2] - m[0][0] * m[1][1] * m[2][3] * m[3][2] - - m[0][2] * m[1][1] * m[2][0] * m[3][3] + m[0][1] * m[1][2] * m[2][0] * m[3][3] + - m[0][2] * m[1][0] * m[2][1] * m[3][3] - m[0][0] * m[1][2] * m[2][1] * m[3][3] - - m[0][1] * m[1][0] * m[2][2] * m[3][3] + m[0][0] * m[1][1] * m[2][2] * m[3][3]; -} - -/// @private -template -inline constexpr matrix componentwise_mul(const matrix& a, const matrix& b, std::index_sequence) noexcept -{ - return {(a[I] * b[I]) ...}; -} - -template -constexpr matrix componentwise_mul(const matrix& a, const matrix& b) noexcept -{ - return componentwise_mul(a, b, std::make_index_sequence{}); -} - -/// @private -template -inline constexpr matrix div(const matrix& a, const matrix& b, std::index_sequence) noexcept -{ - return {(a[I] / b[I]) ...}; -} - -template -constexpr matrix div(const matrix& a, const matrix& b) noexcept -{ - return div(a, b, std::make_index_sequence{}); -} - -/// @private -template -inline constexpr matrix div(const matrix& a, T b, std::index_sequence) noexcept -{ - return {(a[I] / b) ...}; -} - -template -constexpr matrix div(const matrix& a, T b) noexcept -{ - return div(a, b, std::make_index_sequence{}); -} - -/// @private -template -inline constexpr matrix div(T a, const matrix& b, std::index_sequence) noexcept -{ - return {(a / b[I]) ...}; -} - -template -constexpr matrix div(T a, const matrix& b) noexcept -{ - return div(a, b, std::make_index_sequence{}); -} - -template -inline constexpr typename matrix::column_vector_type& get(math::matrix& m) noexcept -{ - static_assert(I < N); - return m.columns[I]; -} - -template -inline constexpr typename matrix::column_vector_type&& get(math::matrix&& m) noexcept -{ - static_assert(I < N); - return std::move(m.columns[I]); -} - -template -inline constexpr const typename matrix::column_vector_type& get(const math::matrix& m) noexcept -{ - static_assert(I < N); - return m.columns[I]; -} - -template -inline constexpr const typename matrix::column_vector_type&& get(const math::matrix&& m) noexcept -{ - static_assert(I < N); - return std::move(m.columns[I]); -} - -/// @private -template -constexpr matrix inverse(const matrix& m) noexcept -{ - const T inv_det = T{1} / determinant(m); - - return - { - m[1][1] * inv_det, - -m[0][1] * inv_det, - -m[1][0] * inv_det, - m[0][0] * inv_det - }; -} - -/// @private -template -constexpr matrix inverse(const matrix& m) noexcept -{ - const T inv_det = T{1} / determinant(m); - - return - { - (m[1][1] * m[2][2] - m[1][2] * m[2][1]) * inv_det, - (m[0][2] * m[2][1] - m[0][1] * m[2][2]) * inv_det, - (m[0][1] * m[1][2] - m[0][2] * m[1][1]) * inv_det, - - (m[1][2] * m[2][0] - m[1][0] * m[2][2]) * inv_det, - (m[0][0] * m[2][2] - m[0][2] * m[2][0]) * inv_det, - (m[0][2] * m[1][0] - m[0][0] * m[1][2]) * inv_det, - - (m[1][0] * m[2][1] - m[1][1] * m[2][0]) * inv_det, - (m[0][1] * m[2][0] - m[0][0] * m[2][1]) * inv_det, - (m[0][0] * m[1][1] - m[0][1] * m[1][0]) * inv_det - }; -} - -/// @private -template -constexpr matrix inverse(const matrix& m) noexcept -{ - const T inv_det = T{1} / determinant(m); - - return - { - (m[1][2] * m[2][3] * m[3][1] - m[1][3] * m[2][2] * m[3][1] + m[1][3] * m[2][1] * m[3][2] - m[1][1] * m[2][3] * m[3][2] - m[1][2] * m[2][1] * m[3][3] + m[1][1] * m[2][2] * m[3][3]) * inv_det, - (m[0][3] * m[2][2] * m[3][1] - m[0][2] * m[2][3] * m[3][1] - m[0][3] * m[2][1] * m[3][2] + m[0][1] * m[2][3] * m[3][2] + m[0][2] * m[2][1] * m[3][3] - m[0][1] * m[2][2] * m[3][3]) * inv_det, - (m[0][2] * m[1][3] * m[3][1] - m[0][3] * m[1][2] * m[3][1] + m[0][3] * m[1][1] * m[3][2] - m[0][1] * m[1][3] * m[3][2] - m[0][2] * m[1][1] * m[3][3] + m[0][1] * m[1][2] * m[3][3]) * inv_det, - (m[0][3] * m[1][2] * m[2][1] - m[0][2] * m[1][3] * m[2][1] - m[0][3] * m[1][1] * m[2][2] + m[0][1] * m[1][3] * m[2][2] + m[0][2] * m[1][1] * m[2][3] - m[0][1] * m[1][2] * m[2][3]) * inv_det, - - (m[1][3] * m[2][2] * m[3][0] - m[1][2] * m[2][3] * m[3][0] - m[1][3] * m[2][0] * m[3][2] + m[1][0] * m[2][3] * m[3][2] + m[1][2] * m[2][0] * m[3][3] - m[1][0] * m[2][2] * m[3][3]) * inv_det, - (m[0][2] * m[2][3] * m[3][0] - m[0][3] * m[2][2] * m[3][0] + m[0][3] * m[2][0] * m[3][2] - m[0][0] * m[2][3] * m[3][2] - m[0][2] * m[2][0] * m[3][3] + m[0][0] * m[2][2] * m[3][3]) * inv_det, - (m[0][3] * m[1][2] * m[3][0] - m[0][2] * m[1][3] * m[3][0] - m[0][3] * m[1][0] * m[3][2] + m[0][0] * m[1][3] * m[3][2] + m[0][2] * m[1][0] * m[3][3] - m[0][0] * m[1][2] * m[3][3]) * inv_det, - (m[0][2] * m[1][3] * m[2][0] - m[0][3] * m[1][2] * m[2][0] + m[0][3] * m[1][0] * m[2][2] - m[0][0] * m[1][3] * m[2][2] - m[0][2] * m[1][0] * m[2][3] + m[0][0] * m[1][2] * m[2][3]) * inv_det, - - (m[1][1] * m[2][3] * m[3][0] - m[1][3] * m[2][1] * m[3][0] + m[1][3] * m[2][0] * m[3][1] - m[1][0] * m[2][3] * m[3][1] - m[1][1] * m[2][0] * m[3][3] + m[1][0] * m[2][1] * m[3][3]) * inv_det, - (m[0][3] * m[2][1] * m[3][0] - m[0][1] * m[2][3] * m[3][0] - m[0][3] * m[2][0] * m[3][1] + m[0][0] * m[2][3] * m[3][1] + m[0][1] * m[2][0] * m[3][3] - m[0][0] * m[2][1] * m[3][3]) * inv_det, - (m[0][1] * m[1][3] * m[3][0] - m[0][3] * m[1][1] * m[3][0] + m[0][3] * m[1][0] * m[3][1] - m[0][0] * m[1][3] * m[3][1] - m[0][1] * m[1][0] * m[3][3] + m[0][0] * m[1][1] * m[3][3]) * inv_det, - (m[0][3] * m[1][1] * m[2][0] - m[0][1] * m[1][3] * m[2][0] - m[0][3] * m[1][0] * m[2][1] + m[0][0] * m[1][3] * m[2][1] + m[0][1] * m[1][0] * m[2][3] - m[0][0] * m[1][1] * m[2][3]) * inv_det, - - (m[1][2] * m[2][1] * m[3][0] - m[1][1] * m[2][2] * m[3][0] - m[1][2] * m[2][0] * m[3][1] + m[1][0] * m[2][2] * m[3][1] + m[1][1] * m[2][0] * m[3][2] - m[1][0] * m[2][1] * m[3][2]) * inv_det, - (m[0][1] * m[2][2] * m[3][0] - m[0][2] * m[2][1] * m[3][0] + m[0][2] * m[2][0] * m[3][1] - m[0][0] * m[2][2] * m[3][1] - m[0][1] * m[2][0] * m[3][2] + m[0][0] * m[2][1] * m[3][2]) * inv_det, - (m[0][2] * m[1][1] * m[3][0] - m[0][1] * m[1][2] * m[3][0] - m[0][2] * m[1][0] * m[3][1] + m[0][0] * m[1][2] * m[3][1] + m[0][1] * m[1][0] * m[3][2] - m[0][0] * m[1][1] * m[3][2]) * inv_det, - (m[0][1] * m[1][2] * m[2][0] - m[0][2] * m[1][1] * m[2][0] + m[0][2] * m[1][0] * m[2][1] - m[0][0] * m[1][2] * m[2][1] - m[0][1] * m[1][0] * m[2][2] + m[0][0] * m[1][1] * m[2][2]) * inv_det - }; -} - -template -constexpr matrix look_at(const vector& position, const vector& target, vector up) -{ - vector forward = normalize(sub(target, position)); - vector right = normalize(cross(forward, up)); - up = cross(right, forward); - - matrix m = - {{ - {right[0], up[0], -forward[0], T(0)}, - {right[1], up[1], -forward[1], T(0)}, - {right[2], up[2], -forward[2], T(0)}, - {T(0), T(0), T(0), T(1)} - }}; - - return translate(m, negate(position)); -} - -template -constexpr matrix mul(const matrix& a, const matrix& b) noexcept -{ - matrix c = matrix::zero(); - - for (std::size_t i = 0; i < P; ++i) - { - for (std::size_t j = 0; j < M; ++j) - { - for (std::size_t k = 0; k < N; ++k) - { - c[i][j] += a[k][j] * b[i][k]; - } - } - } - - return c; -} - -/// @private -template -inline constexpr matrix mul(const matrix& a, T b, std::index_sequence) noexcept -{ - return {(a[I] * b) ...}; -} - -template -constexpr matrix mul(const matrix& a, T b) noexcept -{ - return mul(a, b, std::make_index_sequence{}); -} - -/// @private -template -inline constexpr typename matrix::column_vector_type mul(const matrix& a, const typename matrix::row_vector_type& b, std::index_sequence) noexcept -{ - return ((a[I] * b[I]) + ...); -} - -template -constexpr typename matrix::column_vector_type mul(const matrix& a, const typename matrix::row_vector_type& b) noexcept -{ - return mul(a, b, std::make_index_sequence{}); -} - -/// @private -template -inline constexpr typename matrix::row_vector_type mul(const typename matrix::column_vector_type& a, const matrix& b, std::index_sequence) noexcept -{ - return {dot(a, b[I]) ...}; -} - -template -constexpr typename matrix::row_vector_type mul(const typename matrix::column_vector_type& a, const matrix& b) noexcept -{ - return mul(a, b, std::make_index_sequence{}); -} - -template -matrix rotate(T angle, const vector& axis) -{ - const T c = std::cos(angle); - const T s = std::sin(angle); - const vector temp = mul(axis, T(1) - c); - - matrix rotation; - rotation[0][0] = axis[0] * temp[0] + c; - rotation[0][1] = axis[1] * temp[0] + axis[2] * s; - rotation[0][2] = axis[2] * temp[0] - axis[1] * s; - rotation[1][0] = axis[0] * temp[1] - axis[2] * s; - rotation[1][1] = axis[1] * temp[1] + c; - rotation[1][2] = axis[2] * temp[1] + axis[0] * s; - rotation[2][0] = axis[0] * temp[2] + axis[1] * s; - rotation[2][1] = axis[1] * temp[2] - axis[0] * s; - rotation[2][2] = axis[2] * temp[2] + c; - - return rotation; -} - -template -matrix3 rotate_x(T angle) -{ - const T c = std::cos(angle); - const T s = std::sin(angle); - - return matrix3 - { - T(1), T(0), T(0), - T(0), c, s, - T(0), -s, c - }; -} - -template -matrix3 rotate_y(T angle) -{ - const T c = std::cos(angle); - const T s = std::sin(angle); - - return matrix3 - { - c, T(0), -s, - T(0), T(1), T(0), - s, T(0), c - }; -} - -template -matrix3 rotate_z(T angle) -{ - const T c = std::cos(angle); - const T s = std::sin(angle); - - return matrix3 - { - c, s, T(0), - -s, c, T(0), - T(0), T(0), T(1) - }; -} - -template -constexpr matrix scale(const matrix& m, const vector& v) -{ - return mul(m, matrix - {{ - {v[0], T(0), T(0), T(0)}, - {T(0), v[1], T(0), T(0)}, - {T(0), T(0), v[2], T(0)}, - {T(0), T(0), T(0), T(1)} - }}); -} - -/// @private -template -inline constexpr matrix sub(const matrix& a, const matrix& b, std::index_sequence) noexcept -{ - return {(a[I] - b[I]) ...}; -} - -template -constexpr matrix sub(const matrix& a, const matrix& b) noexcept -{ - return sub(a, b, std::make_index_sequence{}); -} - -/// @private -template -inline constexpr matrix sub(const matrix& a, T b, std::index_sequence) noexcept -{ - return {(a[I] - b) ...}; -} - -template -constexpr matrix sub(const matrix& a, T b) noexcept -{ - return sub(a, b, std::make_index_sequence{}); -} - -/// @private -template -inline constexpr matrix sub(T a, const matrix& b, std::index_sequence) noexcept -{ - return {(a - b[I]) ...}; -} - -template -constexpr matrix sub(T a, const matrix& b) noexcept -{ - return sub(a, b, std::make_index_sequence{}); -} - -/// @private -template -inline constexpr T trace(const matrix& m, std::index_sequence) noexcept -{ - return ((m[I][I]) + ...); -} - -template -constexpr T trace(const matrix& m) noexcept -{ - return trace(m, std::make_index_sequence{}); -} - -template -constexpr matrix translate(const matrix& m, const vector& v) -{ - return mul(m, matrix - {{ - {T(1), T(0), T(0), T(0)}, - {T(0), T(1), T(0), T(0)}, - {T(0), T(0), T(1), T(0)}, - {v[0], v[1], v[2], T(1)} - }}); -} - -/// @private -template -inline constexpr typename matrix::column_vector_type transpose_column(const matrix& m, std::size_t i, std::index_sequence) noexcept -{ - return {m[I][i] ...}; -} - -/// @private -template -inline constexpr matrix transpose(const matrix& m, std::index_sequence) noexcept -{ - return {transpose_column(m, I, std::make_index_sequence{}) ...}; -} - -template -constexpr matrix transpose(const matrix& m) noexcept -{ - return transpose(m, std::make_index_sequence{}); -} - -namespace operators { - -/// @copydoc add(const matrix&, const matrix&) -template -inline constexpr matrix operator+(const matrix& a, const matrix& b) noexcept -{ - return add(a, b); -} - -/// @copydoc add(const matrix&, T) -/// @{ -template -inline constexpr matrix operator+(const matrix& a, T b) noexcept -{ - return add(a, b); -} -template -inline constexpr matrix operator+(T a, const matrix& b) noexcept -{ - return add(b, a); -} -/// @} - -/// @copydoc div(const matrix&, const matrix&) -template -inline constexpr matrix operator/(const matrix& a, const matrix& b) noexcept -{ - return div(a, b); -} - -/// @copydoc div(const matrix&, T) -template -inline constexpr matrix operator/(const matrix& a, T b) noexcept -{ - return div(a, b); -} - -/// @copydoc div(T, const matrix&) -template -inline constexpr matrix operator/(T a, const matrix& b) noexcept -{ - return div(a, b); -} - -/// @copydoc mul(const matrix&, const matrix&) -template -inline constexpr matrix operator*(const matrix& a, const matrix& b) noexcept -{ - return mul(a, b); -} - -/// @copydoc mul(const matrix&, T) -/// @{ -template -inline constexpr matrix operator*(const matrix& a, T b) noexcept -{ - return mul(a, b); -} -template -inline constexpr matrix operator*(T a, const matrix& b) noexcept -{ - return mul(b, a); -} -/// @} - -/// @copydoc mul(const matrix&, const typename matrix::row_vector_type&) -template -inline constexpr typename matrix::column_vector_type operator*(const matrix& a, const typename matrix::row_vector_type& b) noexcept -{ - return mul(a, b); -} - -/// @copydoc mul(const typename matrix::column_vector_type&, const matrix&) -template -inline constexpr typename matrix::row_vector_type operator*(const typename matrix::column_vector_type& a, const matrix& b) noexcept -{ - return mul(a, b); -} - -/// @copydoc sub(const matrix&, const matrix&) -template -inline constexpr matrix operator-(const matrix& a, const matrix& b) noexcept -{ - return sub(a, b); -} - -/// @copydoc sub(const matrix&, T) -template -inline constexpr matrix operator-(const matrix& a, T b) noexcept -{ - return sub(a, b); -} - -/// @copydoc sub(T, const matrix&) -template -inline constexpr matrix operator-(T a, const matrix& b) noexcept -{ - return sub(a, b); -} - -/** - * Adds two values and stores the result in the first value. - * - * @param a First value. - * @param b Second value. - * - * @return Reference to the first value. - */ -/// @{ -template -inline constexpr matrix& operator+=(matrix& a, const matrix& b) noexcept -{ - return (a = a + b); -} -template -inline constexpr matrix& operator+=(matrix& a, T b) noexcept -{ - return (a = a + b); -} -/// @} - -/** - * Subtracts the first value by the second value and stores the result in the first value. - * - * @param a First value. - * @param b Second value. - * - * @return Reference to the first value. - */ -/// @{ -template -inline constexpr matrix& operator-=(matrix& a, const matrix& b) noexcept -{ - return (a = a - b); -} -template -inline constexpr matrix& operator-=(matrix& a, T b) noexcept -{ - return (a = a - b); -} -/// @} - -/** - * Multiplies two values and stores the result in the first value. - * - * @param a First value. - * @param b Second value. - * - * @return Reference to the first value. - */ -/// @{ -template -inline constexpr matrix& operator*=(matrix& a, const matrix& b) noexcept -{ - return (a = a * b); -} -template -inline constexpr matrix& operator*=(matrix& a, T b) noexcept -{ - return (a = a * b); -} -/// @} - -/** - * Divides the first value by the second value and stores the result in the first value. - * - * @param a First value. - * @param b Second value. - * - * @return Reference to the first value. - */ -/// @{ -template -inline constexpr matrix& operator/=(matrix& a, const matrix& b) noexcept -{ - return (a = a / b); -} -template -inline constexpr matrix& operator/=(matrix& a, T b) noexcept -{ - return (a = a / b); -} -/// @} - -/** - * Writes the elements of a matrix to an output stream, with each element delimeted by a space. - * - * @param os Output stream. - * @param m Matrix. - * - * @return Output stream. - */ -template -std::ostream& operator<<(std::ostream& os, const matrix& m) -{ - for (std::size_t i = 0; i < m.size(); ++i) - { - if (i) - os << ' '; - os << m.element(i); - } - - return os; -} - -/** - * Reads the elements of a matrix from an input stream, with each element delimeted by a space. - * - * @param is Input stream. - * @param m Matrix. - * - * @return Input stream. - */ -template -std::istream& operator>>(std::istream& is, matrix& m) -{ - for (std::size_t i = 0; i < m.size(); ++i) - is >> m.element(i); - - return is; -} - -} // namespace operators - -} // namespace math - -using namespace math::operators; - -// Structured binding support -namespace std -{ - /** - * Provides access to the number of columns in a math::matrix as a compile-time constant expression. - * - * @tparam T Element type. - * @tparam N Number of columns. - * @tparam M Number of rows. - */ - template - struct tuple_size> - { - /// Number of columns in the matrix. - static constexpr std::size_t value = math::matrix::column_count; - }; - - /** - * Provides compile-time indexed access to the type of the columns in a math::matrix using a tuple-like interface. - * - * @tparam I Index of a column. - * @tparam T Element type. - * @tparam N Number of columns. - * @tparam M Number of rows. - */ - template - struct tuple_element> - { - /// Type of columns in the matrix. - using type = math::matrix::column_vector_type; - }; -} - -#endif // ANTKEEPER_MATH_MATRIX_HPP diff --git a/src/math/noise/fbm.hpp b/src/math/noise/fbm.hpp deleted file mode 100644 index de1d889..0000000 --- a/src/math/noise/fbm.hpp +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_MATH_NOISE_FBM_HPP -#define ANTKEEPER_MATH_NOISE_FBM_HPP - -#include "math/hash/make-uint.hpp" -#include "math/hash/pcg.hpp" -#include "math/noise/simplex.hpp" -#include "math/vector.hpp" -#include - -namespace math { -namespace noise { - -/** - * Fractional Brownian motion (fBm). - * - * @tparam T Real type. - * @tparam N Number of dimensions. - * - * @param position Input position. - * @param octaves Number of octaves. - * @param lacunarity Frequency multiplier. - * @param gain Amplitude multiplier. - * @param noise Noise function. - * @param hash Hash function. - * - */ -template -[[nodiscard]] T fbm -( - vector position, - std::size_t octaves, - T lacunarity, - T gain, - T (*noise)(const vector&, vector, N> (*)(const vector&)) = &simplex, - vector, N> (*hash)(const vector&) = &hash::pcg -) -{ - T amplitude{1}; - T value{0}; - - while (octaves--) - { - value += noise(position, hash) * amplitude; - position *= lacunarity; - amplitude *= gain; - } - - return value; -} - -} // namespace noise -} // namespace math - -#endif // ANTKEEPER_MATH_NOISE_FBM_HPP diff --git a/src/math/noise/noise.hpp b/src/math/noise/noise.hpp deleted file mode 100644 index 3763475..0000000 --- a/src/math/noise/noise.hpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_MATH_NOISE_HPP -#define ANTKEEPER_MATH_NOISE_HPP - -namespace math { - -/// Noise functions. -namespace noise {} - -} // namespace math - -#include "math/noise/fbm.hpp" -#include "math/noise/simplex.hpp" -#include "math/noise/voronoi.hpp" - -#endif // ANTKEEPER_MATH_NOISE_HPP diff --git a/src/math/noise/simplex.hpp b/src/math/noise/simplex.hpp deleted file mode 100644 index e4f7bfb..0000000 --- a/src/math/noise/simplex.hpp +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_MATH_NOISE_SIMPLEX_HPP -#define ANTKEEPER_MATH_NOISE_SIMPLEX_HPP - -#include "math/vector.hpp" -#include "math/hash/make-uint.hpp" -#include "math/hash/pcg.hpp" -#include -#include - -namespace math { -namespace noise { - -/** - * Number of corners in an *n*-dimensional simplex lattice cell. - * - * @private - */ -template -constexpr std::size_t simplex_corner_count = std::size_t(2) << std::max(0, N - 1); - -/** - * Number of edges in an *n*-dimensional simplex lattice cell. - * - * @private - */ -template -constexpr std::size_t simplex_edge_count = (N > 1) ? N * simplex_corner_count : 2; - -/** - * Returns the simplex lattice cell corner vector for a given dimension and index. - * - * @private - */ -template -[[nodiscard]] constexpr vector make_simplex_corner(std::size_t i, std::index_sequence) -{ - return {((i >> I) % 2) * T{2} - T{1}...}; -} - -/** - * Builds an array of simplex lattice cell corner vectors for a given dimension. - * - * @private - */ -template -[[nodiscard]] constexpr std::array, simplex_corner_count> make_simplex_corners(std::index_sequence) -{ - return {make_simplex_corner(I, std::make_index_sequence{})...}; -} - -/** - * Array of simplex lattice cell corner vectors for a given dimension. - * - * @private - */ -template -constexpr auto simplex_corners = make_simplex_corners(std::make_index_sequence>{}); - -/** - * Returns the simplex lattice cell edge vector for a given dimension and index. - * - * @private - */ -template -[[nodiscard]] constexpr vector make_simplex_edge(std::size_t i, std::index_sequence) -{ - std::size_t j = i / (simplex_edge_count / N); - - return - { - I < j ? - simplex_corners[i % simplex_corner_count][I] - : - I > j ? - simplex_corners[i % simplex_corner_count][I - 1] - : - T{0} - ... - }; -} - -/** - * Builds an array of simplex lattice cell edge vectors for a given dimension. - * - * @private - */ -template -[[nodiscard]] constexpr std::array, simplex_edge_count> make_simplex_edges(std::index_sequence) -{ - if constexpr (N == 1) - return std::array, simplex_edge_count>{vector{T{1}}, vector{T{-1}}}; - else - return {make_simplex_edge(I, std::make_index_sequence{})...}; -} - -/** - * Array of simplex lattice cell edge vectors for a given dimension. - * - * @private - */ -template -constexpr auto simplex_edges = make_simplex_edges(std::make_index_sequence>{}); - -/** - * *n*-dimensional simplex noise. - * - * @tparam T Real type. - * @tparam N Number of dimensions. - * - * @param position Input position. - * @param hash Hash function. - * - * @return Noise value, on `[-1, 1]`. - * - * @see https://en.wikipedia.org/wiki/Simplex_noise - * @see https://catlikecoding.com/unity/tutorials/pseudorandom-noise/simplex-noise/ - * @see https://briansharpe.wordpress.com/2012/01/13/simplex-noise/ - * @see https://briansharpe.wordpress.com/2011/11/14/two-useful-interpolation-functions-for-noise-development/ - * @see https://math.stackexchange.com/questions/474638/radius-and-amplitude-of-kernel-for-simplex-noise/1901116 - */ -template -[[nodiscard]] T simplex -( - const vector& position, - vector, N> (*hash)(const vector&) = &hash::pcg -) -{ - // Skewing (F) and unskewing (G) factors - static const T f = (std::sqrt(static_cast(N + 1)) - T{1}) / static_cast(N); - static const T g = f / (T{1} + f * static_cast(N)); - - // Kernel radius set to the height of the equilateral triangle, `sqrt(0.5)` - constexpr T sqr_kernel_radius = T{0.5}; - - /** - * C2-continuous kernel falloff function. - * - * @param sqr_distance Squared distance from the kernel center. - * - * @return Kernel strength at the given distance. - */ - auto falloff = [sqr_kernel_radius](T sqr_distance) constexpr - { - sqr_distance = sqr_kernel_radius - sqr_distance; - return sqr_distance * sqr_distance * sqr_distance; - }; - - // Normalization factor when using corner gradient vectors - // @see https://math.stackexchange.com/questions/474638/radius-and-amplitude-of-kernel-for-simplex-noise/1901116 - static const T corner_normalization = T{1} / ((static_cast(N) / std::sqrt(static_cast(N + 1))) * falloff(static_cast(N) / (T{4} * static_cast(N + 1)))); - - // Adjust normalization factor for difference in length between corner gradient vectors and edge gradient vectors - static const T edge_normalization = corner_normalization * (std::sqrt(static_cast(N)) / length(simplex_edges[0])); - - // Skew input position to get the origin vertex of the unit hypercube cell to which they belong - const vector origin_vertex = floor(position + sum(position) * f); - - // Displacement vector from origin vertex position to input position - const vector dx = position - origin_vertex + sum(origin_vertex) * g; - - // Find axis traversal order - vector axis_order; - for (std::size_t i = 0; i < N; ++i) - axis_order[i] = i; - std::sort - ( - axis_order.begin(), - axis_order.end(), - [&dx](std::size_t lhs, std::size_t rhs) - { - return dx[lhs] > dx[rhs]; - } - ); - - T n = T{0}; - vector current_vertex = origin_vertex; - for (std::size_t i = 0; i <= N; ++i) - { - if (i) - ++current_vertex[axis_order[i - 1]]; - - // Calculate displacement vector from current vertex to input position - const vector d = dx - (current_vertex - origin_vertex) + g * static_cast(i); - - // Calculate falloff - T t = falloff(sqr_length(d)); - if (t > T{0}) - { - const hash::make_uint_t gradient_index = hash(current_vertex)[0] % simplex_edges.size(); - const vector& gradient = simplex_edges[gradient_index]; - - n += dot(d, gradient) * t; - } - } - - // Normalize value - return n * edge_normalization; -} - -} // namespace noise -} // namespace math - -#endif // ANTKEEPER_MATH_NOISE_SIMPLEX_HPP diff --git a/src/math/noise/voronoi.hpp b/src/math/noise/voronoi.hpp deleted file mode 100644 index 8103b77..0000000 --- a/src/math/noise/voronoi.hpp +++ /dev/null @@ -1,429 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_MATH_NOISE_VORONOI_HPP -#define ANTKEEPER_MATH_NOISE_VORONOI_HPP - -#include "math/vector.hpp" -#include "math/hash/make-uint.hpp" -#include "math/hash/pcg.hpp" -#include -#include -#include -#include -#include -#include - -namespace math { -namespace noise { - -/// Voronoi functions. -namespace voronoi { - -/** - * Number of Voronoi cells to search. - * - * @tparam N Number of dimensions. - * - * @private - */ -template -constexpr std::size_t kernel_size = 4 << std::max(0, (2 * (N - 1))); - -/** - * Generates an kernel offset vector for a given index. - * - * @tparam T Real type. - * @tparam N Number of dimensions. - * @tparam I Index sequence. - * - * @param i Index of a kernel offset vector. - * - * @return Kernel offset vector. - * - * @private - */ -template -[[nodiscard]] constexpr vector kernel_offset(std::size_t i, std::index_sequence) -{ - return {static_cast((I ? (i / (2 << std::max(0, 2 * I - 1))) : i) % 4)...}; -} - -/** - * Generates a Voronoi search kernel. - * - * @tparam T Real type. - * @tparam N Number of dimensions. - * @tparam I Index sequence. - * - * @return Voronoi search kernel. - * - * @private - */ -template -[[nodiscard]] constexpr std::array, kernel_size> generate_kernel(std::index_sequence) -{ - return {kernel_offset(I, std::make_index_sequence{})...}; -} - -/** - * *n*-dimensional search kernel. - * - * @tparam T Real type. - * @tparam N Number of dimensions. - * - * @private - */ -template -constexpr auto kernel = generate_kernel(std::make_index_sequence>{}); - -/** - * Finds the Voronoi cell (F1) containing the input position. - * - * @tparam T Real type. - * @tparam N Number of dimensions. - * - * @param position Input position. - * @param randomness Degree of randomness, on `[0, 1]`. - * @param tiling Distance at which the Voronoi pattern should repeat. A value of `0` indicates no repetition. - * @param hash Hash function. - * - * @return Tuple containing the square Euclidean distance from @p position to the F1 cell, the displacement vector from the input position to the F1 cell center, and a hash value indicating the ID of the F1 cell. - */ -template -[[nodiscard]] std::tuple -< - // F1 square distance to center - T, - - // F1 position to center displacement - vector, - - // F1 hash - hash::make_uint_t -> -f1 -( - const vector& position, - T randomness = T{1}, - const vector& tiling = vector::zero(), - vector, N> (*hash)(const vector&) = &hash::pcg -) -{ - // Calculate factor which scales hash value onto `[0, 1]`, modulated by desired randomness - T hash_scale = (T{1} / static_cast(std::numeric_limits>::max())) * randomness; - - // Get integer and fractional parts - vector position_i = floor(position - T{1.5}); - vector position_f = position - position_i; - - // Find the F1 cell - T f1_sqr_distance = std::numeric_limits::infinity(); - vector f1_displacement; - hash::make_uint_t f1_hash; - for (std::size_t i = 0; i < kernel_size; ++i) - { - // Get kernel offset for current cell - const vector& offset_i = kernel[i]; - - // Calculate hash input position, tiling where specified - vector hash_position = position_i + offset_i; - for (std::size_t j = 0; j < N; ++j) - { - if (tiling[j]) - { - hash_position[j] = std::fmod(hash_position[j], tiling[j]); - if (hash_position[j] < T{0}) - hash_position[j] += tiling[j]; - } - } - - // Calculate hash values for the hash position - vector, N> hash_i = hash(hash_position); - - // Convert hash values to pseudorandom fractional offset - vector offset_f = vector(hash_i) * hash_scale; - - // Calculate displacement from input position to cell center - vector displacement = (offset_i + offset_f) - position_f; - - // Calculate square distance to the current cell center - T sqr_distance = sqr_length(displacement); - - // Update F1 cell - if (sqr_distance < f1_sqr_distance) - { - f1_sqr_distance = sqr_distance; - f1_displacement = displacement; - f1_hash = hash_i[0]; - } - } - - return - { - f1_sqr_distance, - f1_displacement, - f1_hash - }; -} - -/** - * Finds the Voronoi cell (F1) containing the input position, along with the distance to the nearest edge. - * - * @tparam T Real type. - * @tparam N Number of dimensions. - * - * @param position Input position. - * @param randomness Degree of randomness, on `[0, 1]`. - * @param tiling Distance at which the Voronoi pattern should repeat. A value of `0` indicates no repetition. - * @param hash Hash function. - * - * @return Tuple containing the square Euclidean distance from @p position to the F1 cell center, the displacement vector from the input position to the F1 cell center, a hash value indicating the ID of the F1 cell, and the square Euclidean distance from @p position to the nearest edge. - */ -template -[[nodiscard]] std::tuple -< - // F1 square distance to center - T, - - // F1 position to center displacement - vector, - - // F1 hash - hash::make_uint_t, - - // Edge square distance - T -> -f1_edge -( - const vector& position, - T randomness = T{1}, - const vector& tiling = vector::zero(), - vector, N> (*hash)(const vector&) = &hash::pcg -) -{ - // Calculate factor which scales hash value onto `[0, 1]`, modulated by desired randomness - T hash_scale = (T{1} / static_cast(std::numeric_limits>::max())) * randomness; - - // Get integer and fractional parts - vector position_i = floor(position - T{1.5}); - vector position_f = position - position_i; - - // Find F1 cell - T f1_sqr_distance_center = std::numeric_limits::infinity(); - vector displacement_cache[kernel_size]; - std::size_t f1_i = 0; - hash::make_uint_t f1_hash; - for (std::size_t i = 0; i < kernel_size; ++i) - { - // Get kernel offset for current cell - const vector& offset_i = kernel[i]; - - // Calculate hash input position, tiling where specified - vector hash_position = position_i + offset_i; - for (std::size_t j = 0; j < N; ++j) - { - if (tiling[j]) - { - hash_position[j] = std::fmod(hash_position[j], tiling[j]); - if (hash_position[j] < T{0}) - hash_position[j] += tiling[j]; - } - } - - // Calculate hash values for the hash position - vector, N> hash_i = hash(hash_position); - - // Convert hash values to pseudorandom fractional offset - vector offset_f = vector(hash_i) * hash_scale; - - // Calculate and cache displacement from input position to cell center - displacement_cache[i] = (offset_i + offset_f) - position_f; - - // Calculate square distance to the current cell center - T sqr_distance = sqr_length(displacement_cache[i]); - - // Update F1 cell - if (sqr_distance < f1_sqr_distance_center) - { - f1_sqr_distance_center = sqr_distance; - f1_i = i; - f1_hash = hash_i[0]; - } - } - - // Get displacement vector from input position to the F1 cell center - const vector& f1_displacement = displacement_cache[f1_i]; - - // Find distance to the closest edge - T edge_sqr_distance_edge = std::numeric_limits::infinity(); - for (std::size_t i = 0; i < kernel_size; ++i) - { - // Skip F1 cell - if (i == f1_i) - continue; - - // Fetch cached displacement vector for current cell - const vector& displacement = displacement_cache[i]; - - // Find midpoint between the displacement vectors - const vector midpoint = (f1_displacement + displacement) * T{0.5}; - - // Calculate direction from the F1 cell to current cell - const vector direction = normalize(displacement - f1_displacement); - - // Calculate square distance to the edge - const T sqr_distance = dot(midpoint, direction); - - // Update minimum edge distance if closer than the nearest edge - if (sqr_distance < edge_sqr_distance_edge) - edge_sqr_distance_edge = sqr_distance; - } - - return - { - f1_sqr_distance_center, - f1_displacement, - f1_hash, - edge_sqr_distance_edge - }; -} - -/** - * Finds the Voronoi cell (F1) containing the input position, as well as the nearest neighboring cell (F2). - * - * @tparam T Real type. - * @tparam N Number of dimensions. - * - * @param position Input position. - * @param randomness Degree of randomness, on `[0, 1]`. - * @param tiling Distance at which the Voronoi pattern should repeat. A value of `0` indicates no repetition. - * @param hash Hash function. - * - * @return Tuple containing the square Euclidean distances, displacement vectors from the input position to the cell centers, and hash values indicating the cell IDs, for both the F1 and F2 cells. - */ -template -[[nodiscard]] std::tuple -< - // F1 square distance to center - T, - - // F1 position to center displacement - vector, - - // F1 hash - hash::make_uint_t, - - // F2 square distance to center - T, - - // F2 position to center displacement - vector, - - // F2 hash - hash::make_uint_t -> -f1_f2 -( - const vector& position, - T randomness = T{1}, - const vector& tiling = vector::zero(), - vector, N> (*hash)(const vector&) = &hash::pcg -) -{ - // Calculate factor which scales hash value onto `[0, 1]`, modulated by desired randomness - T hash_scale = (T{1} / static_cast(std::numeric_limits>::max())) * randomness; - - // Get integer and fractional parts - vector position_i = floor(position - T{1.5}); - vector position_f = position - position_i; - - // Find the F1 and F2 cells - T f1_sqr_distance_center = std::numeric_limits::infinity(); - vector f1_displacement = {0, 0}; - hash::make_uint_t f1_hash = 0; - T f2_sqr_distance_center = std::numeric_limits::infinity(); - vector f2_displacement = {0, 0}; - hash::make_uint_t f2_hash = 0; - for (std::size_t i = 0; i < kernel_size; ++i) - { - // Get kernel offset for current cell - const vector& offset_i = kernel[i]; - - // Calculate hash input position, tiling where specified - vector hash_position = position_i + offset_i; - for (std::size_t j = 0; j < N; ++j) - { - if (tiling[j]) - { - hash_position[j] = std::fmod(hash_position[j], tiling[j]); - if (hash_position[j] < T{0}) - hash_position[j] += tiling[j]; - } - } - - // Calculate hash values for the hash position - vector, N> hash_i = hash(hash_position); - - // Convert hash values to pseudorandom fractional offset - vector offset_f = vector(hash_i) * hash_scale; - - // Calculate displacement from input position to cell center - vector displacement = (offset_i + offset_f) - position_f; - - // Calculate square distance to the current cell center - T sqr_distance = sqr_length(displacement); - - // Update F1 and F2 cells - if (sqr_distance < f1_sqr_distance_center) - { - f2_sqr_distance_center = f1_sqr_distance_center; - f2_displacement = f1_displacement; - f2_hash = f1_hash; - - f1_sqr_distance_center = sqr_distance; - f1_displacement = displacement; - f1_hash = hash_i[0]; - } - else if (sqr_distance < f2_sqr_distance_center) - { - f2_sqr_distance_center = sqr_distance; - f2_displacement = displacement; - f2_hash = hash_i[0]; - } - } - - return - { - f1_sqr_distance_center, - f1_displacement, - f1_hash, - f2_sqr_distance_center, - f2_displacement, - f2_hash, - }; -} - -} // namespace voronoi - -} // namespace noise -} // namespace math - -#endif // ANTKEEPER_MATH_NOISE_VORONOI_HPP diff --git a/src/math/polynomial.hpp b/src/math/polynomial.hpp deleted file mode 100644 index 182f703..0000000 --- a/src/math/polynomial.hpp +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_MATH_POLYNOMIAL_HPP -#define ANTKEEPER_MATH_POLYNOMIAL_HPP - -#include "math/numbers.hpp" -#include "math/map.hpp" - -namespace math { - -/// Polynomial functions. -namespace polynomial { - -/** - * Evaluates a polynomial using Horner's method. - * - * @param first,last Range of polynomial coefficients, in descending order of degree. - * @param x Variable value. - * @return Evaluation of P(x). - * - * @see https://en.wikipedia.org/wiki/Horner%27s_method - */ -template -[[nodiscard]] constexpr T horner(InputIt first, InputIt last, T x) -{ - T y = *first; - for (++first; first != last; ++first) - y = y * x + *first; - return y; -} - -/** Chebychev polynomials. - * - * @see https://en.wikipedia.org/wiki/Chebyshev_polynomials - */ -namespace chebyshev { - - /** - * Evaluates a Chebyshev polynomial. - * - * @param[in] first,last Range of Chebychev polynomial coefficients. - * @param[in] x Value on the interval `[-1, 1]`. - * - * @return Evaluated value. - */ - template - [[nodiscard]] T evaluate(InputIt first, InputIt last, T x) - { - T y = *(first++); - y += *(first++) * x; - - T n2 = T(1), n1 = x, n0; - x *= T(2); - - for (; first != last; n2 = n1, n1 = n0) - { - n0 = x * n1 - n2; - y += *(first++) * n0; - } - - return y; - } - - /** - * Evaluates a Chebyshev polynomial. - * - * @param first,last Range of Chebychev polynomial coefficients. - * @param min,max Domain of the approximated function. - * @param x Value on the interval `[min, max]`. - * - * @return Evaluated value. - */ - template - [[nodiscard]] T evaluate(InputIt first, InputIt last, T min, T max, T x) - { - return evaluate(first, last, math::map(x, min, max, T(-1), T(1))); - } - -} // namespace chebyshev - -} // namespace polynomial -} // namespace math - -#endif // ANTKEEPER_MATH_POLYNOMIAL_HPP diff --git a/src/math/projection.hpp b/src/math/projection.hpp deleted file mode 100644 index b6d31e8..0000000 --- a/src/math/projection.hpp +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_MATH_PROJECTION_HPP -#define ANTKEEPER_MATH_PROJECTION_HPP - -#include "math/matrix.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 -[[nodiscard]] 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 -[[nodiscard]] T vertical_fov(T h, T r) -{ - return T{2} * std::atan(std::tan(h * T{0.5}) / r); -} - -/** - * Creates an orthographic projection matrix which will transform the near and far clipping planes to `[-1, 1]`, respectively. - * - * @param left Signed distance to the left clipping plane. - * @param right Signed distance to the right clipping plane. - * @param bottom Signed distance to the bottom clipping plane. - * @param top Signed distance to the top clipping plane. - * @param z_near Signed distance to the near clipping plane. - * @param z_far Signed distance to the far clipping plane. - * - * @return Orthographic projection matrix. - */ -template -[[nodiscard]] constexpr matrix ortho(T left, T right, T bottom, T top, T z_near, T z_far) noexcept -{ - return - {{ - {T(2) / (right - left), T(0), T(0), T(0)}, - {T(0), T(2) / (top - bottom), T(0), T(0)}, - {T(0), T(0), T(-2) / (z_far - z_near), T(0)}, - {-((right + left) / (right - left)), -((top + bottom) / (top - bottom)), -((z_far + z_near) / (z_far - z_near)), T(1)} - }}; -} - -/** - * Creates an orthographic projection matrix which will transform the near and far clipping planes to `[0, 1]`, respectively. - * - * @param left Signed distance to the left clipping plane. - * @param right Signed distance to the right clipping plane. - * @param bottom Signed distance to the bottom clipping plane. - * @param top Signed distance to the top clipping plane. - * @param z_near Signed distance to the near clipping plane. - * @param z_far Signed distance to the far clipping plane. - * - * @return Orthographic projection matrix. - */ -template -[[nodiscard]] constexpr matrix ortho_half_z(T left, T right, T bottom, T top, T z_near, T z_far) noexcept -{ - return - {{ - {T(2) / (right - left), T(0), T(0), T(0)}, - {T(0), T(2) / (top - bottom), T(0), T(0)}, - {T(0), T(0), T(-1) / (z_far - z_near), T(0)}, - {-((right + left) / (right - left)), -((top + bottom) / (top - bottom)), -z_near / (z_far - z_near), T(1)} - }}; -} - -/** - * Creates a perspective projection matrix which will transform the near and far clipping planes to `[-1, 1]`, respectively. - * - * @param vertical_fov Vertical field of view angle, in radians. - * @param aspect_ratio Aspect ratio which determines the horizontal field of view. - * @param z_near Distance to the near clipping plane. - * @param z_far Distance to the far clipping plane. - * - * @return Perspective projection matrix. - */ -template -[[nodiscard]] matrix perspective(T vertical_fov, T aspect_ratio, T z_near, T z_far) -{ - T half_fov = vertical_fov * T(0.5); - T f = std::cos(half_fov) / std::sin(half_fov); - - return - {{ - {f / aspect_ratio, T(0), T(0), T(0)}, - {T(0), f, T(0), T(0)}, - {T(0), T(0), (z_far + z_near) / (z_near - z_far), T(-1)}, - {T(0), T(0), (T(2) * z_far * z_near) / (z_near - z_far), T(0)} - }}; -} - -/** - * Creates a perspective projection matrix which will transform the near and far clipping planes to `[0, 1]`, respectively. - * - * @param vertical_fov Vertical field of view angle, in radians. - * @param aspect_ratio Aspect ratio which determines the horizontal field of view. - * @param z_near Distance to the near clipping plane. - * @param z_far Distance to the far clipping plane. - * - * @return Perspective projection matrix. - */ -template -[[nodiscard]] matrix perspective_half_z(T vertical_fov, T aspect_ratio, T z_near, T z_far) -{ - T half_fov = vertical_fov * T(0.5); - T f = std::cos(half_fov) / std::sin(half_fov); - - return - {{ - {f / aspect_ratio, T(0), T(0), T(0)}, - {T(0), f, T(0), T(0)}, - {T(0), T(0), z_far / (z_near - z_far), T(-1)}, - {T(0), T(0), -(z_far * z_near) / (z_far - z_near), T(0)} - }}; -} - -} // namespace math - -#endif // ANTKEEPER_MATH_PROJECTION_HPP diff --git a/src/math/quaternion.hpp b/src/math/quaternion.hpp deleted file mode 100644 index a0f4b93..0000000 --- a/src/math/quaternion.hpp +++ /dev/null @@ -1,969 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_MATH_QUATERNION_HPP -#define ANTKEEPER_MATH_QUATERNION_HPP - -#include "math/numbers.hpp" -#include "math/matrix.hpp" -#include "math/vector.hpp" -#include -#include -#include - -namespace math { - -/** - * Quaternion composed of a real scalar part and imaginary vector part. - * - * @tparam T Scalar type. - */ -template -struct quaternion -{ - /// Scalar type. - typedef T scalar_type; - - /// Vector type. - typedef vector vector_type; - - /// Rotation matrix type. - typedef matrix matrix_type; - - /// Quaternion real part. - scalar_type r; - - /// Quaternion imaginary part. - vector_type i; - - /// Returns a reference to the quaternion real part. - /// @{ - [[nodiscard]] inline constexpr scalar_type& w() noexcept - { - return r; - } - [[nodiscard]] inline constexpr const scalar_type& w() const noexcept - { - return r; - } - /// @} - - /// Returns a reference to the first element of the quaternion imaginary part. - /// @{ - [[nodiscard]] inline constexpr scalar_type& x() noexcept - { - return i.x(); - } - [[nodiscard]] inline constexpr const scalar_type& x() const noexcept - { - return i.x(); - } - /// @} - - /// Returns a reference to the second element of the quaternion imaginary part. - /// @{ - [[nodiscard]] inline constexpr scalar_type& y() noexcept - { - return i.y(); - } - [[nodiscard]] inline constexpr const scalar_type& y() const noexcept - { - return i.y(); - } - /// @} - - /// Returns a reference to the third element of the quaternion imaginary part. - /// @{ - [[nodiscard]] inline constexpr scalar_type& z() noexcept - { - return i.z(); - } - [[nodiscard]] inline constexpr const scalar_type& z() const noexcept - { - return i.z(); - } - /// @} - - /** - * Returns a quaternion representing a rotation about the x-axis. - * - * @param angle Angle of rotation, in radians. - * - * @return Quaternion representing an x-axis rotation. - */ - [[nodiscard]] static quaternion rotate_x(scalar_type angle) - { - return {std::cos(angle * T(0.5)), std::sin(angle * T(0.5)), T(0), T(0)}; - } - - /** - * Returns a quaternion representing a rotation about the y-axis. - * - * @param angle Angle of rotation, in radians. - * - * @return Quaternion representing an y-axis rotation. - */ - [[nodiscard]] static quaternion rotate_y(scalar_type angle) - { - return {std::cos(angle * T(0.5)), T(0), std::sin(angle * T(0.5)), T(0)}; - } - - /** - * Returns a quaternion representing a rotation about the z-axis. - * - * @param angle Angle of rotation, in radians. - * @return Quaternion representing an z-axis rotation. - */ - [[nodiscard]] static quaternion rotate_z(scalar_type angle) - { - return {std::cos(angle * T(0.5)), T(0), T(0), std::sin(angle * T(0.5))}; - } - - /** - * Type-casts the quaternion scalars using `static_cast`. - * - * @tparam U Target scalar type. - * - * @return Type-casted quaternion. - */ - template - [[nodiscard]] inline constexpr explicit operator quaternion() const noexcept - { - return {static_cast(r), vector(i)}; - } - - /** - * Constructs a matrix representing the rotation described the quaternion. - * - * @return Rotation matrix. - */ - [[nodiscard]] constexpr explicit operator matrix_type() const noexcept - { - const T xx = x() * x(); - const T xy = x() * y(); - const T xz = x() * z(); - const T xw = x() * w(); - const T yy = y() * y(); - const T yz = y() * z(); - const T yw = y() * w(); - const T zz = z() * z(); - const T zw = z() * w(); - - return - { - T(1) - (yy + zz) * T(2), (xy + zw) * T(2), (xz - yw) * T(2), - (xy - zw) * T(2), T(1) - (xx + zz) * T(2), (yz + xw) * T(2), - (xz + yw) * T(2), (yz - xw) * T(2), T(1) - (xx + yy) * T(2) - }; - } - - /** - * Casts the quaternion to a 4-element vector, with the real part as the first element and the imaginary part as the following three elements. - * - * @return Vector containing the real and imaginary parts of the quaternion. - */ - [[nodiscard]] inline constexpr explicit operator vector() const noexcept - { - return {r, i[0], i[1], i[2]}; - } - - /// Returns a zero quaternion, where every scalar is equal to zero. - [[nodiscard]] static constexpr quaternion zero() noexcept - { - return {}; - } - - /// Returns a rotation identity quaternion. - [[nodiscard]] static constexpr quaternion identity() noexcept - { - return {T{1}, vector_type::zero()}; - } -}; - -/** - * Adds two quaternions. - * - * @param a First quaternion. - * @param b Second quaternion. - * - * @return Sum of the two quaternions. - */ -template -[[nodiscard]] constexpr quaternion add(const quaternion& a, const quaternion& b) noexcept; - -/** - * Adds a quaternion and a scalar. - * - * @param a First value. - * @param b Second second value. - * - * @return Sum of the quaternion and scalar. - */ -template -[[nodiscard]] constexpr quaternion add(const quaternion& a, T b) noexcept; - -/** - * Calculates the conjugate of a quaternion. - * - * @param q Quaternion from which the conjugate will be calculated. - * - * @return Conjugate of the quaternion. - */ -template -[[nodiscard]] constexpr quaternion conjugate(const quaternion& q) noexcept; - -/** - * Calculates the dot product of two quaternions. - * - * @param a First quaternion. - * @param b Second quaternion. - * - * @return Dot product of the two quaternions. - */ -template -[[nodiscard]] constexpr T dot(const quaternion& a, const quaternion& b) noexcept; - -/** - * Divides a quaternion by another quaternion. - * - * @param a First value. - * @param b Second value. - * - * @return Result of the division. - */ -template -[[nodiscard]] constexpr quaternion div(const quaternion& a, const quaternion& b) noexcept; - -/** - * Divides a quaternion by a scalar. - * - * @param a Quaternion. - * @param b Scalar. - * - * @return Result of the division. - */ -template -[[nodiscard]] constexpr quaternion div(const quaternion& a, T b) noexcept; - -/** - * Divides a scalar by a quaternion. - * - * @param a Scalar. - * @param b Quaternion. - * - * @return Result of the division. - */ -template -[[nodiscard]] constexpr quaternion div(T a, const quaternion& b) noexcept; - -/** - * Calculates the inverse length of a quaternion. - * - * @param q Quaternion to calculate the inverse length of. - * - * @return Inverse length of the quaternion. - */ -template -[[nodiscard]] T inv_length(const quaternion& q); - -/** - * Calculates the length of a quaternion. - * - * @param q Quaternion to calculate the length of. - * - * @return Length of the quaternion. - */ -template -[[nodiscard]] T length(const quaternion& q); - -/** - * Performs linear interpolation between two quaternions. - * - * @param a First quaternion. - * @param b Second quaternion. - * @param t Interpolation factor. - * - * @return Interpolated quaternion. - */ -template -[[nodiscard]] constexpr quaternion lerp(const quaternion& a, const quaternion& b, T t) noexcept; - -/** - * Creates a unit quaternion rotation using forward and up vectors. - * - * @param forward Unit forward vector. - * @param up Unit up vector. - * - * @return Unit rotation quaternion. - */ -template -[[nodiscard]] quaternion look_rotation(const vector& forward, vector up); - -/** - * Multiplies two quaternions. - * - * @param a First quaternion. - * @param b Second quaternion. - * - * @return Product of the two quaternions. - */ -template -[[nodiscard]] constexpr quaternion mul(const quaternion& a, const quaternion& b) noexcept; - -/** - * Multiplies a quaternion by a scalar. - * - * @param a First value. - * @param b Second value. - * - * @return Product of the quaternion and scalar. - */ -template -[[nodiscard]] constexpr quaternion mul(const quaternion& a, T b) noexcept; - -/** - * Calculates the product of a quaternion and a vector. - * - * @param a First value. - * @param b second value. - * - * @return Product of the quaternion and vector. - */ -/// @{ -template -[[nodiscard]] constexpr vector mul(const quaternion& a, const vector& b) noexcept; -template -[[nodiscard]] constexpr vector mul(const vector& a, const quaternion& b) noexcept; -/// @} - -/** - * Negates a quaternion. - * - * @param q Quaternion to negate. - * - * @return Negated quaternion. - */ -template -[[nodiscard]] constexpr quaternion negate(const quaternion& q) noexcept; - -/** - * Performs normalized linear interpolation between two quaternions. - * - * @param a First quaternion. - * @param b Second quaternion. - * @param t Interpolation factor. - * - * @return Interpolated quaternion. - */ -template -[[nodiscard]] quaternion nlerp(const quaternion& a, const quaternion& b, T t); - -/** - * Normalizes a quaternion. - * - * @param q Quaternion to normalize. - * - * @return Normalized quaternion. - */ -template -[[nodiscard]] quaternion normalize(const quaternion& q); - -/** - * Creates a rotation from an angle and axis. - * - * @param angle Angle of rotation (in radians). - * @param axis Axis of rotation - * - * @return Quaternion representing the rotation. - */ -template -[[nodiscard]] quaternion angle_axis(T angle, const vector& axis); - -/** - * Calculates the minimum rotation between two normalized direction vectors. - * - * @param source Normalized source direction. - * @param destination Normalized destination direction. - * - * @return Quaternion representing the minimum rotation between the source and destination vectors. - */ -template -[[nodiscard]] quaternion rotation(const vector& source, const vector& destination); - -/** - * Performs spherical linear interpolation between two quaternions. - * - * @param a First quaternion. - * @param b Second quaternion. - * @param t Interpolation factor. - * - * @return Interpolated quaternion. - */ -template -[[nodiscard]] quaternion slerp(const quaternion& a, const quaternion& b, T t, T error = T{1e-6}); - -/** - * Calculates the square length of a quaternion. The square length can be calculated faster than the length because a call to `std::sqrt` is saved. - * - * @param q Quaternion to calculate the square length of. - * - * @return Square length of the quaternion. - */ -template -[[nodiscard]] constexpr T sqr_length(const quaternion& q) noexcept; - -/** - * Subtracts a quaternion from another quaternion. - * - * @param a First quaternion. - * @param b Second quaternion. - * - * @return Difference between the quaternions. - */ -template -[[nodiscard]] constexpr quaternion sub(const quaternion& a, const quaternion& b) noexcept; - -/** - * Subtracts a quaternion and a scalar. - * - * @param a First value. - * @param b Second second. - * - * @return Difference between the quaternion and scalar. - */ -/// @{ -template -[[nodiscard]] constexpr quaternion sub(const quaternion& a, T b) noexcept; -template -[[nodiscard]] constexpr quaternion sub(T a, const quaternion& b) noexcept; -/// @} - -/** - * Decomposes a quaternion into swing and twist rotation components. - * - * @param[in] q Quaternion to decompose. - * @param[in] a Axis of twist rotation. - * @param[out] swing Swing rotation component. - * @param[out] twist Twist rotation component. - * @param[in] error Threshold at which a number is considered zero. - * - * @see https://www.euclideanspace.com/maths/geometry/rotations/for/decomposition/ - */ -template -void swing_twist(const quaternion& q, const vector& a, quaternion& qs, quaternion& qt, T error = T{1e-6}); - -/** - * Converts a 3x3 rotation matrix to a quaternion. - * - * @param m Rotation matrix. - * - * @return Unit quaternion representing the rotation described by @p m. - */ -template -[[nodiscard]] quaternion quaternion_cast(const matrix& m); - -template -inline constexpr quaternion add(const quaternion& a, const quaternion& b) noexcept -{ - return {a.r + b.r, a.i + b.i}; -} - -template -inline constexpr quaternion add(const quaternion& a, T b) noexcept -{ - return {a.r + b, a.i + b}; -} - -template -inline constexpr quaternion conjugate(const quaternion& q) noexcept -{ - return {q.r, -q.i}; -} - -template -inline constexpr T dot(const quaternion& a, const quaternion& b) noexcept -{ - return a.r * b.r + dot(a.i, b.i); -} - -template -inline constexpr quaternion div(const quaternion& a, const quaternion& b) noexcept -{ - return {a.r / b.r, a.i / b.i}; -} - -template -inline constexpr quaternion div(const quaternion& a, T b) noexcept -{ - return {a.r / b, a.i / b}; -} - -template -inline constexpr quaternion div(T a, const quaternion& b) noexcept -{ - return {a / b.r, a / b.i}; -} - -template -inline T inv_length(const quaternion& q) -{ - return T{1} / length(q); -} - -template -inline T length(const quaternion& q) -{ - return std::sqrt(sqr_length(q)); -} - -template -inline constexpr quaternion lerp(const quaternion& a, const quaternion& b, T t) noexcept -{ - return - { - (b.r - a.r) * t + a.r, - (b.i - a.i) * t + a.i - }; -} - -template -quaternion look_rotation(const vector& forward, vector up) -{ - vector right = normalize(cross(forward, up)); - up = cross(right, forward); - - matrix m = - { - right, - up, - -forward - }; - - // Convert to quaternion - return normalize(quaternion_cast(m)); -} - -template -constexpr quaternion mul(const quaternion& a, const quaternion& b) noexcept -{ - return - { - -a.x() * b.x() - a.y() * b.y() - a.z() * b.z() + a.w() * b.w(), - a.x() * b.w() + a.y() * b.z() - a.z() * b.y() + a.w() * b.x(), - -a.x() * b.z() + a.y() * b.w() + a.z() * b.x() + a.w() * b.y(), - a.x() * b.y() - a.y() * b.x() + a.z() * b.w() + a.w() * b.z() - }; -} - -template -inline constexpr quaternion mul(const quaternion& a, T b) noexcept -{ - return {a.r * b, a.i * b}; -} - -template -constexpr vector mul(const quaternion& a, const vector& b) noexcept -{ - return a.i * dot(a.i, b) * T(2) + b * (a.r * a.r - sqr_length(a.i)) + cross(a.i, b) * a.r * T(2); -} - -template -inline constexpr vector mul(const vector& a, const quaternion& b) noexcept -{ - return mul(conjugate(b), a); -} - -template -inline constexpr quaternion negate(const quaternion& q) noexcept -{ - return {-q.r, -q.i}; -} - -template -quaternion nlerp(const quaternion& a, const quaternion& b, T t) -{ - return normalize(add(mul(a, T(1) - t), mul(b, t * std::copysign(T(1), dot(a, b))))); -} - -template -inline quaternion normalize(const quaternion& q) -{ - return mul(q, inv_length(q)); -} - -template -quaternion angle_axis(T angle, const vector& axis) -{ - angle *= T{0.5}; - return {std::cos(angle), axis * std::sin(angle)}; -} - -template -quaternion rotation(const vector& source, const vector& destination) -{ - quaternion q = {dot(source, destination), cross(source, destination)}; - q.w() += length(q); - return normalize(q); -} - -template -quaternion slerp(const quaternion& a, const quaternion& b, T t, T error) -{ - T cos_theta = dot(a, b); - - if (cos_theta > T(1) - error) - return normalize(lerp(a, b, t)); - - cos_theta = std::max(T(-1), std::min(T(1), cos_theta)); - const T theta = std::acos(cos_theta) * t; - - quaternion c = normalize(sub(b, mul(a, cos_theta))); - - return add(mul(a, std::cos(theta)), mul(c, std::sin(theta))); -} - -template -inline constexpr T sqr_length(const quaternion& q) noexcept -{ - return q.r * q.r + sqr_length(q.i); -} - -template -inline constexpr quaternion sub(const quaternion& a, const quaternion& b) noexcept -{ - return {a.r - b.r, a.i - b.i}; -} - -template -inline constexpr quaternion sub(const quaternion& a, T b) noexcept -{ - return {a.r - b, a.i - b}; -} - -template -inline constexpr quaternion sub(T a, const quaternion& b) noexcept -{ - return {a - b.r, a - b.i}; -} - -template -void swing_twist(const quaternion& q, const vector& a, quaternion& qs, quaternion& qt, T error) -{ - if (sqr_length(q.i) > error) - { - qt = normalize(quaternion{q.w(), a * dot(a, q.i)}); - qs = mul(q, conjugate(qt)); - } - else - { - qt = angle_axis(pi, a); - - const vector qa = mul(q, a); - const vector sa = cross(a, qa); - if (sqr_length(sa) > error) - qs = angle_axis(std::acos(dot(a, qa)), sa); - else - qs = quaternion::identity(); - } -} - -template -quaternion quaternion_cast(const matrix& m) -{ - const T t = trace(m); - - if (t > T(0)) - { - T s = T(0.5) / std::sqrt(t + T(1)); - return - { - T(0.25) / s, - (m[1][2] - m[2][1]) * s, - (m[2][0] - m[0][2]) * s, - (m[0][1] - m[1][0]) * s - }; - } - else - { - if (m[0][0] > m[1][1] && m[0][0] > m[2][2]) - { - T s = T(2) * std::sqrt(T(1) + m[0][0] - m[1][1] - m[2][2]); - - return - { - (m[1][2] - m[2][1]) / s, - T(0.25) * s, - (m[1][0] + m[0][1]) / s, - (m[2][0] + m[0][2]) / s - }; - } - else if (m[1][1] > m[2][2]) - { - T s = T(2) * std::sqrt(T(1) + m[1][1] - m[0][0] - m[2][2]); - return - { - (m[2][0] - m[0][2]) / s, - (m[1][0] + m[0][1]) / s, - T(0.25) * s, - (m[2][1] + m[1][2]) / s - }; - } - else - { - T s = T(2) * std::sqrt(T(1) + m[2][2] - m[0][0] - m[1][1]); - return - { - (m[0][1] - m[1][0]) / s, - (m[2][0] + m[0][2]) / s, - (m[2][1] + m[1][2]) / s, - T(0.25) * s - }; - } - } -} - -namespace operators { - -/// @copydoc add(const quaternion&, const quaternion&) -template -inline constexpr quaternion operator+(const quaternion& a, const quaternion& b) noexcept -{ - return add(a, b); -} - -/// @copydoc add(const quaternion&, T) -/// @{ -template -inline constexpr quaternion operator+(const quaternion& a, T b) noexcept -{ - return add(a, b); -} -template -inline constexpr quaternion operator+(T a, const quaternion& b) noexcept -{ - return add(b, a); -} -/// @} - -/// @copydoc div(const quaternion&, const quaternion&) -template -inline constexpr quaternion operator/(const quaternion& a, const quaternion& b) noexcept -{ - return div(a, b); -} - -/// @copydoc div(const quaternion&, T) -template -inline constexpr quaternion operator/(const quaternion& a, T b) noexcept -{ - return div(a, b); -} - -/// @copydoc div(T, const quaternion&) -template -inline constexpr quaternion operator/(T a, const quaternion& b) noexcept -{ - return div(a, b); -} - -/// @copydoc mul(const quaternion&, const quaternion&) -template -inline constexpr quaternion operator*(const quaternion& a, const quaternion& b) noexcept -{ - return mul(a, b); -} - -/// @copydoc mul(const quaternion&, T) -/// @{ -template -inline constexpr quaternion operator*(const quaternion& a, T b) noexcept -{ - return mul(a, b); -} -template -inline constexpr quaternion operator*(T a, const quaternion& b) noexcept -{ - return mul(b, a); -} -/// @} - -/// @copydoc mul(const quaternion&, const vector&) -template -inline constexpr vector operator*(const quaternion& a, const vector& b) noexcept -{ - return mul(a, b); -} - -/// @copydoc mul(const vector&, const quaternion&) -template -inline constexpr vector operator*(const vector& a, const quaternion& b) noexcept -{ - return mul(a, b); -} - -/// @copydoc sub(const quaternion&, const quaternion&) -template -inline constexpr quaternion operator-(const quaternion& a, const quaternion& b) noexcept -{ - return sub(a, b); -} - -/// @copydoc sub(const quaternion&, T) -/// @{ -template -inline constexpr quaternion operator-(const quaternion& a, T b) noexcept -{ - return sub(a, b); -} -template -inline constexpr quaternion operator-(T a, const quaternion& b) noexcept -{ - return sub(a, b); -} -/// @} - -/// @copydoc negate(const quaternion&) -template -inline constexpr quaternion operator-(const quaternion& q) noexcept -{ - return negate(q); -} - -/** - * Adds two values and stores the result in the first value. - * - * @param a First value. - * @param b Second value. - * - * @return Reference to the first value. - */ -/// @{ -template -inline constexpr quaternion& operator+=(quaternion& a, const quaternion& b) noexcept -{ - return (a = a + b); -} -template -inline constexpr quaternion& operator+=(quaternion& a, T b) noexcept -{ - return (a = a + b); -} -/// @} - -/** - * Subtracts the first value by the second value and stores the result in the first value. - * - * @param a First value. - * @param b Second value. - * - * @return Reference to the first value. - */ -/// @{ -template -inline constexpr quaternion& operator-=(quaternion& a, const quaternion& b) noexcept -{ - return (a = a - b); -} -template -inline constexpr quaternion& operator-=(quaternion& a, T b) noexcept -{ - return (a = a - b); -} -/// @} - -/** - * Multiplies two values and stores the result in the first value. - * - * @param a First value. - * @param b Second value. - * - * @return Reference to the first value. - */ -/// @{ -template -inline constexpr quaternion& operator*=(quaternion& a, const quaternion& b) noexcept -{ - return (a = a * b); -} -template -inline constexpr quaternion& operator*=(quaternion& a, T b) noexcept -{ - return (a = a * b); -} -/// @} - -/** - * Divides the first value by the second value and stores the result in the first value. - * - * @param a First value. - * @param b Second value. - * - * @return Reference to the first value. - */ -/// @{ -template -inline constexpr quaternion& operator/=(quaternion& a, const quaternion& b) noexcept -{ - return (a = a / b); -} -template -inline constexpr quaternion& operator/=(quaternion& a, T b) noexcept -{ - return (a = a / b); -} -/// @} - -/** - * Writes the real and imaginary parts of a quaternion to an output stream, with each number delimeted by a space. - * - * @param os Output stream. - * @param q Quaternion. - * - * @return Output stream. - */ -template -std::ostream& operator<<(std::ostream& os, const math::quaternion& q) -{ - os << q.r << ' ' << q.i; - return os; -} - -/** - * Reads the real and imaginary parts of a quaternion from an input stream, with each number delimeted by a space. - * - * @param is Input stream. - * @param q Quaternion. - * - * @return Input stream. - */ -template -std::istream& operator>>(std::istream& is, const math::quaternion& q) -{ - is >> q.r; - is >> q.i; - return is; -} - -} // namespace operators - -} // namespace math - -using namespace math::operators; - -#endif // ANTKEEPER_MATH_QUATERNION_HPP diff --git a/src/math/se3.hpp b/src/math/se3.hpp deleted file mode 100644 index 6b76c98..0000000 --- a/src/math/se3.hpp +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_MATH_TRANSFORMATION_SE3_HPP -#define ANTKEEPER_MATH_TRANSFORMATION_SE3_HPP - -#include "math/vector.hpp" -#include "math/quaternion.hpp" - -namespace math { -namespace transformation { - -/** - * 3-dimensional Euclidean proper rigid transformation in SE(3). - * - * @tparam T Scalar type. - */ -template -struct se3 -{ -public: - /// Scalar type. - typedef T scalar_type; - - /// Vector type. - typedef math::vector3 vector_type; - - /// Quaternion type. - typedef math::quaternion quaternion_type; - - /// Transformation matrix type. - typedef math::matrix matrix_type; - - /// Vector representing the translation component of this SE(3) transformation. - vector_type t; - - /// Quaternion representing the rotation component of this SE(3) transformation. - quaternion_type r; - - /// Returns the inverse of this SE(3) transformation. - [[nodiscard]] se3 inverse() const; - - /// Returns a matrix representation of the SE(3) transformation. - [[nodiscard]] matrix_type matrix() const; - - /** - * Transforms a vector by this SE(3) transformation. - * - * @param x Untransformed vector. - * @return Transformed vector. - */ - [[nodiscard]] vector_type transform(const vector_type& x) const; - - /** - * Transforms an SE(3) transformation by this SE(3) transformation. - * - * @param x Other SE(3) transformation. - * @return Frame in this se3's space. - */ - [[nodiscard]] se3 transform(const se3& x) const; - - /// @copydoc se3::transform(const vector_type&) const - [[nodiscard]] vector_type operator*(const vector_type& x) const; - - /// @copydoc se3::transform(const se3&) const - [[nodiscard]] se3 operator*(const se3& x) const; -}; - -template -se3 se3::inverse() const -{ - const quaternion_type inverse_r = math::conjugate(r); - const vector_type inverse_t = -(inverse_r * t); - return se3{inverse_t, inverse_r}; -} - -template -typename se3::matrix_type se3::matrix() const -{ - matrix_type m = math::matrix(math::matrix(r)); - - m[3].x() = t.x(); - m[3].y() = t.y(); - m[3].z() = t.z(); - - return m; -} - -template -typename se3::vector_type se3::transform(const vector_type& x) const -{ - return r * x + t; -} - -template -se3 se3::transform(const se3& x) const -{ - return se3 - { - x.transform(t), - math::normalize(x.r * r) - }; -} - -template -typename se3::vector_type se3::operator*(const vector_type& x) const -{ - return transform(x); -} - -template -se3 se3::operator*(const se3& x) const -{ - return transform(x); -} - -} // namespace transformation -} // namespace math - -#endif // ANTKEEPER_MATH_TRANSFORMATION_SE3_HPP diff --git a/src/math/transform-functions.hpp b/src/math/transform-functions.hpp deleted file mode 100644 index e1994f3..0000000 --- a/src/math/transform-functions.hpp +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_MATH_TRANSFORM_FUNCTIONS_HPP -#define ANTKEEPER_MATH_TRANSFORM_FUNCTIONS_HPP - -#include "math/transform-type.hpp" -#include "math/quaternion.hpp" - -namespace math { - -/** - * Calculates the inverse of a transform. - * - * @param t Transform of which to take the inverse. - */ -template -[[nodiscard]] transform inverse(const transform& t); - -/** - * Converts a transform to a transformation matrix. - * - * @param t Transform. - * @return Matrix representing the transformation described by `t`. - */ -template -[[nodiscard]] matrix matrix_cast(const transform& t); - -/** - * Multiplies two transforms. - * - * @param x First transform. - * @param y Second transform. - * @return Product of the two transforms. - */ -template -[[nodiscard]] transform mul(const transform& x, const transform& y); - -/** - * Multiplies a vector by a transform. - * - * @param t Transform. - * @param v Vector. - * @return Product of the transform and vector. - */ -template -[[nodiscard]] vector mul(const transform& t, const vector& v); - -template -transform inverse(const transform& t) -{ - transform inverse_t; - inverse_t.scale = {T(1) / t.scale.x(), T(1) / t.scale.y(), T(1) / t.scale.z()}; - inverse_t.rotation = conjugate(t.rotation); - inverse_t.translation = negate(mul(inverse_t.rotation, t.translation)); - return inverse_t; -} - -template -matrix matrix_cast(const transform& t) -{ - matrix transformation = matrix(matrix(t.rotation)); - transformation[3] = {t.translation[0], t.translation[1], t.translation[2], T(1)}; - return scale(transformation, t.scale); -} - -template -transform mul(const transform& x, const transform& y) -{ - return - { - mul(x, y.translation), - normalize(mul(x.rotation, y.rotation)), - mul(x.scale, y.scale) - }; -} - -template -vector mul(const transform& t, const vector& v) -{ - return add(t.translation, (mul(t.rotation, mul(v, t.scale)))); -} - -} // namespace math - -#endif // ANTKEEPER_MATH_TRANSFORM_FUNCTIONS_HPP - diff --git a/src/math/transform-operators.hpp b/src/math/transform-operators.hpp deleted file mode 100644 index 5c8774b..0000000 --- a/src/math/transform-operators.hpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_MATH_TRANSFORM_OPERATORS_HPP -#define ANTKEEPER_MATH_TRANSFORM_OPERATORS_HPP - -#include "math/transform-type.hpp" -#include "math/transform-functions.hpp" - -/// @copydoc math::mul(const math::transform&, const math::transform&) -template -[[nodiscard]] math::transform operator*(const math::transform& x, const math::transform& y); - -/// @copydoc math::mul(const math::transform&, const math::vector&) -template -[[nodiscard]] math::vector operator*(const math::transform& t, const math::vector& v); - -/** - * Multiplies two transforms and stores the result in the first transform. - * - * @param x First transform. - * @param y Second transform. - * @return Reference to the first transform. - */ -template -math::transform& operator*=(math::transform& x, const math::transform& y); - -template -inline math::transform operator*(const math::transform& x, const math::transform& y) -{ - return mul(x, y); -} - -template -inline math::vector operator*(const math::transform& t, const math::vector& v) -{ - return mul(t, v); -} - -template -inline math::transform& operator*=(math::transform& x, const math::vector& y) -{ - return (x = x * y); -} - -#endif // ANTKEEPER_MATH_TRANSFORM_OPERATORS_HPP - diff --git a/src/math/transform-type.hpp b/src/math/transform-type.hpp deleted file mode 100644 index feaee52..0000000 --- a/src/math/transform-type.hpp +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_MATH_TRANSFORM_TYPE_HPP -#define ANTKEEPER_MATH_TRANSFORM_TYPE_HPP - -#include "math/vector.hpp" -#include "math/quaternion.hpp" - -namespace math { - -/** - * Represents 3D TRS transformation. - * - * @tparam T Scalar type. - */ -template -struct transform -{ - /// Translation vector - vector translation; - - /// Rotation quaternion - quaternion rotation; - - /// Scale vector - vector scale; - - /// Identity transform. - static const transform identity; -}; - -template -const transform transform::identity = -{ - vector::zero(), - quaternion::identity(), - vector::one() -}; - -} // namespace math - -#endif // ANTKEEPER_MATH_TRANSFORM_TYPE_HPP diff --git a/src/physics/gas/atmosphere.hpp b/src/physics/gas/atmosphere.hpp deleted file mode 100644 index 328edf1..0000000 --- a/src/physics/gas/atmosphere.hpp +++ /dev/null @@ -1,252 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_PHYSICS_GAS_ATMOSPHERE_HPP -#define ANTKEEPER_PHYSICS_GAS_ATMOSPHERE_HPP - -#include "physics/constants.hpp" -#include "math/numbers.hpp" -#include -#include - -namespace physics { -namespace gas { - -/// Atmosphere-related functions. -namespace atmosphere { - -/** - * Calculates a particle polarizability factor. - * - * @param ior Atmospheric index of refraction. - * @param density Molecular number density, in mol/m-3. - * @return Polarizability factor. - * - * @see Elek, O., & Kmoch, P. (2010). Real-time spectral scattering in large-scale natural participating media. Proceedings of the 26th Spring Conference on Computer Graphics - SCCG ’10. doi:10.1145/1925059.1925074 - * @see Elek, Oskar. (2009). Rendering Parametrizable Planetary Atmospheres with Multiple Scattering in Real-Time. - */ -template -T polarization(T ior, T density) -{ - constexpr T k = T(2) * math::pi * math::pi; - const T ior2m1 = ior * ior - T(1); - const T num = k * ior2m1 * ior2m1; - const T den = T(3) * density * density; - return num / den; -} - -/** - * Calculates a wavelength-dependent scattering coefficient. - * - * @param density Molecular number density of the particles, in mol/m-3. - * @param polarization Particle polarizability factor. - * @param wavelength Wavelength of light, in meters. - * - * @return Scattering coefficient. - * - * @see atmosphere::polarization - * - * @see Elek, O., & Kmoch, P. (2010). Real-time spectral scattering in large-scale natural participating media. Proceedings of the 26th Spring Conference on Computer Graphics - SCCG ’10. doi:10.1145/1925059.1925074 - * @see Elek, Oskar. (2009). Rendering Parametrizable Planetary Atmospheres with Multiple Scattering in Real-Time. - */ -template -T scattering(T density, T polarization, T wavelength) -{ - const T wavelength2 = wavelength * wavelength; - return math::four_pi * (density / (wavelength2 * wavelength2)) * polarization; -} - -/** - * Calculates a wavelength-independent scattering coefficient. - * - * @param density Molecular number density of the particles, in mol/m-3. - * @param polarization Particle polarizability factor. - * - * @return Scattering coefficient. - * - * @see atmosphere::polarization - * - * @see Elek, O., & Kmoch, P. (2010). Real-time spectral scattering in large-scale natural participating media. Proceedings of the 26th Spring Conference on Computer Graphics - SCCG ’10. doi:10.1145/1925059.1925074 - * @see Elek, Oskar. (2009). Rendering Parametrizable Planetary Atmospheres with Multiple Scattering in Real-Time. - */ -template -T scattering(T density, T polarization) -{ - return math::four_pi * density * polarization; -} - -/** - * Calculates an absorption coefficient. - * - * @param scattering Scattering coefficient. - * @param albedo Single-scattering albedo. - * - * @return Absorption coefficient. - * - * @see https://en.wikipedia.org/wiki/Single-scattering_albedo - */ -template -T absorption(T scattering, T albedo) -{ - return scattering * (T(1) / albedo - T(1)); -} - -/** - * Calculates an extinction coefficient. - * - * @param scattering Scattering coefficient. - * @param albedo Single-scattering albedo. - * - * @return Extinction coefficient. - * - * @see https://en.wikipedia.org/wiki/Single-scattering_albedo - */ -template -T extinction(T scattering, T albedo) -{ - return scattering / albedo; -} - -/** - * Approximates the optical depth of exponentially-distributed atmospheric particles between two points using the trapezoidal rule. - * - * @param a Start point. - * @param b End point. - * @param r Radius of the planet. - * @param sh Scale height of the atmospheric particles. - * @param n Number of samples. - * @return Optical depth between @p a and @p b. - */ -template -T optical_depth_exp(const math::vector3& a, const math::vector3& b, T r, T sh, std::size_t n) -{ - sh = T(-1) / sh; - - const T h = math::length(b - a) / T(n); - - math::vector3 dy = (b - a) / T(n); - math::vector3 y = a + dy; - - T f_x = std::exp((math::length(a) - r) * sh); - T f_y = std::exp((math::length(y) - r) * sh); - T sum = (f_x + f_y); - - for (std::size_t i = 1; i < n; ++i) - { - f_x = f_y; - y += dy; - f_y = std::exp((math::length(y) - r) * sh); - sum += (f_x + f_y); - } - - return sum / T(2) * h; -} - -/** - * Approximates the optical depth of triangularly-distributed atmospheric particles between two points using the trapezoidal rule. - * - * @param p0 Start point. - * @param p1 End point. - * @param r Radius of the planet. - * @param a Distribution lower limit. - * @param b Distribution upper limit. - * @param c Distribution upper mode. - * @param n Number of samples. - * @return Optical depth between @p a and @p b. - */ -template -T optical_depth_tri(const math::vector3& p0, const math::vector3& p1, T r, T a, T b, T c, std::size_t n) -{ - a = T(1) / (a - c); - b = T(1) / (b - c); - - const T h = math::length(p1 - p0) / T(n); - - math::vector3 dy = (p1 - p0) / T(n); - math::vector3 y = p0 + dy; - - T z = math::length(p0) - r; - T f_x = std::max(T(0), std::max(T(0), c - z) * a - std::max(T(0), z - c) * b + T(1)); - - z = math::length(y) - r; - T f_y = std::max(T(0), std::max(T(0), c - z) * a - std::max(T(0), z - c) * b + T(1)); - T sum = (f_x + f_y); - - for (std::size_t i = 1; i < n; ++i) - { - f_x = f_y; - y += dy; - - z = math::length(y) - r; - f_y = std::max(T(0), std::max(T(0), c - z) * a - std::max(T(0), z - c) * b + T(1)); - - sum += (f_x + f_y); - } - - return sum / T(2) * h; -} - -/// Atmospheric density functions. -namespace density { - - /** - * Calculates the density of exponentially-distributed atmospheric particles at a given elevation. - * - * @param d0 Density at sea level. - * @param z Height above sea level. - * @param sh Scale height of the particle type. - * - * @return Particle density at elevation @p z. - * - * @see https://en.wikipedia.org/wiki/Barometric_formula - * @see https://en.wikipedia.org/wiki/Scale_height - */ - template - T exponential(T d0, T z, T sh) - { - return d0 * std::exp(-z / sh); - } - - /** - * Calculates the density of triangularly-distributed atmospheric particles at a given elevation. - * - * @param d0 Density at sea level. - * @param z Height above sea level. - * @param a Distribution lower limit. - * @param b Distribution upper limit. - * @param c Distribution mode. - * - * @return Particle density at elevation @p z. - * - * @see https://en.wikipedia.org/wiki/Triangular_distribution - */ - template - T triangular(T d0, T z, T a, T b, T c) - { - return d0 * max(T(0), max(T(0), c - z) / (a - c) - max(T(0), z - c) / (b - c) + T(1)); - } - -} // namespace density - -} // namespace atmosphere - -} // namespace gas -} // namespace physics - -#endif // ANTKEEPER_PHYSICS_GAS_ATMOSPHERE_HPP diff --git a/src/physics/gas/gas.hpp b/src/physics/gas/gas.hpp deleted file mode 100644 index c2a642b..0000000 --- a/src/physics/gas/gas.hpp +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_PHYSICS_GAS_HPP -#define ANTKEEPER_PHYSICS_GAS_HPP - -namespace physics { - -/// Gas-related functions. -namespace light {} - -} // namespace physics - -#include "physics/gas/atmosphere.hpp" -#include "physics/gas/ozone.hpp" - -#endif // ANTKEEPER_PHYSICS_GAS_HPP diff --git a/src/physics/gas/ozone.hpp b/src/physics/gas/ozone.hpp deleted file mode 100644 index 172c4f0..0000000 --- a/src/physics/gas/ozone.hpp +++ /dev/null @@ -1,583 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_PHYSICS_GAS_OZONE_HPP -#define ANTKEEPER_PHYSICS_GAS_OZONE_HPP - -#include "math/interpolation.hpp" - -namespace physics { -namespace gas { - -/// Ozone-related constants and functions. -namespace ozone { - -/** - * Cross sections of ozone from wavelength 280nm to 780nm, in increments of 1nm, at a temperature of 293K, in m-2/molecule. - * - * @see https://www.iup.uni-bremen.de/gruppen/molspec/databases/referencespectra/o3spectra2011/index.html - */ -constexpr double cross_sections_280nm_780nm_293k[781 - 280] = -{ - 4.08020162329358e-22, - 3.71300005805609e-22, - 3.33329613050011e-22, - 3.08830771169272e-22, - 2.7952452827933e-22, - 2.54507006594824e-22, - 2.31059906830132e-22, - 2.06098663896454e-22, - 1.77641837487505e-22, - 1.60649162987982e-22, - 1.43573256268989e-22, - 1.28028940869356e-22, - 1.11222715354512e-22, - 1.00812840644175e-22, - 8.62757275283743e-23, - 7.74492225274189e-23, - 6.74998572150431e-23, - 5.92584261096169e-23, - 5.09573191001311e-23, - 4.58263122360992e-23, - 3.90608822487099e-23, - 3.4712884682176e-23, - 3.05004947986011e-23, - 2.61948471583207e-23, - 2.35950204633063e-23, - 2.01696279467763e-23, - 1.77716671613151e-23, - 1.57884103804348e-23, - 1.35332466460157e-23, - 1.23148992566335e-23, - 1.00674476685553e-23, - 9.14966132477946e-24, - 8.0373280016411e-24, - 6.82346538467894e-24, - 6.42747947320284e-24, - 5.12300259416845e-24, - 4.77060350699273e-24, - 4.0011431423959e-24, - 3.72184265884461e-24, - 2.71828160861414e-24, - 3.14980819763314e-24, - 2.01180905161078e-24, - 2.23741500460792e-24, - 2.04586872483057e-24, - 1.14774957440809e-24, - 1.7873766001915e-24, - 1.14326155695545e-24, - 7.61393733215954e-25, - 1.30841854222986e-24, - 6.50013011248201e-25, - 4.47253301018733e-25, - 7.22254319278391e-25, - 4.42923420258652e-25, - 3.22168537281097e-25, - 5.60700006481047e-25, - 2.51991724359347e-25, - 1.64975530971913e-25, - 2.69863618345909e-25, - 2.2962216595934e-25, - 1.2768604186372e-25, - 1.6428080690911e-25, - 1.15252741016694e-25, - 6.88123014503947e-26, - 9.01478526179051e-26, - 1.38671804168295e-25, - 7.28648586727799e-26, - 6.3468766437083e-26, - 4.1705109317344e-26, - 3.22298756116943e-26, - 2.52476541455397e-26, - 2.96595291121762e-26, - 2.10981495904366e-26, - 3.0696184107227e-26, - 2.38627682184272e-26, - 1.35645160962203e-26, - 1.14737436955784e-26, - 1.00293019429341e-26, - 1.42699666872085e-26, - 1.03892014298915e-26, - 7.46029500400895e-27, - 7.86729405487508e-27, - 6.49493671023213e-27, - 3.90586420068501e-27, - 3.3969327080211e-27, - 2.69156849765275e-27, - 4.89998022220222e-27, - 4.18363151355665e-27, - 2.41505691668684e-27, - 1.52926807176976e-27, - 1.74010836686791e-27, - 1.43997701486263e-27, - 1.61611242687813e-27, - 1.09444991940235e-27, - 8.68337506495441e-28, - 1.28660044051837e-27, - 1.07534571825705e-27, - 7.59223090396554e-28, - 6.75850905941831e-28, - 6.05594086115429e-28, - 7.40387066310015e-28, - 6.04999720618854e-28, - 5.17923583652293e-28, - 4.0858846895433e-28, - 6.47448369216067e-28, - 5.29992493931534e-28, - 5.63128808710364e-28, - 4.17695119955099e-28, - 5.15330384762416e-28, - 5.25185850011986e-28, - 6.17618171380047e-28, - 6.37119303086457e-28, - 6.8496876006444e-28, - 5.9229625229341e-28, - 5.99998795876176e-28, - 7.06136396740756e-28, - 9.93040926727483e-28, - 1.03668944015898e-27, - 9.60158757124091e-28, - 9.00247643555494e-28, - 1.01801330597124e-27, - 1.0893103182854e-27, - 1.30096252080506e-27, - 1.42018105948242e-27, - 1.53477820553937e-27, - 1.60452920542724e-27, - 1.45613806382105e-27, - 1.63671034304492e-27, - 1.88680147053984e-27, - 2.26659932677371e-27, - 2.57113808884621e-27, - 2.73584069155576e-27, - 2.84524835939992e-27, - 2.65294565386989e-27, - 2.52007318042733e-27, - 2.56347774832346e-27, - 2.73272493525925e-27, - 3.21972108535573e-27, - 3.64879308272645e-27, - 3.86875166703077e-27, - 3.77657059174972e-27, - 3.67917967079418e-27, - 3.84603321414093e-27, - 4.47110813921024e-27, - 5.19879478264214e-27, - 6.13707395634462e-27, - 6.88229484256763e-27, - 7.11935416561536e-27, - 7.15812015493665e-27, - 6.67142397990209e-27, - 6.35218112458219e-27, - 6.34510826220203e-27, - 6.90321809802859e-27, - 7.82871587803871e-27, - 8.52938477234511e-27, - 8.74335964163491e-27, - 8.6718822639457e-27, - 8.59495406258221e-27, - 8.90719500501358e-27, - 9.90176156668504e-27, - 1.14857395824068e-26, - 1.36017282525383e-26, - 1.53232356175871e-26, - 1.75024000506424e-26, - 1.8179765858316e-26, - 1.80631911188605e-26, - 1.70102948254892e-26, - 1.56536231218255e-26, - 1.51141962474665e-26, - 1.57847025841346e-26, - 1.72161452840856e-26, - 1.90909798194127e-26, - 1.96774621921165e-26, - 1.99812678813396e-26, - 1.96900102296014e-26, - 1.95126617395258e-26, - 2.06408915381352e-26, - 2.28866836725858e-26, - 2.57662977048169e-26, - 2.96637551360212e-26, - 3.34197426701549e-26, - 3.73735792971477e-26, - 4.03453827718193e-26, - 4.1291323324037e-26, - 4.07643587970195e-26, - 3.84067732691303e-26, - 3.6271000065179e-26, - 3.50502931147362e-26, - 3.48851626868318e-26, - 3.73778737646723e-26, - 3.97091132121154e-26, - 4.21773978585713e-26, - 4.21620738550362e-26, - 4.22087900183437e-26, - 4.27973060694892e-26, - 4.36010682588909e-26, - 4.60584575680881e-26, - 4.91428506793045e-26, - 5.4918846417406e-26, - 6.10573296817762e-26, - 6.83025566941932e-26, - 7.51469261186479e-26, - 8.08197962688924e-26, - 8.44082645474868e-26, - 8.4843465766735e-26, - 8.4126775729461e-26, - 8.14411830190209e-26, - 7.83636723569755e-26, - 7.60974711104334e-26, - 7.57877917471603e-26, - 7.77887132347866e-26, - 8.07522339055262e-26, - 8.32310316258138e-26, - 8.59015818152486e-26, - 8.67834106505007e-26, - 8.72244226406716e-26, - 8.84734167611993e-26, - 9.0711580597939e-26, - 9.51778147590566e-26, - 1.01490385328969e-25, - 1.09448341447976e-25, - 1.18257044868557e-25, - 1.28105938778111e-25, - 1.37732704252934e-25, - 1.47491161151436e-25, - 1.55090701035304e-25, - 1.60575752538508e-25, - 1.62886093543744e-25, - 1.62802718439262e-25, - 1.60288510229631e-25, - 1.57216917046401e-25, - 1.54475957021763e-25, - 1.534341089264e-25, - 1.5409967492982e-25, - 1.56495784865383e-25, - 1.60012763627009e-25, - 1.63952588489707e-25, - 1.67232066912218e-25, - 1.70167894480834e-25, - 1.7335194060265e-25, - 1.7602731663686e-25, - 1.79953556347172e-25, - 1.84084607422109e-25, - 1.89999243847493e-25, - 1.97490644310087e-25, - 2.05279021301286e-25, - 2.14427839223598e-25, - 2.24098369182092e-25, - 2.34362490982003e-25, - 2.44138366650567e-25, - 2.53826212075759e-25, - 2.62577562731292e-25, - 2.70621837640467e-25, - 2.76622780914432e-25, - 2.80661519633223e-25, - 2.82737499370866e-25, - 2.82968166962191e-25, - 2.82659484597549e-25, - 2.81717325255129e-25, - 2.82197485088881e-25, - 2.84600833360439e-25, - 2.88048754046166e-25, - 2.92686210579676e-25, - 2.98551267943544e-25, - 3.04464034622896e-25, - 3.09724266051229e-25, - 3.14551726028915e-25, - 3.18670379817661e-25, - 3.22330249380314e-25, - 3.25463784914917e-25, - 3.2854797958699e-25, - 3.31405330400124e-25, - 3.34398013095565e-25, - 3.38005272562664e-25, - 3.41218557614901e-25, - 3.45555599852459e-25, - 3.5043277615423e-25, - 3.55984911371566e-25, - 3.6264721972979e-25, - 3.70647322984569e-25, - 3.79188179306458e-25, - 3.88981479760571e-25, - 3.98973596432023e-25, - 4.08527421216539e-25, - 4.17464990288797e-25, - 4.25462181228461e-25, - 4.32317712812093e-25, - 4.38401384845366e-25, - 4.44216978945654e-25, - 4.49508440820886e-25, - 4.5516564927479e-25, - 4.60931329475278e-25, - 4.66173637960526e-25, - 4.70665064920011e-25, - 4.74051362440113e-25, - 4.75812011867871e-25, - 4.7570031038151e-25, - 4.73747927545327e-25, - 4.71027119364443e-25, - 4.66860282624977e-25, - 4.6288533265784e-25, - 4.57997871538082e-25, - 4.53548773884213e-25, - 4.49607272653461e-25, - 4.4568818824566e-25, - 4.42881930827398e-25, - 4.40157474149992e-25, - 4.38677564524713e-25, - 4.37182142194489e-25, - 4.37104565551326e-25, - 4.37919711899623e-25, - 4.39683352146027e-25, - 4.42484514000691e-25, - 4.47212673326828e-25, - 4.53157968174371e-25, - 4.60815452736499e-25, - 4.69376508835705e-25, - 4.78054316070552e-25, - 4.87030272266768e-25, - 4.95220907227656e-25, - 5.02898332230558e-25, - 5.09027352924287e-25, - 5.13210890748271e-25, - 5.15374454317407e-25, - 5.15075653533686e-25, - 5.13171378996911e-25, - 5.09303097809138e-25, - 5.03697164998998e-25, - 4.97218676726656e-25, - 4.89461758141549e-25, - 4.80937594526597e-25, - 4.72371968798565e-25, - 4.63859449875443e-25, - 4.55132403268817e-25, - 4.46787830232533e-25, - 4.38816245012175e-25, - 4.31406743009557e-25, - 4.24483372714822e-25, - 4.17474755876221e-25, - 4.11305082072427e-25, - 4.05554427636835e-25, - 4.0030863998631e-25, - 3.95393149188544e-25, - 3.90576318741963e-25, - 3.86085282288514e-25, - 3.81425489414328e-25, - 3.76584427585746e-25, - 3.71837025816073e-25, - 3.66911165810168e-25, - 3.61941817240908e-25, - 3.56533744970388e-25, - 3.51159798289664e-25, - 3.46140984744989e-25, - 3.41173597486151e-25, - 3.36073006491665e-25, - 3.30753716054224e-25, - 3.25252799457143e-25, - 3.19530424634748e-25, - 3.13651908668849e-25, - 3.07841534489121e-25, - 3.01880275032991e-25, - 2.95965464815758e-25, - 2.90194743931008e-25, - 2.84696394478385e-25, - 2.79488978476361e-25, - 2.74800171431914e-25, - 2.70053589638645e-25, - 2.65588371839819e-25, - 2.6091795190684e-25, - 2.56708069403319e-25, - 2.52375403058723e-25, - 2.48382556862202e-25, - 2.44458617524673e-25, - 2.40594587498642e-25, - 2.36627105488787e-25, - 2.32528309566254e-25, - 2.28436716651676e-25, - 2.24424313328781e-25, - 2.20515093141858e-25, - 2.16647251017334e-25, - 2.12914125517962e-25, - 2.09073684368918e-25, - 2.05335637747553e-25, - 2.01912040845123e-25, - 1.98301051757051e-25, - 1.94465669006103e-25, - 1.9057000954812e-25, - 1.86388414183128e-25, - 1.82241187234978e-25, - 1.78160123951627e-25, - 1.74179167768809e-25, - 1.70080577609513e-25, - 1.6615348247565e-25, - 1.62305192083274e-25, - 1.58738563014106e-25, - 1.55171430112041e-25, - 1.51949383874537e-25, - 1.48607225456117e-25, - 1.45360366466218e-25, - 1.42252183610792e-25, - 1.39462753606039e-25, - 1.36820899679147e-25, - 1.34377836247288e-25, - 1.3233906891166e-25, - 1.30492894377563e-25, - 1.28681831161393e-25, - 1.2663174075999e-25, - 1.2420229295933e-25, - 1.21412305909061e-25, - 1.18502869999655e-25, - 1.15382448376731e-25, - 1.12540171803974e-25, - 1.09558453899584e-25, - 1.06672010609186e-25, - 1.03896476876362e-25, - 1.01172047316729e-25, - 9.84602582159865e-26, - 9.58059205575365e-26, - 9.33099533469407e-26, - 9.08936155224937e-26, - 8.8491955813636e-26, - 8.64349280941688e-26, - 8.44550335501804e-26, - 8.25653238461191e-26, - 8.08238949115334e-26, - 7.94009406468206e-26, - 7.80797137891831e-26, - 7.67364989571709e-26, - 7.54965718671858e-26, - 7.43779079827743e-26, - 7.31964666857758e-26, - 7.2232944312979e-26, - 7.12771524608971e-26, - 7.0510155861502e-26, - 6.99911412369698e-26, - 6.96383131319201e-26, - 6.92914966812787e-26, - 6.85928437919081e-26, - 6.74428511228458e-26, - 6.59224480420957e-26, - 6.38840433633278e-26, - 6.15855599572905e-26, - 5.95772659493798e-26, - 5.76494205198861e-26, - 5.60397496087846e-26, - 5.46017309852595e-26, - 5.32285644696103e-26, - 5.22365816180628e-26, - 5.07899578121548e-26, - 4.93592723237266e-26, - 4.79667132541204e-26, - 4.68132550170927e-26, - 4.56404612656666e-26, - 4.48276475068268e-26, - 4.40173864033052e-26, - 4.35417448301629e-26, - 4.30681941574933e-26, - 4.28871372503407e-26, - 4.24230311436515e-26, - 4.22301315090103e-26, - 4.19685390394596e-26, - 4.18917236558952e-26, - 4.17687017488352e-26, - 4.22019512128238e-26, - 4.2557462015274e-26, - 4.31172890769302e-26, - 4.36741466155527e-26, - 4.41740590419353e-26, - 4.44874945269751e-26, - 4.42029497925774e-26, - 4.34317624813875e-26, - 4.21853858585038e-26, - 4.01907542789597e-26, - 3.80281792816081e-26, - 3.60902775684479e-26, - 3.41921953398109e-26, - 3.25291539681547e-26, - 3.10743399295997e-26, - 2.99157340432027e-26, - 2.89660087626517e-26, - 2.82185391364051e-26, - 2.76872520775773e-26, - 2.72983807771732e-26, - 2.65963957090044e-26, - 2.62737083974039e-26, - 2.56625477748644e-26, - 2.53980768456023e-26, - 2.49759827680602e-26, - 2.47017705195044e-26, - 2.47697548770545e-26, - 2.46973660414457e-26, - 2.47714280793403e-26, - 2.50190654239581e-26, - 2.56196471101148e-26, - 2.64525680175745e-26, - 2.72275203692747e-26, - 2.826728922873e-26, - 2.94507453632992e-26, - 3.05247618840877e-26, - 3.1005889405393e-26, - 3.1508637563266e-26, - 3.13148927506362e-26 -}; - -/** - * Returns the cross section of ozone at temperature 293k and a given wavelength in the visible spectrum. - * - * @param wavelength Wavelength, in nanometers, on `[280, 780)`. - * @return Ozone cross section at temperature 293k and the given wavelength, in m-2/molecule. - */ -template -constexpr T cross_section_293k(T wavelength) -{ - int i = static_cast(wavelength); - int j = static_cast(wavelength + T(1)); - - if (i < 280 || j > 780) - return T(0); - - const T x = static_cast(cross_sections_280nm_780nm_293k[i - 280]); - const T y = static_cast(cross_sections_280nm_780nm_293k[j - 280]); - - return math::lerp(x, y, wavelength - static_cast(i)); -} - -/** - * Calculates an ozone absorption coefficient (wavelength-dependent). - * - * @param cross_section Ozone cross section of a wavelength, in m-2/molecule. - * @param density Molecular number density of ozone, in mol/m-3. - * - * @return Ozone absorption coefficient. - * - * @see https://skyrenderer.blogspot.com/2012/10/ozone-absorption.html - */ -template -T absorption(T cross_section, T density) -{ - return cross_section * density; -} - -} // namespace ozone - -} // namespace gas -} // namespace physics - -#endif // ANTKEEPER_PHYSICS_GAS_OZONE_HPP diff --git a/src/physics/light/blackbody.hpp b/src/physics/light/blackbody.hpp deleted file mode 100644 index bc5d80d..0000000 --- a/src/physics/light/blackbody.hpp +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_PHYSICS_LIGHT_BLACKBODY_HPP -#define ANTKEEPER_PHYSICS_LIGHT_BLACKBODY_HPP - -#include "math/numbers.hpp" -#include "physics/constants.hpp" - -namespace physics { -namespace light { - -/** - * Blackbody radiation functions. - * - * @see https://en.wikipedia.org/wiki/Stefan%E2%80%93Boltzmann_law - */ -namespace blackbody { - -/** - * Calculates the radiant exitance of a blackbody. - * - * @param t Temperature of the blackbody, in kelvin. - * @return Radiant exitance of the blackbody, in watt per square meter. - */ -template -T radiant_exitance(T t); - -/** - * Calculates the radiant flux of a blackbody. - * - * @param t Temperature of the blackbody, in kelvin. - * @param a Surface area of the blackbody, in square meters. - * @return Radiant flux of the blackbody, in watt. - */ -template -T radiant_flux(T t, T a); - -/** - * Calculates the radiant intensity of a blackbody. - * - * @param t Temperature of the blackbody, in kelvin. - * @param a Surface area of the blackbody, in square meters. - * @param omega Solid angle, in steradians. - * @return Radiant intensity of the blackbody, in watt per steradian. - */ -template -T radiant_intensity(T t, T a, T omega); - -/** - * Calculates the spectral exitance of a blackbody for the given wavelength. - * - * @param t Temperature of the blackbody, in kelvin. - * @param lambda Wavelength of light, in meters. - * @param c Speed of light in medium. - * @return Spectral exitance, in watt per square meter, per meter. - */ -template -T spectral_exitance(T t, T lambda, T c = constants::speed_of_light); - -/** - * Calculates the spectral flux of a blackbody for the given wavelength. - * - * @param t Temperature of the blackbody, in kelvin. - * @param a Surface area of the blackbody, in square meters. - * @param lambda Wavelength of light, in meters. - * @param c Speed of light in medium. - * @return Spectral flux of the blackbody, in watt per meter. - */ -template -T spectral_flux(T t, T a, T lambda, T c = constants::speed_of_light); - -/** - * Calculates the spectral intensity of a blackbody for the given wavelength. - * - * @param t Temperature of the blackbody, in kelvin. - * @param a Surface area of the blackbody, in square meters. - * @param lambda Wavelength of light, in meters. - * @param omega Solid angle, in steradians. - * @param c Speed of light in medium. - * @return Spectral intensity of the blackbody for the given wavelength, in watt per steradian per meter. - */ -template -T spectral_intensity(T t, T a, T lambda, T omega, T c = constants::speed_of_light); - -/** - * Calculates the spectral radiance of a blackbody for the given wavelength. - * - * @param t Temperature of the blackbody, in kelvin. - * @param lambda Wavelength of light, in meters. - * @param c Speed of light in medium. - * @return Spectral radiance, in watt per steradian per square meter per meter. - */ -template -T spectral_radiance(T t, T lambda, T c = constants::speed_of_light); - -template -T radiant_exitance(T t) -{ - const T tt = t * t; - return constants::stefan_boltzmann * (tt * tt); -} - -template -T radiant_flux(T t, T a) -{ - return a * radiant_exitance(t); -} - -template -T radiant_intensity(T t, T a, T omega) -{ - return radiant_flux(t, a) / omega; -} - -template -T spectral_exitance(T t, T lambda, T c) -{ - const T hc = constants::planck * c; - const T lambda2 = lambda * lambda; - - // First radiation constant (c1) - const T c1 = T(2) * math::pi * hc * c; - - // Second radiation constant (c2) - const T c2 = hc / constants::boltzmann; - - return (c1 / (lambda2 * lambda2 * lambda)) / std::expm1(c2 / (lambda * t)); -} - -template -T spectral_flux(T t, T a, T lambda, T c) -{ - return a * spectral_exitance(t, lambda, c); -} - -template -T spectral_intensity(T t, T a, T lambda, T omega, T c) -{ - return spectral_flux(t, a, lambda, c) / omega; -} - -template -T spectral_radiance(T t, T lambda, T c) -{ - const T hc = constants::planck * c; - const T lambda2 = lambda * lambda; - - // First radiation constant (c1L) - const T c1l = T(2) * hc * c; - - // Second radiation constant (c2) - const T c2 = hc / constants::boltzmann; - - return (c1l / (lambda2 * lambda2 * lambda)) / std::expm1(c2 / (lambda * t)); -} - -} // namespace blackbody - -} // namespace light -} // namespace physics - -#endif // ANTKEEPER_PHYSICS_LIGHT_BLACKBODY_HPP diff --git a/src/physics/light/light.hpp b/src/physics/light/light.hpp deleted file mode 100644 index 75e612f..0000000 --- a/src/physics/light/light.hpp +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_PHYSICS_LIGHT_HPP -#define ANTKEEPER_PHYSICS_LIGHT_HPP - -namespace physics { - -/// Light-related functions. -namespace light {} - -} // namespace physics - -#include "blackbody.hpp" -#include "luminosity.hpp" -#include "phase.hpp" -#include "photometry.hpp" -#include "refraction.hpp" -#include "vmag.hpp" - -#endif // ANTKEEPER_PHYSICS_LIGHT_HPP diff --git a/src/physics/light/phase.hpp b/src/physics/light/phase.hpp deleted file mode 100644 index 46fae4c..0000000 --- a/src/physics/light/phase.hpp +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_PHYSICS_LIGHT_PHASE_HPP -#define ANTKEEPER_PHYSICS_LIGHT_PHASE_HPP - -#include "math/numbers.hpp" -#include - -namespace physics { -namespace light { - -/// Light-scattering phase functions. -namespace phase { - -/** - * Cornette-Shanks phase function. - * - * @param mu Cosine of the angle between the light and view directions. - * @param g Asymmetry factor, on [-1, 1]. Positive values cause forward scattering, negative values cause back scattering. - */ -template -T cornette_shanks(T mu, T g); - -/** - * Henyey–Greenstein phase function. - * - * @param mu Cosine of the angle between the light and view directions. - * @param g Asymmetry factor, on `[-1, 1]`. Positive values cause forward scattering, negative values cause back scattering. - * - * @see http://www.pbr-book.org/3ed-2018/Volume_Scattering/Phase_Functions.html - */ -template -T henyey_greenstein(T mu, T g); - -/** - * Isotropic phase function. - */ -template -constexpr T isotropic() -{ - return T(1) / (T(4) * math::pi); -} - -/** - * Rayleigh phase function. - * - * @param mu Cosine of the angle between the light and view directions. - */ -template -T rayleigh(T mu); - -template -T cornette_shanks(T mu, T g) -{ - static const T k = T(3) / (T(8) * math::pi); - const T gg = g * g; - const T num = (T(1) - gg) * (T(1) + mu * mu); - const T den = (T(2) + gg) * std::pow(T(1) + gg - T(2) * g * mu, T(1.5)); - return k * num / den; -} - -template -T henyey_greenstein(T mu, T g) -{ - const T gg = g * g; - return (T(1) - gg) / (T(4) * math::pi * std::pow(T(1) + gg - T(2) * g * mu, T(1.5))); -} - -template -T rayleigh(T mu) -{ - static const T k = T(3) / (T(16) * math::pi); - return k * (1.0 + mu * mu); -} - -} // namespace phase - -} // namespace light -} // namespace physics - -#endif // ANTKEEPER_PHYSICS_LIGHT_PHASE_HPP diff --git a/src/physics/light/photometry.hpp b/src/physics/light/photometry.hpp deleted file mode 100644 index 0d845d7..0000000 --- a/src/physics/light/photometry.hpp +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_PHYSICS_LIGHT_PHOTOMETRY_HPP -#define ANTKEEPER_PHYSICS_LIGHT_PHOTOMETRY_HPP - -#include "math/numbers.hpp" -#include "math/quadrature.hpp" - -namespace physics { -namespace light { - -/// Maximum luminous efficacy of an ideal monochromatic source, in lumen per watt. -template -constexpr T max_luminous_efficacy = T(683.002); - -/** - * Calculates the luminous efficiency of a light source. - * - * @param spd Unary function object that returns spectral radiance given a wavelength. - * @param lef Unary function object that returns luminous efficiency given a wavelength. - * @param first,last Range of sample wavelengths. - * @return Luminous efficiency, on `[0, 1]`. - * - * @see physics::light::blackbody::spectral_radiance - * @see physics::light::luminosity::photopic - */ -template -T luminous_efficiency(UnaryOp1 spd, UnaryOp2 lef, InputIt first, InputIt last) -{ - auto spd_lef = [spd, lef](T x) -> T - { - return spd(x) * lef(x); - }; - - const T num = math::quadrature::simpson(spd_lef, first, last); - const T den = math::quadrature::simpson(spd, first, last); - - return num / den; -} - -/** - * Calculates luminous efficacy given luminous efficiency. - * - * @param efficiency Luminous efficiency, on `[0, 1]`. - * @return Luminous flux, in lumens. - */ -template -T luminous_efficacy(T efficiency) -{ - return max_luminous_efficacy * efficiency; -} - -/** - * Converts watts (radiant flux) to lumens (luminous flux). - * - * @param radiant_flux Radiant flux, in watts. - * @param efficiency Luminous efficiency, on `[0, 1]`. - * @return Luminous flux, in lumens. - */ -template -T watts_to_lumens(T radiant_flux, T efficiency) -{ - return radiant_flux * luminous_efficacy(efficiency); -} - -} // namespace light -} // namespace physics - -#endif // ANTKEEPER_PHYSICS_LIGHT_PHOTOMETRY_HPP diff --git a/src/physics/number-density.hpp b/src/physics/number-density.hpp deleted file mode 100644 index feb95a0..0000000 --- a/src/physics/number-density.hpp +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_PHYSICS_NUMBER_DENSITY_HPP -#define ANTKEEPER_PHYSICS_NUMBER_DENSITY_HPP - -#include "physics/constants.hpp" - -namespace physics { - -/** - * Calculates the number density of a substance. - * - * @param c Molar concentration, in mol/m-3. - * @return Number density, in m-3. - * - * @see https://en.wikipedia.org/wiki/Number_density - */ -template -T number_density(T c) -{ - return physics::constants::avogadro * c; -} - -} // namespace physics - -#endif // ANTKEEPER_PHYSICS_NUMBER_DENSITY_HPP diff --git a/src/physics/orbit/anomaly.hpp b/src/physics/orbit/anomaly.hpp deleted file mode 100644 index dfb14d7..0000000 --- a/src/physics/orbit/anomaly.hpp +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_PHYSICS_ORBIT_ANOMALY_HPP -#define ANTKEEPER_PHYSICS_ORBIT_ANOMALY_HPP - -#include "math/numbers.hpp" -#include - -namespace physics { -namespace orbit { - -/** - * Orbital anomaly functions. - */ -namespace anomaly { - -/** - * Derives the eccentric anomaly given eccentricity and true anomaly. - * - * @param ec Eccentricity (e). - * @param ta True anomaly (nu). - * @return Eccentric anomaly (E). - */ -template -T true_to_eccentric(T ec, T ta); - -/** - * Derives the mean anomaly given eccentricity and true anomaly. - * - * @param ec Eccentricity (e). - * @param ta True anomaly (nu). - * @return Mean anomaly (M). - */ -template -T true_to_mean(T ec, T ta); - -/** - * Derives the true anomaly given eccentricity and eccentric anomaly. - * - * @param ec Eccentricity (e). - * @param ea Eccentric anomaly (E). - * @return True anomaly (nu). - */ -template -T eccentric_to_true(T ec, T ea); - -/** - * Derives the mean anomaly given eccentricity and eccentric anomaly. - * - * @param ec Eccentricity (e). - * @param ea Eccentric anomaly (E). - * @return Mean anomaly (M). - */ -template -T eccentric_to_mean(T ec, T ea); - -/** - * Iteratively derives the eccentric anomaly given eccentricity and mean anomaly. - * - * @param ec Eccentricity (e). - * @param ma Mean anomaly (M). - * @param iterations Maximum number of iterations. - * @param tolerance Solution error tolerance. - * @return Eccentric anomaly (E). - * - * @see Murison, Marc. (2006). A Practical Method for Solving the Kepler Equation. 10.13140/2.1.5019.6808. - */ -template -T mean_to_eccentric(T ec, T ma, std::size_t iterations, T tolerance); - -/** - * Iteratively derives the true anomaly given eccentricity and mean anomaly. - * - * @param ec Eccentricity (e). - * @param ma Mean anomaly (M). - * @param iterations Maximum number of iterations. - * @param tolerance Solution error tolerance. - * @return True anomaly (nu). - */ -template -T mean_to_true(T ec, T ma, std::size_t iterations, T tolerance); - -template -T true_to_eccentric(T ec, T ta) -{ - // Parabolic orbit - if (ec == T(1)) - return std::tan(ta * T(0.5)); - - // Hyperbolic orbit - if (ec > T(1)) - return std::acosh((ec + std::cos(ta)) / (T(1) + ec * std::cos(ta))) * ((ta < T(0)) ? T(-1) : T(1)); - - // Elliptic orbit - return std::atan2(std::sqrt(T(1) - ec * ec) * std::sin(ta), std::cos(ta) + ec); -} - -template -T true_to_mean(T ec, T ta) -{ - return eccentric_to_mean(ec, true_to_eccentric(ec, ta)); -} - -template -T eccentric_to_true(T ec, T ea) -{ - // Parabolic orbit - if (ec == T(1)) - return std::atan(ea) * T(2); - - // Hyperbolic orbit - if (ec > T(1)) - return std::atan(std::sqrt((ec + T(1)) / (ec - T(1))) * std::tanh(ea * T(0.5))) * T(2); - - // Elliptic orbit - return std::atan2(sqrt(T(1) - ec * ec) * std::sin(ea), std::cos(ea) - ec); -} - -template -T eccentric_to_mean(T ec, T ea) -{ - // Parabolic orbit - if (ec == T(1)) - return (ea * ea * ea) / T(6) + ea * T(0.5); - - // Hyperbolic orbit - if (ec > T(1)) - return ec * std::sinh(ea) - ea; - - // Elliptic orbit - return ea - ec * std::sin(ea); -} - -template -T mean_to_eccentric(T ec, T ma, std::size_t iterations, T tolerance) -{ - // Wrap mean anomaly to `[-pi, pi]` - ma = std::remainder(ma, math::two_pi); - - // Third-order approximation of eccentric anomaly starting value, E0 - const T t33 = std::cos(ma); - const T t34 = ec * ec; - const T t35 = t34 * ec; - T ea0 = ma + (T(-0.5) * t35 + ec + (t34 + T(1.5) * t33 * t35) * t33) * std::sin(ma); - - // Iteratively converge E0 and E1 - for (std::size_t i = 0; i < iterations; ++i) - { - // Third-order approximation of eccentric anomaly, E1 - const T t1 = std::cos(ea0); - const T t2 = T(-1) + ec * t1; - const T t3 = std::sin(ea0); - const T t4 = ec * t3; - const T t5 = -ea0 + t4 + ma; - const T t6 = t5 / (T(0.5) * t5 * t4 / t2 + t2); - const T ea1 = ea0 - (t5 / ((T(0.5) * t3 - (T(1) / T(6)) * t1 * t6) * ec * t6 + t2)); - - // Determine solution error - const T error = std::abs(ea1 - ea0); - - // Set E0 to E1 - ea0 = ea1; - - // Break if solution is within error tolerance - if (error < tolerance) - break; - } - - return ea0; -} - -template -T mean_to_true(T ec, T ma, std::size_t iterations, T tolerance) -{ - eccentric_to_true(ec, mean_to_eccentric(ec, ma, iterations, tolerance)); -} - -} // namespace anomaly -} // namespace orbit -} // namespace physics - -#endif // ANTKEEPER_PHYSICS_ORBIT_ANOMALY_HPP diff --git a/src/physics/orbit/elements.hpp b/src/physics/orbit/elements.hpp deleted file mode 100644 index 82f54bd..0000000 --- a/src/physics/orbit/elements.hpp +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_PHYSICS_ORBIT_ELEMENTS_HPP -#define ANTKEEPER_PHYSICS_ORBIT_ELEMENTS_HPP - -#include "utility/fundamental-types.hpp" -#include "math/numbers.hpp" -#include - -namespace physics { -namespace orbit { - -/** - * Set of six Keplerian elements required to uniquely identify an orbit. - * - * @tparam T Scalar type. - */ -template -struct elements -{ - /// Scalar type. - typedef T scalar_type; - - /// Eccentricity (e). - scalar_type ec; - - /// Semimajor axis (a). - scalar_type a; - - /// Inclination (i), in radians. - scalar_type in; - - /// Right ascension of the ascending node (OMEGA), in radians. - scalar_type om; - - /// Argument of periapsis (omega), in radians. - scalar_type w; - - /// Mean anomaly (M) at epoch, in radians. - scalar_type ma; -}; - -/** - * Calculates the period of an elliptical orbit according to Kepler's third law. - * - * @param a Semimajor axis (a). - * @param gm Standard gravitational parameter (GM). - * @return Orbital period (T). - */ -template -T period(T a, T gm); - -/** - * Calculates the mean motion (n) of an orbit. - * - * @param a Semimajor axis (a). - * @param gm Standard gravitational parameter (GM). - * @return Mean motion (n), in radians per unit time. - */ -template -T mean_motion(T a, T gm); - -/** - * Calculates the mean motion (n) of an orbit. - * - * @param t Orbital period (T). - * @return Mean motion (n), in radians per unit time. - */ -template -T mean_motion(T t); - -/** - * Derives the argument of the periapsis (omega) of an orbit, given the longitude of periapsis (pomega) and longitude of the ascending node (OMEGA). - * - * @param lp Longitude of the periapsis (pomega), in radians. - * @param om Right ascension of the ascending node (OMEGA), in radians. - * @return Argument of the periapsis (omega), in radians. - */ -template -T argument_periapsis(T om, T lp); - -/** - * Derives the longitude of the periapsis (pomega) of an orbit, given the argument of periapsis (omega) and longitude of the ascending node (OMEGA). - * - * @param w Argument of the periapsis (omega), in radians. - * @param om Right ascension of the ascending node (OMEGA), in radians. - * @return Longitude of the periapsis (pomega), in radians. - */ -template -T longitude_periapsis(T om, T w); - -/** - * Derives the semiminor axis (b) of an orbit, given the semimajor axis (a) and eccentricity (e). - * - * @param a Semimajor axis (a). - * @param ec Eccentricity (e). - * @return Semiminor axis (b). - */ -template -T semiminor_axis(T a, T ec); - -/** - * Derives the semi-latus rectum (l) of an orbit, given the semimajor axis (a) and eccentricity (e). - * - * @param a Semimajor axis (a). - * @param ec Eccentricity (e). - * @return Semi-latus rectum (l). - */ -template -T semilatus_rectum(T a, T ec); - -template -T period(T a, T gm) -{ - return math::two_pi * std::sqrt((a * a * a) / gm); -} - -template -T mean_motion(T a, T gm) -{ - return std::sqrt((a * a * a) / gm); -} - -template -T mean_motion(T t) -{ - return math::two_pi / t; -} - -template -T argument_periapsis(T om, T lp) -{ - return lp - om; -} - -template -T longitude_periapsis(T om, T w) -{ - return w + om; -} - -template -T semiminor_axis(T a, T ec) -{ - return a * std::sqrt(T(1) - ec * ec); -} - -template -T semilatus_rectum(T a, T ec) -{ - return a * (T(1) - ec * ec); -} - -} // namespace orbit -} // namespace physics - -#endif // ANTKEEPER_PHYSICS_ORBIT_ELEMENTS_HPP diff --git a/src/physics/orbit/ephemeris.hpp b/src/physics/orbit/ephemeris.hpp deleted file mode 100644 index d5dfd44..0000000 --- a/src/physics/orbit/ephemeris.hpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_PHYSICS_ORBIT_EPHEMERIS_HPP -#define ANTKEEPER_PHYSICS_ORBIT_EPHEMERIS_HPP - -#include "physics/orbit/trajectory.hpp" - -namespace physics { -namespace orbit { - -/** - * Table of orbital trajectories. - * - * @tparam t Real type. - */ -template -using ephemeris = std::vector>; - -} // namespace orbit -} // namespace physics - -#endif // ANTKEEPER_PHYSICS_ORBIT_EPHEMERIS_HPP diff --git a/src/physics/orbit/frame.hpp b/src/physics/orbit/frame.hpp deleted file mode 100644 index 84ce71a..0000000 --- a/src/physics/orbit/frame.hpp +++ /dev/null @@ -1,406 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_PHYSICS_ORBIT_FRAME_HPP -#define ANTKEEPER_PHYSICS_ORBIT_FRAME_HPP - -#include "math/se3.hpp" -#include - -namespace physics { -namespace orbit { - -/// Orbital reference frames. -namespace frame { - -/// Perifocal (PQW) frame. -namespace pqw { - - /** - * Converts PQW coordinates from Cartesian to spherical. - * - * @param v PQW Cartesian coordinates. - * @return PQW spherical coordinates, in the ISO order of radial distance, inclination (radians), and true anomaly (radians). - */ - template - math::vector3 spherical(const math::vector3& v) - { - const T xx_yy = v.x() * v.x() + v.y() * v.y(); - - return math::vector3 - { - std::sqrt(xx_yy + v.z() * v.z()), - std::atan2(v.z(), std::sqrt(xx_yy)), - std::atan2(v.y(), v.x()) - }; - } - - /** - * Constructs spherical PQW coordinates from Keplerian orbital elements. - * - * @param ec Eccentricity (e). - * @param a Semimajor axis (a). - * @param ea Eccentric anomaly (E), in radians. - * @param b Semiminor axis (b). - * @return PQW spherical coordinates, in the ISO order of radial distance, inclination (radians), and true anomaly (radians). - */ - template - math::vector3 spherical(T ec, T a, T ea, T b) - { - const T x = a * (std::cos(ea) - ec); - const T y = b * std::sin(ea); - const T d = std::sqrt(x * x + y * y); - const T ta = std::atan2(y, x); - - return math::vector3{d, T(0), ta}; - } - - /** - * Constructs spherical PQW coordinates from Keplerian orbital elements. - * - * @param ec Eccentricity (e). - * @param a Semimajor axis (a). - * @param ea Eccentric anomaly (E), in radians. - * @return PQW spherical coordinates, in the ISO order of radial distance, inclination (radians), and true anomaly (radians). - */ - template - math::vector3 spherical(T ec, T a, T ea) - { - const T b = a * std::sqrt(T(1) - ec * ec); - return spherical(ec, a, ea, b); - } - - /** - * Converts PQW coordinates from spherical to Cartesian. - * - * @param PQW spherical coordinates, in the ISO order of radial distance, inclination (radians), and true anomaly (radians). - * @return PQW Cartesian coordinates. - */ - template - math::vector3 cartesian(const math::vector3& v) - { - const T x = v[0] * std::cos(v[1]); - - return math::vector3 - { - x * std::cos(v[2]), - x * std::sin(v[2]), - v[0] * std::sin(v[1]), - }; - } - - /** - * Constructs Cartesian PQW coordinates from Keplerian orbital elements. - * - * @param ec Eccentricity (e). - * @param a Semimajor axis (a). - * @param ea Eccentric anomaly (E), in radians. - * @param b Semiminor axis (b). - * @return PQW Cartesian coordinates. - */ - template - math::vector3 cartesian(T ec, T a, T ea, T b) - { - return cartesian(spherical(ec, a, ea, b)); - } - - /** - * Constructs Cartesian PQW coordinates from Keplerian orbital elements. - * - * @param ec Eccentricity (e). - * @param a Semimajor axis (a). - * @param ea Eccentric anomaly (E), in radians. - * @return PQW Cartesian coordinates. - */ - template - math::vector3 cartesian(T ec, T a, T ea) - { - return cartesian(spherical(ec, a, ea)); - } - - /** - * Constructs an SE(3) transformation from a PQW frame to a BCI frame. - * - * @param om Right ascension of the ascending node (OMEGA), in radians. - * @param in Orbital inclination (i), in radians. - * @param w Argument of periapsis (omega), in radians. - * @return PQW to BCI transformation. - */ - template - math::transformation::se3 to_bci(T om, T in, T w) - { - const math::quaternion r = math::normalize - ( - math::quaternion::rotate_z(om) * - math::quaternion::rotate_x(in) * - math::quaternion::rotate_z(w) - ); - - return math::transformation::se3{{T(0), T(0), T(0)}, r}; - } - -} // namespace pqw - -/// Body-centered inertial (BCI) frame. -namespace bci { - - /** - * Converts BCI coordinates from spherical to Cartesian. - * - * @param v BCI spherical coordinates, in the ISO order of radial distance, declination (radians), and right ascension (radians). - * @return BCI Cartesian coordinates. - */ - template - math::vector3 cartesian(const math::vector3& v) - { - const T x = v[0] * std::cos(v[1]); - - return math::vector3 - { - x * std::cos(v[2]), - x * std::sin(v[2]), - v[0] * std::sin(v[1]), - }; - } - - /** - * Converts BCI coordinates from Cartesian to spherical. - * - * @param v BCI Cartesian coordinates. - * @return BCI spherical coordinates, in the ISO order of radial distance, declination (radians), and right ascension (radians). - */ - template - math::vector3 spherical(const math::vector3& v) - { - const T xx_yy = v.x() * v.x() + v.y() * v.y(); - - return math::vector3 - { - std::sqrt(xx_yy + v.z() * v.z()), - std::atan2(v.z(), std::sqrt(xx_yy)), - std::atan2(v.y(), v.x()) - }; - } - - /** - * Constructs an SE(3) transformation from a BCI frame to a BCBF frame. - * - * @param ra Right ascension of the north pole, in radians. - * @param dec Declination of the north pole, in radians. - * @param w Location of the prime meridian, as a rotation about the north pole, in radians. - * - * @return BCI to BCBF transformation. - * - * @see Archinal, B.A., A’Hearn, M.F., Bowell, E. et al. Report of the IAU Working Group on Cartographic Coordinates and Rotational Elements: 2009. Celest Mech Dyn Astr 109, 101–135 (2011). https://doi.org/10.1007/s10569-010-9320-4 - */ - template - math::transformation::se3 to_bcbf(T ra, T dec, T w) - { - const math::quaternion r = math::normalize - ( - math::quaternion::rotate_z(-math::half_pi - ra) * - math::quaternion::rotate_x(dec - math::half_pi) * - math::quaternion::rotate_z(-w) - ); - - return math::transformation::se3{{T(0), T(0), T(0)}, r}; - } - - /** - * Constructs an SE(3) transformation from a BCI frame to a PQW frame. - * - * @param om Right ascension of the ascending node (OMEGA), in radians. - * @param in Orbital inclination (i), in radians. - * @param w Argument of periapsis (omega), in radians. - * @return BCI to PQW transformation. - */ - template - math::transformation::se3 to_pqw(T om, T in, T w) - { - const math::quaternion r = math::normalize - ( - math::quaternion::rotate_z(-w) * - math::quaternion::rotate_x(-in) * - math::quaternion::rotate_z(-om) - ); - - return math::transformation::se3{{T(0), T(0), T(0)}, r}; - } - -} // namespace bci - -/// Body-centered, body-fixed (BCBF) frame. -namespace bcbf { - - /** - * Converts BCBF coordinates from spherical to Cartesian. - * - * @param v BCBF spherical coordinates, in the ISO order of radial distance, latitude (radians), and longitude (radians). - * @return BCBF Cartesian coordinates. - */ - template - math::vector3 cartesian(const math::vector3& v) - { - const T x = v[0] * std::cos(v[1]); - - return math::vector3 - { - x * std::cos(v[2]), - x * std::sin(v[2]), - v[0] * std::sin(v[1]), - }; - } - - /** - * Converts BCBF coordinates from Cartesian to spherical. - * - * @param v BCBF Cartesian coordinates. - * @return BCBF spherical coordinates, in the ISO order of radial distance, latitude (radians), and longitude (radians). - */ - template - math::vector3 spherical(const math::vector3& v) - { - const T xx_yy = v.x() * v.x() + v.y() * v.y(); - - return math::vector3 - { - std::sqrt(xx_yy + v.z() * v.z()), - std::atan2(v.z(), std::sqrt(xx_yy)), - std::atan2(v.y(), v.x()) - }; - } - - /** - * Constructs an SE(3) transformation from a BCBF frame to a BCI frame. - * - * @param ra Right ascension of the north pole, in radians. - * @param dec Declination of the north pole, in radians. - * @param w Location of the prime meridian, as a rotation about the north pole, in radians. - * - * @return BCBF to BCI transformation. - * - * @see Archinal, B.A., A’Hearn, M.F., Bowell, E. et al. Report of the IAU Working Group on Cartographic Coordinates and Rotational Elements: 2009. Celest Mech Dyn Astr 109, 101–135 (2011). https://doi.org/10.1007/s10569-010-9320-4 - */ - template - math::transformation::se3 to_bci(T ra, T dec, T w) - { - const math::quaternion r = math::normalize - ( - math::quaternion::rotate_z(w) * - math::quaternion::rotate_x(math::half_pi - dec) * - math::quaternion::rotate_z(ra + math::half_pi) - - ); - - return math::transformation::se3{{T(0), T(0), T(0)}, r}; - } - - /** - * Constructs an SE(3) transformation from a BCBF frame to an ENU frame. - * - * @param distance Radial distance of the observer from the center of the body. - * @param latitude Latitude of the observer, in radians. - * @param longitude Longitude of the observer, in radians. - * @return BCBF to ENU transformation. - */ - template - math::transformation::se3 to_enu(T distance, T latitude, T longitude) - { - const math::vector3 t = {T(0), T(0), -distance}; - const math::quaternion r = math::normalize - ( - math::quaternion::rotate_x(-math::half_pi + latitude) * - math::quaternion::rotate_z(-longitude - math::half_pi) - ); - - return math::transformation::se3{t, r}; - } - -} // namespace bcbf - -/// East, North, Up (ENU) horizontal frame. -namespace enu { - - /** - * Converts ENU coordinates from spherical to Cartesian. - * - * @param v ENU spherical coordinates, in the ISO order of radial distance, elevation (radians), and azimuth (radians). - * @return ENU Cartesian coordinates. - */ - template - math::vector3 cartesian(const math::vector3& v) - { - const T x = v[0] * std::cos(v[1]); - const T y = math::half_pi - v[2]; - - return math::vector3 - { - x * std::cos(y), - x * std::sin(y), - v[0] * std::sin(v[1]), - }; - } - - /** - * Converts ENU coordinates from Cartesian to spherical. - * - * @param v ENU Cartesian coordinates. - * @return ENU spherical coordinates, in the ISO order of radial distance, elevation (radians), and azimuth (radians). - */ - template - math::vector3 spherical(const math::vector3& v) - { - const T xx_yy = v.x() * v.x() + v.y() * v.y(); - - return math::vector3 - { - std::sqrt(xx_yy + v.z() * v.z()), - std::atan2(v.z(), std::sqrt(xx_yy)), - math::half_pi - std::atan2(v.y(), v.x()) - }; - } - - /** - * Constructs an SE(3) transformation from an ENU frame to a BCBF frame. - * - * @param distance Radial distance of the observer from the center of the body. - * @param latitude Latitude of the observer, in radians. - * @param longitude Longitude of the observer, in radians. - * @return ENU to BCBF transformation. - */ - template - math::transformation::se3 to_bcbf(T distance, T latitude, T longitude) - { - const math::vector3 t = {T(0), T(0), distance}; - const math::quaternion r = math::normalize - ( - math::quaternion::rotate_z(longitude + math::half_pi) * - math::quaternion::rotate_x(math::half_pi - latitude) - ); - - return math::transformation::se3{r * t, r}; - } - -} // namespace enu - -} // namespace frame -} // namespace orbit -} // namespace physics - -#endif // ANTKEEPER_PHYSICS_ORBIT_FRAME_HPP diff --git a/src/physics/orbit/orbit.hpp b/src/physics/orbit/orbit.hpp deleted file mode 100644 index ce2c9e5..0000000 --- a/src/physics/orbit/orbit.hpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_PHYSICS_ORBIT_HPP -#define ANTKEEPER_PHYSICS_ORBIT_HPP - -namespace physics { - -/** - * Orbital mechanics. - * - * @see Curtis, H. D. (2005). *Orbital mechanics for engineering students*. Amsterdam: Elsevier Butterworth Heinemann. - */ -namespace orbit {} - -} // namespace physics - -#include "physics/orbit/anomaly.hpp" -#include "physics/orbit/elements.hpp" -#include "physics/orbit/frame.hpp" -#include "physics/orbit/state.hpp" - -#endif // ANTKEEPER_PHYSICS_ORBIT_HPP diff --git a/src/physics/orbit/state.hpp b/src/physics/orbit/state.hpp deleted file mode 100644 index 0b751af..0000000 --- a/src/physics/orbit/state.hpp +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_PHYSICS_ORBIT_STATE_HPP -#define ANTKEEPER_PHYSICS_ORBIT_STATE_HPP - -#include "utility/fundamental-types.hpp" - -namespace physics { -namespace orbit { - -/** - * Pair of orbital state Cartesian position (r) and velocity (v) vectors. - * - * @tparam T Scalar type. - */ -template -struct state -{ - /// Scalar type. - typedef T scalar_type; - - /// Vector type. - typedef math::vector3 vector_type; - - /// Cartesian orbital position vector (r). - vector_type r; - - /// Cartesian orbital velocity vector (v). - vector_type v; -}; - -} // namespace orbit -} // namespace physics - -#endif // ANTKEEPER_PHYSICS_ORBIT_STATE_HPP diff --git a/src/physics/orbit/trajectory.hpp b/src/physics/orbit/trajectory.hpp deleted file mode 100644 index f1b17cd..0000000 --- a/src/physics/orbit/trajectory.hpp +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_PHYSICS_ORBIT_TRAJECTORY_HPP -#define ANTKEEPER_PHYSICS_ORBIT_TRAJECTORY_HPP - -#include "math/polynomial.hpp" -#include "math/vector.hpp" -#include - -namespace physics { -namespace orbit { - -/** - * Describes the trajectory of an orbit with Chebyshev polynomials. - * - * @tparam t Real type. - */ -template -struct trajectory -{ - /// Start time of the trajectory. - T t0; - - /// End time of the trajectory. - T t1; - - /// Time step duration. - T dt; - - /// Chebyshev polynomial degree. - std::size_t n; - - /// Chebyshev polynomial coefficients. - std::vector a; - - /** - * Calculates the Cartesian position of a trajectory at a given time. - * - * @param t Time, on `[t0, t1)`. - * @return Trajectory position at time @p t. - */ - math::vector position(T t) const; -}; - -template -math::vector trajectory::position(T t) const -{ - t -= t0; - std::size_t i = static_cast(t / dt); - - const T* ax = &a[i * n * 3]; - const T* ay = ax + n; - const T* az = ay + n; - - t = (t / dt - i) * T(2) - T(1); - - math::vector3 r; - r.x() = math::polynomial::chebyshev::evaluate(ax, ay, t); - r.y() = math::polynomial::chebyshev::evaluate(ay, az, t); - r.z() = math::polynomial::chebyshev::evaluate(az, az + n, t); - - return r; -} - -} // namespace orbit -} // namespace physics - -#endif // ANTKEEPER_PHYSICS_ORBIT_TRAJECTORY_HPP diff --git a/src/physics/physics.hpp b/src/physics/physics.hpp deleted file mode 100644 index fad5094..0000000 --- a/src/physics/physics.hpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_PHYSICS_HPP -#define ANTKEEPER_PHYSICS_HPP - -/// Physics -namespace physics {} - -#include "constants.hpp" -#include "frame.hpp" -#include "number-density.hpp" -#include "planck.hpp" -#include "gas/gas.hpp" -#include "light/light.hpp" -#include "orbit/orbit.hpp" -#include "time/time.hpp" - -#endif // ANTKEEPER_PHYSICS_HPP diff --git a/src/physics/planck.hpp b/src/physics/planck.hpp deleted file mode 100644 index 232ae2c..0000000 --- a/src/physics/planck.hpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_PHYSICS_PLANCK_HPP -#define ANTKEEPER_PHYSICS_PLANCK_HPP - -#include "physics/constants.hpp" -#include - -namespace physics { - -/// Various forms of Planck's law. -namespace planck { - -/** - * Wavelength variant of Planck's law. - * - * @param t Temperature of the blackbody, in kelvin. - * @param lambda Wavelength of light, in meters. - * @param c Speed of light in medium. - * @return Spectral radiance, in watt per steradian per square meter per meter. - */ -template -T wavelength(T t, T lambda, T c = constants::speed_of_light); - -template -T wavelength(T t, T lambda, T c) -{ - const T hc = constants::planck * c; - const T lambda2 = lambda * lambda; - - // First radiation constant (c1L) - const T c1 = T(2) * hc * c; - - // Second radiation constant (c2) - const T c2 = hc / constants::boltzmann; - - return (c1 / (lambda2 * lambda2 * lambda)) / std::expm1(c2 / (lambda * t)); -} - -} // namespace planck - -} // namespace physics - -#endif // ANTKEEPER_PHYSICS_PLANCK_HPP diff --git a/src/physics/time/gregorian.hpp b/src/physics/time/gregorian.hpp deleted file mode 100644 index 309908e..0000000 --- a/src/physics/time/gregorian.hpp +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_PHYSICS_TIME_GREGORIAN_HPP -#define ANTKEEPER_PHYSICS_TIME_GREGORIAN_HPP - -#include "physics/time/jd.hpp" - -namespace physics { -namespace time { - -/// Gregorian calender time. -namespace gregorian { - -/** - * Calculates the JD time from a Gregorian date and time. Valid for all dates after November 23, −4713. - * - * @param year Astronomical year numbering. 1 BC is `0`, 2 BC is `-1`. - * @param month Month number on `[1, 12]`. - * @param day Day number on `[1, 31]`. - * @param hour Hour number on `[0, 23]`. - * @param minute Minute number on `[0, 59]`. - * @param second Fractional second on `[0.0, 60.0)`. - * @param utc UTC offset. - * - * @return JD time. - * - * @see L. E. Doggett, Ch. 12, "Calendars", p. 606, in Seidelmann 1992 - */ -template -T to_jd(int year, int month, int day, int hour, int minute, T second, T utc) -{ - T jdn = static_cast((1461 * (year + 4800 + (month - 14) / 12)) / 4 + (367 * (month - 2 - 12 * ((month - 14) / 12))) / 12 - (3 * ((year + 4900 + (month - 14) / 12) / 100)) / 4 + day - 32075); - return jdn + static_cast(hour - 12) / T(24) + static_cast(minute) / T(1440) + static_cast(second) / T(86400) - utc / T(24); -} - -/** - * Calculates the UT1 time from a Gregorian date and time. Valid for all dates after November 23, −4713. - * - * @param year Astronomical year numbering. 1 BC is `0`, 2 BC is `-1`. - * @param month Month number on `[1, 12]`. - * @param day Day number on `[1, 31]`. - * @param hour Hour number on `[0, 23]`. - * @param minute Minute number on `[0, 59]`. - * @param second Fractional second on `[0.0, 60.0)`. - * @param utc UTC offset. - * - * @return UT1 time. - */ -template -T to_ut1(int year, int month, int day, int hour, int minute, T second, T utc) -{ - return physics::time::jd::to_ut1(to_jd(year, month, day, hour, minute, second, utc)); -} - -} // namespace gregorian - -} // namespace time -} // namespace physics - -#endif // ANTKEEPER_PHYSICS_TIME_GREGORIAN_HPP diff --git a/src/physics/time/time.hpp b/src/physics/time/time.hpp deleted file mode 100644 index 76b1122..0000000 --- a/src/physics/time/time.hpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_PHYSICS_TIME_HPP -#define ANTKEEPER_PHYSICS_TIME_HPP - -namespace physics { - -/// Time-related functions. -namespace time {} - -} // namespace physics - -#include "constants.hpp" -#include "gregorian.hpp" -#include "jd.hpp" -#include "ut1.hpp" -#include "utc.hpp" - -#endif // ANTKEEPER_PHYSICS_TIME_HPP diff --git a/src/physics/time/ut1.hpp b/src/physics/time/ut1.hpp deleted file mode 100644 index 59424f9..0000000 --- a/src/physics/time/ut1.hpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_PHYSICS_TIME_UT1_HPP -#define ANTKEEPER_PHYSICS_TIME_UT1_HPP - -#include "math/numbers.hpp" - -namespace physics { -namespace time { - -/// UT1 universal time. -namespace ut1 { - -/** - * Converts UT1 to JD. - * - * @param t UT1 time. - * @return JD time. - */ -template -T to_jd(T t) -{ - return t + T(2451545); -} - -/** - * Calculates the Earth Rotation Angle (ERA) at a given UT1 date. - * - * @param t J2000 UT1 date. - * @return ERA at the given date, in radians. - */ -template -T era(T t) -{ - return math::two_pi * (T(0.7790572732640) + T(1.00273781191135448) * t); -} - -} // namespace ut1 - -} // namespace time -} // namespace physics - -#endif // ANTKEEPER_PHYSICS_TIME_UT1_HPP diff --git a/src/physics/time/utc.hpp b/src/physics/time/utc.hpp deleted file mode 100644 index 8485cef..0000000 --- a/src/physics/time/utc.hpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_PHYSICS_TIME_UTC_HPP -#define ANTKEEPER_PHYSICS_TIME_UTC_HPP - -#include "math/numbers.hpp" - -namespace physics { -namespace time { - -/// Coordinated Universal Time (UTC). -namespace utc { - -/** - * Calculates the UTC offset at a given longitude - * - * @param longitude Longitude, in radians. - * @return UTC offset. - */ -template -T offset(T longitude) -{ - return longitude / (math::two_pi / 24.0); -} - -} // namespace utc - -} // namespace time -} // namespace physics - -#endif // ANTKEEPER_PHYSICS_TIME_UTC_HPP diff --git a/src/render/compositor.cpp b/src/render/compositor.cpp deleted file mode 100644 index 547d7fd..0000000 --- a/src/render/compositor.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "render/compositor.hpp" -#include "render/pass.hpp" - -namespace render { - -void compositor::add_pass(pass* pass) -{ - passes.push_back(pass); -} - -void compositor::remove_pass(pass* pass) -{ - passes.remove(pass); -} - -void compositor::remove_passes() -{ - passes.clear(); -} - -void compositor::composite(const render::context& ctx, render::queue& queue) const -{ - for (const pass* pass: passes) - { - if (pass->is_enabled()) - { - pass->render(ctx, queue); - } - } -} - -} // namespace render diff --git a/src/render/compositor.hpp b/src/render/compositor.hpp deleted file mode 100644 index 8ea66b5..0000000 --- a/src/render/compositor.hpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_RENDER_COMPOSITOR_HPP -#define ANTKEEPER_RENDER_COMPOSITOR_HPP - -#include "render/context.hpp" -#include "render/queue.hpp" -#include - -namespace render { - -class pass; - -/** - * - */ -class compositor -{ -public: - void add_pass(pass* pass); - void remove_pass(pass* pass); - void remove_passes(); - - void composite(const render::context& ctx, render::queue& queue) const; - - const std::list* get_passes() const; - -private: - std::list passes; -}; - -inline const std::list* compositor::get_passes() const -{ - return &passes; -} - -} // namespace render - -#endif // ANTKEEPER_RENDER_COMPOSITOR_HPP diff --git a/src/render/context.hpp b/src/render/context.hpp deleted file mode 100644 index 523a23c..0000000 --- a/src/render/context.hpp +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_RENDER_CONTEXT_HPP -#define ANTKEEPER_RENDER_CONTEXT_HPP - -#include "geom/plane.hpp" -#include "geom/bounding-volume.hpp" -#include "utility/fundamental-types.hpp" -#include "math/transform-operators.hpp" -#include - -namespace scene -{ - class camera; - class collection; - class object_base; -} - -namespace render { - -/** - * Context of a renderer. - */ -struct context -{ - /// Pointer to the camera. - const scene::camera* camera; - - /// Camera transform. - math::transform camera_transform; - - /// Camera forward vector - float3 camera_forward; - - /// Camera up vector. - float3 camera_up; - - /// Camera culling volume. - const geom::bounding_volume* camera_culling_volume; - - /// Near clipping plane of the camera. - geom::plane clip_near; - - /// Camera view matrix. - float4x4 view; - - /// Camera projection matrix. - float4x4 projection; - - /// Camera view projection matrix. - float4x4 view_projection; - - /// Camera exposure normalization factor. - float exposure; - - /// Collection of scene objects being rendered. - const scene::collection* collection; - - /// Current time, in seconds. - float t; - - /// Timestep, in seconds. - float dt; - - /// Subframe interpolation factor. - float alpha; - - /// List of objects visible to the active camera. - std::list visible_objects; -}; - -} // namespace render - -#endif // ANTKEEPER_RENDER_CONTEXT_HPP diff --git a/src/render/material-property.cpp b/src/render/material-property.cpp deleted file mode 100644 index 14efdb4..0000000 --- a/src/render/material-property.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "render/material-property.hpp" -#include "gl/shader-input.hpp" - -namespace render { - -material_property_base::material_property_base(): - input(nullptr) -{} - -bool material_property_base::connect(const gl::shader_input* input) -{ - if (!input || input->get_data_type() != get_data_type()) - { - return false; - } - - this->input = input; - - return true; -} - -void material_property_base::disconnect() -{ - this->input = nullptr; -} - -} // namespace render diff --git a/src/render/material-property.hpp b/src/render/material-property.hpp deleted file mode 100644 index 0d2a5cf..0000000 --- a/src/render/material-property.hpp +++ /dev/null @@ -1,460 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_RENDER_MATERIAL_PROPERTY_HPP -#define ANTKEEPER_RENDER_MATERIAL_PROPERTY_HPP - -#include "animation/tween.hpp" -#include "gl/shader-variable-type.hpp" -#include "gl/shader-input.hpp" -#include "math/interpolation.hpp" -#include "utility/fundamental-types.hpp" -#include "gl/shader-program.hpp" -#include "gl/texture-1d.hpp" -#include "gl/texture-2d.hpp" -#include "gl/texture-3d.hpp" -#include "gl/texture-cube.hpp" -#include - -namespace render { - -class material; - -/** - * Abstract base class for material properties. - */ -class material_property_base -{ -public: - /** - * Connects the material property to a shader input. - * - * @param input Shader input to which the material property should be connected. - * @return `true` if the property was connected to the input successfully, `false` otherwise. - */ - bool connect(const gl::shader_input* input); - - /** - * Disconnects the material property from its shader input. - */ - void disconnect(); - - /** - * Sets state 0 = state 1. - */ - virtual void update_tweens() = 0; - - /** - * Uploads the material property to its shader program. - * - * @param a Interpolation factor. Should be on `[0.0, 1.0]`. - * @return `true` if the property was uploaded successfully, `false` otherwise. - */ - virtual bool upload(double a) const = 0; - - /** - * Returns the type of data which the property contains. - */ - virtual gl::shader_variable_type get_data_type() const = 0; - - /** - * Returns `true` if the material property is connected to a shader input, `false` otherwise. - */ - bool is_connected() const; - - /** - * Creates a copy of this material property. - */ - virtual material_property_base* clone() const = 0; - -protected: - material_property_base(); - - const gl::shader_input* input; -}; - -inline bool material_property_base::is_connected() const -{ - return (input != nullptr); -} - -/** - * A property of a material which can be uploaded to a shader program via a shader input. - * - * @tparam T Property data type. - */ -template -class material_property: public material_property_base -{ -public: - typedef tween tween_type; - typedef typename tween::interpolator_type interpolator_type; - - /// Default tween interpolator function for this material property type. - static T default_interpolator(const T& x, const T& y, double a); - - /** - * Creates a material property. - * - * @param element_count Number of elements in the property array. - */ - material_property(std::size_t element_count); - - /** - * Destroys a material property. - */ - virtual ~material_property(); - - material_property(const material_property&) = delete; - material_property& operator=(const material_property&) = delete; - - /// @copydoc material_property_base::update_tweens() - virtual void update_tweens(); - - /// @copydoc material_property_base::upload() const - virtual bool upload(double a) const; - - /** - * Sets the value of this property. - * - * @param value Value to set. - */ - void set_value(const T& value); - - /** - * Sets the value of a single element in this array property. - * - * @param index Index of an array element. - * @param value Value to set. - */ - void set_value(std::size_t index, const T& value); - - /** - * Sets the values of a range of elements in this array property. - * - * @param index Index of the first array element to set. - * @param values Pointer to an array of values to set. - * @param count Number of elements to set. - */ - void set_values(std::size_t index, const T* values, std::size_t count); - - /** - * Sets the tween interpolator function. - * - * @param interpolator Tween interpolator function. - */ - void set_tween_interpolator(interpolator_type interpolator); - - /// Returns the value of the first element in this property. - const T& get_value() const; - - /** - * Returns the value of the first element in this property. - * - * @param index Index of an array element. - * @return Value of the element at the specified index. - */ - const T& get_value(std::size_t index) const; - - /// @copydoc material_property_base::get_data_type() const - virtual gl::shader_variable_type get_data_type() const; - - /// @copydoc material_property_base::clone() const - virtual material_property_base* clone() const; - -private: - std::size_t element_count; - tween* values; -}; - -template -inline T material_property::default_interpolator(const T& x, const T& y, double a) -{ - return y; -} - -template <> -inline float material_property::default_interpolator(const float& x, const float& y, double a) -{ - return math::lerp(x, y, static_cast(a)); -} - -template <> -inline float2 material_property::default_interpolator(const float2& x, const float2& y, double a) -{ - return math::lerp(x, y, static_cast(a)); -} - -template <> -inline float3 material_property::default_interpolator(const float3& x, const float3& y, double a) -{ - return math::lerp(x, y, static_cast(a)); -} - -template <> -inline float4 material_property::default_interpolator(const float4& x, const float4& y, double a) -{ - return math::lerp(x, y, static_cast(a)); -} - -template -material_property::material_property(std::size_t element_count): - element_count(element_count), - values(nullptr) -{ - values = new tween[element_count]; - set_tween_interpolator(default_interpolator); -} - -template -material_property::~material_property() -{ - delete[] values; -} - -template -void material_property::update_tweens() -{ - for (std::size_t i = 0; i < element_count; ++i) - { - values[i].update(); - } -} - -template -bool material_property::upload(double a) const -{ - if (!is_connected()) - { - return false; - } - - if (element_count > 1) - { - for (std::size_t i = 0; i < element_count; ++i) - { - if (!input->upload(i, values[i].interpolate(static_cast(a)))) - return false; - } - - return true; - } - else - { - return input->upload(values[0].interpolate(static_cast(a))); - } -} - -template -void material_property::set_value(const T& value) -{ - values[0][1] = value; -} - -template -void material_property::set_value(std::size_t index, const T& value) -{ - values[index][1] = value; -} - -template -void material_property::set_values(std::size_t index, const T* values, std::size_t count) -{ - for (std::size_t i = 0; i < count; ++i) - { - this->values[index + i][1] = values[i]; - } -} - -template -void material_property::set_tween_interpolator(interpolator_type interpolator) -{ - for (std::size_t i = 0; i < element_count; ++i) - { - this->values[i].set_interpolator(interpolator); - } -} - -template -inline const T& material_property::get_value() const -{ - return values[0][1]; -} - -template -inline const T& material_property::get_value(std::size_t index) const -{ - return values[index][1]; -} - -template <> -inline gl::shader_variable_type material_property::get_data_type() const -{ - return gl::shader_variable_type::bool1; -} - -template <> -inline gl::shader_variable_type material_property::get_data_type() const -{ - return gl::shader_variable_type::bool2; -} - -template <> -inline gl::shader_variable_type material_property::get_data_type() const -{ - return gl::shader_variable_type::bool3; -} - -template <> -inline gl::shader_variable_type material_property::get_data_type() const -{ - return gl::shader_variable_type::bool4; -} - -template <> -inline gl::shader_variable_type material_property::get_data_type() const -{ - return gl::shader_variable_type::int1; -} - -template <> -inline gl::shader_variable_type material_property::get_data_type() const -{ - return gl::shader_variable_type::int2; -} - -template <> -inline gl::shader_variable_type material_property::get_data_type() const -{ - return gl::shader_variable_type::int3; -} - -template <> -inline gl::shader_variable_type material_property::get_data_type() const -{ - return gl::shader_variable_type::int4; -} - -template <> -inline gl::shader_variable_type material_property::get_data_type() const -{ - return gl::shader_variable_type::uint1; -} - -template <> -inline gl::shader_variable_type material_property::get_data_type() const -{ - return gl::shader_variable_type::uint2; -} - -template <> -inline gl::shader_variable_type material_property::get_data_type() const -{ - return gl::shader_variable_type::uint3; -} - -template <> -inline gl::shader_variable_type material_property::get_data_type() const -{ - return gl::shader_variable_type::uint4; -} - -template <> -inline gl::shader_variable_type material_property::get_data_type() const -{ - return gl::shader_variable_type::float1; -} - -template <> -inline gl::shader_variable_type material_property::get_data_type() const -{ - return gl::shader_variable_type::float2; -} - -template <> -inline gl::shader_variable_type material_property::get_data_type() const -{ - return gl::shader_variable_type::float3; -} - -template <> -inline gl::shader_variable_type material_property::get_data_type() const -{ - return gl::shader_variable_type::float4; -} - -template <> -inline gl::shader_variable_type material_property::get_data_type() const -{ - return gl::shader_variable_type::float2x2; -} - -template <> -inline gl::shader_variable_type material_property::get_data_type() const -{ - return gl::shader_variable_type::float3x3; -} - -template <> -inline gl::shader_variable_type material_property::get_data_type() const -{ - return gl::shader_variable_type::float4x4; -} - -template <> -inline gl::shader_variable_type material_property::get_data_type() const -{ - return gl::shader_variable_type::texture_1d; -} - -template <> -inline gl::shader_variable_type material_property::get_data_type() const -{ - return gl::shader_variable_type::texture_2d; -} - -template <> -inline gl::shader_variable_type material_property::get_data_type() const -{ - return gl::shader_variable_type::texture_3d; -} - -template <> -inline gl::shader_variable_type material_property::get_data_type() const -{ - return gl::shader_variable_type::texture_cube; -} - -template -material_property_base* material_property::clone() const -{ - material_property* property = new material_property(element_count); - for (std::size_t i = 0; i < element_count; ++i) - { - property->values[i][0] = values[i][0]; - property->values[i][1] = values[i][1]; - } - property->input = input; - - return property; -} - -} // namespace render - -#endif // ANTKEEPER_RENDER_MATERIAL_PROPERTY_HPP diff --git a/src/render/material.cpp b/src/render/material.cpp deleted file mode 100644 index 1d58fc5..0000000 --- a/src/render/material.cpp +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "render/material.hpp" - -namespace render { - -material::material(gl::shader_program* program): - program(program), - flags(0), - blend_mode(blend_mode::opaque), - opacity_threshold(0.5f), - two_sided(false), - shadow_mode(shadow_mode::opaque) -{} - -material::material(): - material(nullptr) -{} - -material::material(const material& other) -{ - *this = other; -} - -material::~material() -{ - for (material_property_base* property: properties) - { - delete property; - } -} - -material& material::operator=(const material& other) -{ - // Remove all properties - for (material_property_base* property: properties) - { - delete property; - } - properties.clear(); - property_map.clear(); - - this->program = other.program; - this->flags = other.flags; - this->blend_mode = other.blend_mode; - this->opacity_threshold = other.opacity_threshold; - this->two_sided = other.two_sided; - this->shadow_mode = other.shadow_mode; - for (auto it = other.property_map.begin(); it != other.property_map.end(); ++it) - { - material_property_base* property = it->second->clone(); - properties.push_back(property); - property_map[it->first] = property; - } - - return *this; -} - -void material::update_tweens() -{ - for (material_property_base* property: properties) - { - property->update_tweens(); - } -} - -std::size_t material::upload(double a) const -{ - if (!program) - { - return false; - } - - std::size_t failed_upload_count = 0; - - for (material_property_base* property: properties) - { - if (!property->upload(a)) - { - ++failed_upload_count; - } - } - - return failed_upload_count; -} - -void material::set_shader_program(gl::shader_program* program) -{ - this->program = program; - reconnect_properties(); -} - -void material::set_flags(std::uint32_t flags) noexcept -{ - this->flags = flags; -} - -void material::set_blend_mode(render::blend_mode mode) noexcept -{ - blend_mode = mode; -} - -void material::set_opacity_threshold(float threshold) noexcept -{ - opacity_threshold = threshold; -} - -void material::set_two_sided(bool two_sided) noexcept -{ - this->two_sided = two_sided; -} - -void material::set_shadow_mode(render::shadow_mode mode) noexcept -{ - shadow_mode = mode; -} - -std::size_t material::reconnect_properties() -{ - std::size_t disconnected_property_count = properties.size(); - - for (auto it = property_map.begin(); it != property_map.end(); ++it) - { - material_property_base* property = it->second; - - property->disconnect(); - - if (program != nullptr) - { - if (property->connect(program->get_input(it->first))) - { - --disconnected_property_count; - } - } - } - - return disconnected_property_count; -} - -} // namespace render diff --git a/src/render/material.hpp b/src/render/material.hpp deleted file mode 100644 index e190de0..0000000 --- a/src/render/material.hpp +++ /dev/null @@ -1,254 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_RENDER_MATERIAL_HPP -#define ANTKEEPER_RENDER_MATERIAL_HPP - -#include "render/blend-mode.hpp" -#include "render/material-property.hpp" -#include "render/shadow-mode.hpp" -#include "gl/shader-program.hpp" -#include -#include -#include -#include -#include - -namespace render { - -/** - * A material is associated with exactly one shader program and contains a set of material properties which can be uploaded to that shader program via shader inputs. - */ -class material -{ -public: - /** - * Creates a material. - * - * @param program Shader program with which to associate this material. - */ - explicit material(gl::shader_program* program); - - /** - * Creates a material. - */ - material(); - - /** - * Creates a copy of another material. - * - * @param other Material to copy. - */ - material(const material& other); - - /** - * Destroys a material. - */ - ~material(); - - /** - * Makes this material a copy of aother material. - * - * @param other Material to copy. - * @return Reference to this material. - */ - material& operator=(const material& other); - - /** - * Sets state 0 = state 1 for each material property tween. - */ - void update_tweens(); - - /** - * Uploads each material property to the material's shader program. - * - * @param a Interpolation factor. Should be on `[0.0, 1.0]`. - * @return Number of material property uploads which failed. - */ - std::size_t upload(double a) const; - - /** - * Sets the material's shader program and reconnects all shader properties to their corresponding shader inputs. - * - * @param program Shader program with which to associate the material. - */ - void set_shader_program(gl::shader_program* program); - - /** - * Sets the material flags. - * - * @param flags Material flags. - */ - void set_flags(std::uint32_t flags) noexcept; - - /** - * Sets the material blend mode. - * - * @param mode Blend mode. - */ - void set_blend_mode(blend_mode mode) noexcept; - - /** - * Sets the opacity mask threshold value for masked blend mode. - * - * @param threshold Opacity mask threshold value, above which the surface is considered opaque. - * - * @see render::blend_mode::masked - */ - void set_opacity_threshold(float threshold) noexcept; - - /** - * Enables or disables back-face culling of the material surface. - * - * @param two_sided `true` to disable back-face culling, or `false` to enable it. - */ - void set_two_sided(bool two_sided) noexcept; - - /** - * Sets the material shadow mode. - * - * @param mode Shadow mode. - */ - void set_shadow_mode(shadow_mode mode) noexcept; - - /** - * Adds a material array property to the material. - * - * @param name Name of the material array property. - * @param element_count Number of elements in the array. - * @return Pointer to the added material property. - */ - template - material_property* add_property(const std::string& name, std::size_t element_count = 1); - - /** - * Returns the shader program with which this material is associated. - */ - gl::shader_program* get_shader_program() const; - - /// Returns the material flags. - std::uint32_t get_flags() const noexcept; - - /// Returns the material blend mode. - blend_mode get_blend_mode() const noexcept; - - /// Returns the opacity mask threshold value. - float get_opacity_threshold() const noexcept; - - /// Returns `true` if the material surface is two-sided, and `false` otherwise. - bool is_two_sided() const noexcept; - - /// Returns the material shadow mode. - shadow_mode get_shadow_mode() const noexcept; - - /** - * Returns the material property with the specified name, or `nullptr` if the material could not be found. - */ - material_property_base* get_property(const std::string& name) const; - - /** - * Returns a list of all material properties in the material. - */ - const std::list* get_properties() const; - -private: - /** - * Attempts to reconnect all material properties to their corresponding shader inputs. - * - * @return Number of disconnected properties. - */ - std::size_t reconnect_properties(); - - gl::shader_program* program; - std::uint32_t flags; - blend_mode blend_mode; - float opacity_threshold; - bool two_sided; - shadow_mode shadow_mode; - std::list properties; - std::unordered_map property_map; -}; - -template -material_property* material::add_property(const std::string& name, std::size_t element_count) -{ - // Allocate property - material_property* property = new material_property(element_count); - - // Add to property list and map - properties.push_back(property); - property_map[name] = property; - - // Attempt to connect property to its corresponding shader input - if (program) - { - property->connect(program->get_input(name)); - } - - return property; -} - -inline gl::shader_program* material::get_shader_program() const -{ - return program; -} - -inline std::uint32_t material::get_flags() const noexcept -{ - return flags; -} - -inline blend_mode material::get_blend_mode() const noexcept -{ - return blend_mode; -} - -inline float material::get_opacity_threshold() const noexcept -{ - return opacity_threshold; -} - -inline bool material::is_two_sided() const noexcept -{ - return two_sided; -} - -inline shadow_mode material::get_shadow_mode() const noexcept -{ - return shadow_mode; -} - -inline material_property_base* material::get_property(const std::string& name) const -{ - if (auto it = property_map.find(name); it != property_map.end()) - { - return it->second; - } - - return nullptr; -} - -inline const std::list* material::get_properties() const -{ - return &properties; -} - -} // namespace render - -#endif // ANTKEEPER_RENDER_MATERIAL_HPP diff --git a/src/render/model.cpp b/src/render/model.cpp deleted file mode 100644 index f821356..0000000 --- a/src/render/model.cpp +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "render/model.hpp" - -namespace render { - -model::model(): - bounds({0, 0, 0}, {0, 0, 0}) -{} - -model::~model() -{ - for (model_group* group: groups) - { - delete group; - } -} - -model_group* model::add_group(const std::string& name) -{ - if (!name.empty()) - { - if (auto it = group_map.find(name); it != group_map.end()) - { - return it->second; - } - } - - model_group* group = new model_group(); - group->index = groups.size(); - group->name = name; - group->material = nullptr; - group->drawing_mode = gl::drawing_mode::triangles; - group->start_index = 0; - group->index_count = 0; - - groups.push_back(group); - - if (!name.empty()) - { - group_map[name] = group; - } - - return group; -} - -bool model::remove_group(const std::string& name) -{ - if (auto it = group_map.find(name); it != group_map.end()) - { - return remove_group(it->second); - } - - return false; -} - -bool model::remove_group(model_group* group) -{ - // Remove from group map - if (!group->name.empty()) - { - if (auto it = group_map.find(group->name); it != group_map.end()) - { - group_map.erase(it); - } - } - - // Adjust indices of groups after this group - for (std::size_t i = group->index + 1; i < groups.size(); ++i) - { - --groups[i]->index; - } - - // Remove from groups - groups.erase(groups.begin() + group->index); - - // Deallocate group - delete group; - - return true; -} - -const model_group* model::get_group(const std::string& name) const -{ - if (auto it = group_map.find(name); it != group_map.end()) - { - return it->second; - } - - return nullptr; -} - -model_group* model::get_group(const std::string& name) -{ - if (auto it = group_map.find(name); it != group_map.end()) - { - return it->second; - } - - return nullptr; -} - -} // namespace render diff --git a/src/render/model.hpp b/src/render/model.hpp deleted file mode 100644 index 046118b..0000000 --- a/src/render/model.hpp +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_RENDER_MODEL_HPP -#define ANTKEEPER_RENDER_MODEL_HPP - -#include "animation/skeleton.hpp" -#include "gl/vertex-array.hpp" -#include "gl/vertex-buffer.hpp" -#include "gl/drawing-mode.hpp" -#include "render/material.hpp" -#include "geom/aabb.hpp" -#include -#include -#include - -namespace render { - -/** - * Part of a model which is associated with exactly one material. - */ -class model_group -{ -public: - void set_material(material* material); - void set_drawing_mode(gl::drawing_mode mode); - void set_start_index(std::size_t index); - void set_index_count(std::size_t count); - - std::size_t get_index() const; - const std::string& get_name() const; - const material* get_material() const; - material* get_material(); - gl::drawing_mode get_drawing_mode() const; - std::size_t get_start_index() const; - std::size_t get_index_count() const; - -private: - friend class model; - - std::size_t index; - std::string name; - material* material; - gl::drawing_mode drawing_mode; - std::size_t start_index; - std::size_t index_count; -}; - -inline void model_group::set_material(render::material* material) -{ - this->material = material; -} - -inline void model_group::set_drawing_mode(gl::drawing_mode mode) -{ - this->drawing_mode = mode; -} - -inline void model_group::set_start_index(std::size_t index) -{ - this->start_index = index; -} - -inline void model_group::set_index_count(std::size_t count) -{ - this->index_count = count; -} - -inline std::size_t model_group::get_index() const -{ - return index; -} - -inline const std::string& model_group::get_name() const -{ - return name; -} - -inline const material* model_group::get_material() const -{ - return material; -} - -inline material* model_group::get_material() -{ - return material; -} - -inline gl::drawing_mode model_group::get_drawing_mode() const -{ - return drawing_mode; -} - -inline std::size_t model_group::get_start_index() const -{ - return start_index; -} - -inline std::size_t model_group::get_index_count() const -{ - return index_count; -} - -/** - * - */ -class model -{ -public: - typedef geom::aabb aabb_type; - - model(); - ~model(); - - void set_bounds(const aabb_type& bounds); - - model_group* add_group(const std::string& name = std::string()); - - bool remove_group(const std::string& name); - bool remove_group(model_group* group); - - const aabb_type& get_bounds() const; - - const model_group* get_group(const std::string& name) const; - model_group* get_group(const std::string& name); - - const std::vector* get_groups() const; - - const gl::vertex_array* get_vertex_array() const; - gl::vertex_array* get_vertex_array(); - - const gl::vertex_buffer* get_vertex_buffer() const; - gl::vertex_buffer* get_vertex_buffer(); - - const skeleton& get_skeleton() const; - skeleton& get_skeleton(); - -private: - aabb_type bounds; - std::vector groups; - std::unordered_map group_map; - gl::vertex_array vao; - gl::vertex_buffer vbo; - ::skeleton skeleton; -}; - -inline void model::set_bounds(const aabb_type& bounds) -{ - this->bounds = bounds; -} - -inline const typename model::aabb_type& model::get_bounds() const -{ - return bounds; -} - -inline const std::vector* model::get_groups() const -{ - return &groups; -} - -inline const gl::vertex_array* model::get_vertex_array() const -{ - return &vao; -} - -inline gl::vertex_array* model::get_vertex_array() -{ - return &vao; -} - -inline const gl::vertex_buffer* model::get_vertex_buffer() const -{ - return &vbo; -} - -inline gl::vertex_buffer* model::get_vertex_buffer() -{ - return &vbo; -} - -inline const skeleton& model::get_skeleton() const -{ - return skeleton; -} - -inline skeleton& model::get_skeleton() -{ - return skeleton; -} - -} // namespace render - -#endif // ANTKEEPER_RENDER_MODEL_HPP diff --git a/src/render/operation.hpp b/src/render/operation.hpp deleted file mode 100644 index 230b2d0..0000000 --- a/src/render/operation.hpp +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_RENDER_OPERATION_HPP -#define ANTKEEPER_RENDER_OPERATION_HPP - -#include "utility/fundamental-types.hpp" -#include "animation/pose.hpp" -#include "gl/vertex-array.hpp" -#include "gl/drawing-mode.hpp" -#include - -namespace render { - -class material; - -/** - * Encapsulates an atomic render operation. - */ -struct operation -{ - const float4x4* skinning_palette; - std::size_t bone_count; - const material* material; - const gl::vertex_array* vertex_array; - gl::drawing_mode drawing_mode; - std::size_t start_index; - std::size_t index_count; - float4x4 transform; - float depth; - std::size_t instance_count; -}; - -} // namespace render - -#endif // ANTKEEPER_RENDER_OPERATION_HPP diff --git a/src/render/pass.cpp b/src/render/pass.cpp deleted file mode 100644 index f76b641..0000000 --- a/src/render/pass.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "render/pass.hpp" - -namespace render { - -pass::pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer): - rasterizer(rasterizer), - framebuffer(framebuffer), - enabled(true) -{} - -pass::~pass() -{} - -void pass::set_enabled(bool enabled) -{ - this->enabled = enabled; -} - -void pass::set_framebuffer(const gl::framebuffer* framebuffer) -{ - this->framebuffer = framebuffer; -} - -} // namespace render diff --git a/src/render/pass.hpp b/src/render/pass.hpp deleted file mode 100644 index 8c8e7d9..0000000 --- a/src/render/pass.hpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_RENDER_PASS_HPP -#define ANTKEEPER_RENDER_PASS_HPP - -#include "gl/rasterizer.hpp" -#include "gl/framebuffer.hpp" -#include "render/context.hpp" -#include "render/queue.hpp" - -namespace render { - -/** - * Render pass. - */ -class pass -{ -public: - pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer); - virtual ~pass(); - - virtual void render(const render::context& ctx, render::queue& queue) const = 0; - - void set_enabled(bool enabled); - bool is_enabled() const; - - void set_framebuffer(const gl::framebuffer* framebuffer); - -protected: - gl::rasterizer* rasterizer; - const gl::framebuffer* framebuffer; - -private: - bool enabled; -}; - -inline bool pass::is_enabled() const -{ - return enabled; -} - -} // namespace render - -#endif // ANTKEEPER_RENDER_PASS_HPP - diff --git a/src/render/passes/bloom-pass.cpp b/src/render/passes/bloom-pass.cpp deleted file mode 100644 index 2e99880..0000000 --- a/src/render/passes/bloom-pass.cpp +++ /dev/null @@ -1,303 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "render/passes/bloom-pass.hpp" -#include "resources/resource-manager.hpp" -#include "gl/rasterizer.hpp" -#include "gl/framebuffer.hpp" -#include "gl/shader-program.hpp" -#include "gl/shader-input.hpp" -#include "gl/vertex-buffer.hpp" -#include "gl/vertex-array.hpp" -#include "gl/vertex-attribute.hpp" -#include "gl/drawing-mode.hpp" -#include "gl/texture-2d.hpp" -#include "gl/texture-wrapping.hpp" -#include "gl/texture-filter.hpp" -#include "render/vertex-attribute.hpp" -#include "render/context.hpp" -#include -#include -#include - -namespace render { - -bloom_pass::bloom_pass(gl::rasterizer* rasterizer, resource_manager* resource_manager): - pass(rasterizer, nullptr), - source_texture(nullptr), - mip_chain_length(0), - filter_radius(0.005f), - corrected_filter_radius{filter_radius, filter_radius} -{ - // Load downsample shader template - downsample_shader_template = resource_manager->load("bloom-downsample.glsl"); - - // Build downsample shader program with Karis averaging - downsample_karis_shader = downsample_shader_template->build - ( - { - {"KARIS_AVERAGE", std::string()} - } - ); - downsample_karis_source_texture_input = downsample_karis_shader->get_input("source_texture"); - - // Build downsample shader program without Karis averaging - downsample_shader = downsample_shader_template->build(); - downsample_source_texture_input = downsample_shader->get_input("source_texture"); - - // Load upsample shader template - upsample_shader_template = resource_manager->load("bloom-upsample.glsl"); - - // Build upsample shader program - upsample_shader = upsample_shader_template->build(); - upsample_source_texture_input = upsample_shader->get_input("source_texture"); - upsample_filter_radius_input = upsample_shader->get_input("filter_radius"); - - const float vertex_data[] = - { - -1.0f, 1.0f, - -1.0f, -1.0f, - 1.0f, 1.0f, - 1.0f, 1.0f, - -1.0f, -1.0f, - 1.0f, -1.0f - }; - - std::size_t vertex_size = 2; - std::size_t vertex_stride = sizeof(float) * vertex_size; - std::size_t vertex_count = 6; - - quad_vbo = new gl::vertex_buffer(sizeof(float) * vertex_size * vertex_count, vertex_data); - quad_vao = new gl::vertex_array(); - - // Define position vertex attribute - gl::vertex_attribute position_attribute; - position_attribute.buffer = quad_vbo; - position_attribute.offset = 0; - position_attribute.stride = vertex_stride; - position_attribute.type = gl::vertex_attribute_type::float_32; - position_attribute.components = 2; - - // Bind vertex attributes to VAO - quad_vao->bind(render::vertex_attribute::position, position_attribute); -} - -bloom_pass::~bloom_pass() -{ - delete quad_vao; - delete quad_vbo; - - set_mip_chain_length(0); - - delete downsample_karis_shader; - delete downsample_shader; - delete upsample_shader; - - /// @TODO - //resource_manager->unload("bloom-downsample.glsl"); - //resource_manager->unload("bloom-upsample.glsl"); -} - -void bloom_pass::render(const render::context& ctx, render::queue& queue) const -{ - if (!source_texture || !mip_chain_length) - return; - - // Disable depth testing - glDisable(GL_DEPTH_TEST); - glDepthMask(GL_FALSE); - - // Enable back-face culling - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); - - // Disable blending - glDisable(GL_BLEND); - - // Downsample first mip with Karis average - { - rasterizer->use_program(*downsample_karis_shader); - downsample_karis_source_texture_input->upload(source_texture); - - rasterizer->use_framebuffer(*framebuffers[0]); - rasterizer->set_viewport(0, 0, textures[0]->get_width(), textures[0]->get_height()); - - rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6); - } - - // Downsample remaining mips - rasterizer->use_program(*downsample_shader); - for (int i = 1; i < static_cast(mip_chain_length); ++i) - { - rasterizer->use_framebuffer(*framebuffers[i]); - rasterizer->set_viewport(0, 0, textures[i]->get_width(), textures[i]->get_height()); - - // Use previous downsample texture as downsample source - downsample_source_texture_input->upload(textures[i - 1]); - - rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6); - } - - // Enable additive blending - glEnable(GL_BLEND); - glBlendFunc(GL_ONE, GL_ONE); - glBlendEquation(GL_FUNC_ADD); - - // Upsample - rasterizer->use_program(*upsample_shader); - upsample_filter_radius_input->upload(corrected_filter_radius); - for (int i = static_cast(mip_chain_length) - 1; i > 0; --i) - { - const int j = i - 1; - rasterizer->use_framebuffer(*framebuffers[j]); - rasterizer->set_viewport(0, 0, textures[j]->get_width(), textures[j]->get_height()); - - upsample_source_texture_input->upload(textures[i]); - - rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6); - } -} - -void bloom_pass::resize() -{ - unsigned int source_width = 1; - unsigned int source_height = 1; - if (source_texture) - { - // Get source texture dimensions - source_width = source_texture->get_width(); - source_height = source_texture->get_height(); - - // Correct filter radius according to source texture aspect ratio - const float aspect_ratio = static_cast(source_height) / static_cast(source_width); - corrected_filter_radius = {filter_radius * aspect_ratio, filter_radius}; - } - - // Resize mip chain - for (unsigned int i = 0; i < mip_chain_length; ++i) - { - // Calculate mip dimensions - unsigned int mip_width = std::max(1, source_width >> (i + 1)); - unsigned int mip_height = std::max(1, source_height >> (i + 1)); - - // Resize mip texture - textures[i]->resize(mip_width, mip_height, nullptr); - - // Resize mip framebuffer - framebuffers[i]->resize({(int)mip_width, (int)mip_height}); - } -} - -void bloom_pass::set_source_texture(const gl::texture_2d* texture) -{ - if (texture != source_texture) - { - if (texture) - { - if (source_texture) - { - if (texture->get_width() != source_texture->get_width() || texture->get_height() != source_texture->get_height()) - { - source_texture = texture; - resize(); - } - else - { - source_texture = texture; - } - } - else - { - source_texture = texture; - resize(); - } - } - else - { - source_texture = texture; - } - } -} - -void bloom_pass::set_mip_chain_length(unsigned int length) -{ - unsigned int source_width = 1; - unsigned int source_height = 1; - if (source_texture) - { - // Get source texture dimensions - source_width = source_texture->get_width(); - source_height = source_texture->get_height(); - } - - if (length > mip_chain_length) - { - // Generate additional framebuffers - for (unsigned int i = mip_chain_length; i < length; ++i) - { - // Calculate mip resolution - unsigned int mip_width = std::max(1, source_width >> (i + 1)); - unsigned int mip_height = std::max(1, source_height >> (i + 1)); - - // Generate mip texture - gl::texture_2d* texture = new gl::texture_2d(mip_width, mip_height, gl::pixel_type::float_16, gl::pixel_format::rgb); - texture->set_wrapping(gl::texture_wrapping::extend, gl::texture_wrapping::extend); - texture->set_filters(gl::texture_min_filter::linear, gl::texture_mag_filter::linear); - texture->set_max_anisotropy(0.0f); - textures.push_back(texture); - - // Generate mip framebuffer - gl::framebuffer* framebuffer = new gl::framebuffer(mip_width, mip_height); - framebuffer->attach(gl::framebuffer_attachment_type::color, texture); - framebuffers.push_back(framebuffer); - } - } - else if (length < mip_chain_length) - { - // Free excess framebuffers - while (framebuffers.size() > length) - { - delete framebuffers.back(); - framebuffers.pop_back(); - - delete textures.back(); - textures.pop_back(); - } - } - - // Update mip chain length - mip_chain_length = length; -} - -void bloom_pass::set_filter_radius(float radius) -{ - filter_radius = radius; - - // Get aspect ratio of source texture - float aspect_ratio = 1.0f; - if (source_texture) - { - aspect_ratio = static_cast(source_texture->get_height()) / static_cast(source_texture->get_width()); - } - - // Correct filter radius according to source texture aspect ratio - corrected_filter_radius = {filter_radius * aspect_ratio, filter_radius}; -} - -} // namespace render diff --git a/src/render/passes/bloom-pass.hpp b/src/render/passes/bloom-pass.hpp deleted file mode 100644 index 49644e5..0000000 --- a/src/render/passes/bloom-pass.hpp +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_RENDER_BLOOM_PASS_HPP -#define ANTKEEPER_RENDER_BLOOM_PASS_HPP - -#include "render/pass.hpp" -#include "render/shader-template.hpp" -#include "gl/shader-program.hpp" -#include "gl/shader-input.hpp" -#include "gl/vertex-buffer.hpp" -#include "gl/vertex-array.hpp" -#include "gl/texture-2d.hpp" - -class resource_manager; - -namespace render { - -/** - * Physically-based bloom render pass. - * - * @see Jimenez, J. (2014). Next generation post processing in call of duty advanced warfare. SIGGRAPH Advances in Real-Time Rendering. - * @see https://learnopengl.com/Guest-Articles/2022/Phys.-Based-Bloom - */ -class bloom_pass: public pass -{ -public: - /** - * Constructs a bloom pass. - * - * @param rasterizer Rasterizer. - * @param resource_manager Resource manager. - */ - bloom_pass(gl::rasterizer* rasterizer, resource_manager* resource_manager); - - /** - * Destructs a bloom pass. - */ - virtual ~bloom_pass(); - - /** - * Renders a bloom texture. - * - * @param ctx Render context. - * @param queue Render queue. - */ - virtual void render(const render::context& ctx, render::queue& queue) const final; - - /** - * Resizes the mip chain resolution according to the resolution of the source texture. - */ - void resize(); - - /** - * Sets the bloom source texture. - * - * @param texture Bloom source texture. - */ - void set_source_texture(const gl::texture_2d* texture); - - /** - * Sets the mip chain length. A length of `1` indicates a single stage bloom. - * - * @param length Mip chain length. - */ - void set_mip_chain_length(unsigned int length); - - /** - * Sets the upsample filter radius. - * - * @param radius Upsample filter radius, in texture coordinates. - */ - void set_filter_radius(float radius); - - /** - * Returns the texture containing the bloom result. - */ - const gl::texture_2d* get_bloom_texture() const; - -private: - const gl::texture_2d* source_texture; - - shader_template* downsample_shader_template; - shader_template* upsample_shader_template; - - gl::shader_program* downsample_karis_shader; - const gl::shader_input* downsample_karis_source_texture_input; - - gl::shader_program* downsample_shader; - const gl::shader_input* downsample_source_texture_input; - - gl::shader_program* upsample_shader; - const gl::shader_input* upsample_source_texture_input; - const gl::shader_input* upsample_filter_radius_input; - - gl::vertex_buffer* quad_vbo; - gl::vertex_array* quad_vao; - - unsigned int mip_chain_length; - std::vector framebuffers; - std::vector textures; - float filter_radius; - float2 corrected_filter_radius; -}; - -inline const gl::texture_2d* bloom_pass::get_bloom_texture() const -{ - return textures.empty() ? nullptr : textures.front(); -} - -} // namespace render - -#endif // ANTKEEPER_RENDER_BLOOM_PASS_HPP diff --git a/src/render/passes/clear-pass.cpp b/src/render/passes/clear-pass.cpp deleted file mode 100644 index 9f27e15..0000000 --- a/src/render/passes/clear-pass.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "render/passes/clear-pass.hpp" -#include "gl/rasterizer.hpp" -#include "gl/framebuffer.hpp" -#include - -namespace render { - -clear_pass::clear_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer): - pass(rasterizer, framebuffer), - clear_color_buffer(false), - clear_depth_buffer(false), - clear_stencil_buffer(false), - clear_color({0.0f, 0.0f, 0.0f, 0.0f}), - clear_depth(1.0f), - clear_stencil(0) -{} - -clear_pass::~clear_pass() -{} - -void clear_pass::render(const render::context& ctx, render::queue& queue) const -{ - if (clear_color_buffer) - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - if (clear_depth_buffer) - glDepthMask(GL_TRUE); - if (clear_stencil_buffer) - glStencilMask(0xFF); - - rasterizer->use_framebuffer(*framebuffer); - - auto viewport = framebuffer->get_dimensions(); - rasterizer->set_viewport(0, 0, std::get<0>(viewport), std::get<1>(viewport)); - - rasterizer->set_clear_color(clear_color[0], clear_color[1], clear_color[2], clear_color[3]); - rasterizer->set_clear_depth(clear_depth); - rasterizer->set_clear_stencil(clear_stencil); - rasterizer->clear_framebuffer(clear_color_buffer, clear_depth_buffer, clear_stencil_buffer); -} - -void clear_pass::set_cleared_buffers(bool color, bool depth, bool stencil) -{ - clear_color_buffer = color; - clear_depth_buffer = depth; - clear_stencil_buffer = stencil; -} - -void clear_pass::set_clear_color(const float4& color) -{ - clear_color = color; -} - -void clear_pass::set_clear_depth(float depth) -{ - clear_depth = depth; -} - -void clear_pass::set_clear_stencil(int stencil) -{ - clear_stencil = stencil; -} - -} // namespace render diff --git a/src/render/passes/clear-pass.hpp b/src/render/passes/clear-pass.hpp deleted file mode 100644 index 058d997..0000000 --- a/src/render/passes/clear-pass.hpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_RENDER_CLEAR_PASS_HPP -#define ANTKEEPER_RENDER_CLEAR_PASS_HPP - -#include "render/pass.hpp" -#include "utility/fundamental-types.hpp" - -namespace render { - -/** - * Clears the color, depth, or stencil buffer of a render target. - */ -class clear_pass: public pass -{ -public: - clear_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer); - virtual ~clear_pass(); - virtual void render(const render::context& ctx, render::queue& queue) const final; - - /** - * Sets the buffers to be cleared. - * - * @param color Clear the color buffer. - * @param depth Clear the depth buffer. - * @param stencil Clear the stencil buffer. - */ - void set_cleared_buffers(bool color, bool depth, bool stencil); - - /** - * Sets color buffer clear color. - * - * @param color Clear color. - */ - void set_clear_color(const float4& color); - - /** - * Sets the depth buffer clear value. - * - * @param depth Clear value. - */ - void set_clear_depth(float depth); - - /** - * Sets the stencil buffer clear value. - * - * @param stencil Clear value. - */ - void set_clear_stencil(int stencil); - -private: - bool clear_color_buffer; - bool clear_depth_buffer; - bool clear_stencil_buffer; - float4 clear_color; - float clear_depth; - int clear_stencil; -}; - -} // namespace render - -#endif // ANTKEEPER_RENDER_CLEAR_PASS_HPP - diff --git a/src/render/passes/final-pass.cpp b/src/render/passes/final-pass.cpp deleted file mode 100644 index 435fc68..0000000 --- a/src/render/passes/final-pass.cpp +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "render/passes/final-pass.hpp" -#include "resources/resource-manager.hpp" -#include "gl/rasterizer.hpp" -#include "gl/framebuffer.hpp" -#include "gl/shader-program.hpp" -#include "gl/shader-input.hpp" -#include "gl/vertex-buffer.hpp" -#include "gl/vertex-array.hpp" -#include "gl/vertex-attribute.hpp" -#include "gl/drawing-mode.hpp" -#include "gl/texture-2d.hpp" -#include "gl/texture-wrapping.hpp" -#include "gl/texture-filter.hpp" -#include "render/vertex-attribute.hpp" -#include "render/context.hpp" -#include -#include - -namespace render { - -final_pass::final_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager): - pass(rasterizer, framebuffer), - color_texture(nullptr), - bloom_texture(nullptr), - bloom_weight(0.04f), - blue_noise_texture(nullptr), - blue_noise_scale(1.0f) -{ - // Load shader template - shader_template = resource_manager->load("final.glsl"); - - // Build shader program - shader_program = shader_template->build(); - color_texture_input = shader_program->get_input("color_texture"); - bloom_texture_input = shader_program->get_input("bloom_texture"); - bloom_weight_input = shader_program->get_input("bloom_weight"); - blue_noise_texture_input = shader_program->get_input("blue_noise_texture"); - blue_noise_scale_input = shader_program->get_input("blue_noise_scale"); - resolution_input = shader_program->get_input("resolution"); - time_input = shader_program->get_input("time"); - - const float vertex_data[] = - { - -1.0f, 1.0f, - -1.0f, -1.0f, - 1.0f, 1.0f, - 1.0f, 1.0f, - -1.0f, -1.0f, - 1.0f, -1.0f - }; - - std::size_t vertex_size = 2; - std::size_t vertex_stride = sizeof(float) * vertex_size; - std::size_t vertex_count = 6; - - quad_vbo = new gl::vertex_buffer(sizeof(float) * vertex_size * vertex_count, vertex_data); - quad_vao = new gl::vertex_array(); - - // Define position vertex attribute - gl::vertex_attribute position_attribute; - position_attribute.buffer = quad_vbo; - position_attribute.offset = 0; - position_attribute.stride = vertex_stride; - position_attribute.type = gl::vertex_attribute_type::float_32; - position_attribute.components = 2; - - // Bind vertex attributes to VAO - quad_vao->bind(render::vertex_attribute::position, position_attribute); -} - -final_pass::~final_pass() -{ - delete quad_vao; - delete quad_vbo; - - delete shader_program; - - /// @TODO - // resource_manager->unload("final.glsl"); -} - -void final_pass::render(const render::context& ctx, render::queue& queue) const -{ - rasterizer->use_framebuffer(*framebuffer); - - glDisable(GL_BLEND); - glDisable(GL_DEPTH_TEST); - glDepthMask(GL_FALSE); - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); - - auto viewport = framebuffer->get_dimensions(); - rasterizer->set_viewport(0, 0, std::get<0>(viewport), std::get<1>(viewport)); - - float2 resolution = {std::get<0>(viewport), std::get<1>(viewport)}; - - // Change shader program - rasterizer->use_program(*shader_program); - - // Upload shader parameters - color_texture_input->upload(color_texture); - if (bloom_texture && bloom_texture_input) - bloom_texture_input->upload(bloom_texture); - if (bloom_weight_input) - bloom_weight_input->upload(bloom_weight); - if (blue_noise_texture && blue_noise_texture_input) - blue_noise_texture_input->upload(blue_noise_texture); - if (blue_noise_scale_input) - blue_noise_scale_input->upload(blue_noise_scale); - if (resolution_input) - resolution_input->upload(resolution); - if (time_input) - time_input->upload(ctx.t); - - // Draw quad - rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6); -} - -void final_pass::set_color_texture(const gl::texture_2d* texture) -{ - this->color_texture = texture; -} - -void final_pass::set_bloom_texture(const gl::texture_2d* texture) noexcept -{ - this->bloom_texture = texture; -} - -void final_pass::set_bloom_weight(float weight) noexcept -{ - this->bloom_weight = weight; -} - -void final_pass::set_blue_noise_texture(const gl::texture_2d* texture) -{ - this->blue_noise_texture = texture; - blue_noise_scale = 1.0f / static_cast(texture->get_dimensions()[0]); -} - -} // namespace render diff --git a/src/render/passes/final-pass.hpp b/src/render/passes/final-pass.hpp deleted file mode 100644 index 04d284e..0000000 --- a/src/render/passes/final-pass.hpp +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_RENDER_FINAL_PASS_HPP -#define ANTKEEPER_RENDER_FINAL_PASS_HPP - -#include "render/pass.hpp" -#include "render/shader-template.hpp" -#include "gl/shader-program.hpp" -#include "gl/shader-input.hpp" -#include "gl/vertex-buffer.hpp" -#include "gl/vertex-array.hpp" -#include "gl/texture-2d.hpp" - -class resource_manager; - -namespace render { - -/** - * - */ -class final_pass: public pass -{ -public: - final_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager); - virtual ~final_pass(); - virtual void render(const render::context& ctx, render::queue& queue) const final; - - void set_color_texture(const gl::texture_2d* texture); - void set_bloom_texture(const gl::texture_2d* texture) noexcept; - void set_bloom_weight(float weight) noexcept; - void set_blue_noise_texture(const gl::texture_2d* texture); - -private: - render::shader_template* shader_template; - - gl::shader_program* shader_program; - const gl::shader_input* color_texture_input; - const gl::shader_input* bloom_texture_input; - const gl::shader_input* bloom_weight_input; - const gl::shader_input* blue_noise_texture_input; - const gl::shader_input* blue_noise_scale_input; - const gl::shader_input* resolution_input; - const gl::shader_input* time_input; - gl::vertex_buffer* quad_vbo; - gl::vertex_array* quad_vao; - - const gl::texture_2d* color_texture; - const gl::texture_2d* bloom_texture; - float bloom_weight; - const gl::texture_2d* blue_noise_texture; - float blue_noise_scale; -}; - -} // namespace render - -#endif // ANTKEEPER_RENDER_FINAL_PASS_HPP - diff --git a/src/render/passes/fxaa-pass.cpp b/src/render/passes/fxaa-pass.cpp deleted file mode 100644 index d715984..0000000 --- a/src/render/passes/fxaa-pass.cpp +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "render/passes/fxaa-pass.hpp" -#include "resources/resource-manager.hpp" -#include "gl/rasterizer.hpp" -#include "gl/framebuffer.hpp" -#include "gl/shader-program.hpp" -#include "gl/shader-input.hpp" -#include "gl/vertex-buffer.hpp" -#include "gl/vertex-array.hpp" -#include "gl/vertex-attribute.hpp" -#include "gl/drawing-mode.hpp" -#include "gl/texture-2d.hpp" -#include "render/vertex-attribute.hpp" -#include "render/context.hpp" -#include "debug/log.hpp" -#include - -namespace render { - -fxaa_pass::fxaa_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager): - pass(rasterizer, framebuffer), - source_texture(nullptr) -{ - // Load FXAA shader template - shader_template = resource_manager->load("fxaa.glsl"); - - // Build FXAA shader program - shader = shader_template->build(); - source_texture_input = shader->get_input("source_texture"); - texel_size_input = shader->get_input("texel_size"); - - const float vertex_data[] = - { - -1.0f, 1.0f, - -1.0f, -1.0f, - 1.0f, 1.0f, - 1.0f, 1.0f, - -1.0f, -1.0f, - 1.0f, -1.0f - }; - - std::size_t vertex_size = 2; - std::size_t vertex_stride = sizeof(float) * vertex_size; - std::size_t vertex_count = 6; - - quad_vbo = new gl::vertex_buffer(sizeof(float) * vertex_size * vertex_count, vertex_data); - quad_vao = new gl::vertex_array(); - - // Define position vertex attribute - gl::vertex_attribute position_attribute; - position_attribute.buffer = quad_vbo; - position_attribute.offset = 0; - position_attribute.stride = vertex_stride; - position_attribute.type = gl::vertex_attribute_type::float_32; - position_attribute.components = 2; - - // Bind vertex attributes to VAO - quad_vao->bind(render::vertex_attribute::position, position_attribute); -} - -fxaa_pass::~fxaa_pass() -{ - delete quad_vao; - delete quad_vbo; - - delete shader; - - /// @TODO - // resource_manager->unload("fxaa.glsl"); -} - -void fxaa_pass::render(const render::context& ctx, render::queue& queue) const -{ - if (!source_texture) - return; - - // Set rasterizer state - glDisable(GL_DEPTH_TEST); - glDepthMask(GL_FALSE); - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); - glDisable(GL_BLEND); - - // Render FXAA - rasterizer->use_framebuffer(*framebuffer); - rasterizer->set_viewport(0, 0, framebuffer->get_dimensions()[0], framebuffer->get_dimensions()[1]); - rasterizer->use_program(*shader); - source_texture_input->upload(source_texture); - - if (texel_size_input) - { - const float2 texel_size = 1.0f / float2{static_cast(source_texture->get_width()), static_cast(source_texture->get_height())}; - texel_size_input->upload(texel_size); - } - - rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6); -} - -void fxaa_pass::set_source_texture(const gl::texture_2d* texture) -{ - source_texture = texture; -} - -} // namespace render diff --git a/src/render/passes/fxaa-pass.hpp b/src/render/passes/fxaa-pass.hpp deleted file mode 100644 index 96f0982..0000000 --- a/src/render/passes/fxaa-pass.hpp +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_RENDER_FXAA_PASS_HPP -#define ANTKEEPER_RENDER_FXAA_PASS_HPP - -#include "render/pass.hpp" -#include "render/shader-template.hpp" -#include "gl/shader-program.hpp" -#include "gl/shader-input.hpp" -#include "gl/vertex-buffer.hpp" -#include "gl/vertex-array.hpp" -#include "gl/texture-2d.hpp" - -class resource_manager; - -namespace render { - -/** - * FXAA render pass. - * - * @see Lottes, T. (2009). Fxaa. White paper, Nvidia, Febuary, 2. - */ -class fxaa_pass: public pass -{ -public: - /** - * Constructs an FXAA pass. - * - * @param rasterizer Rasterizer. - * @param framebuffer Target framebuffer. - * @param resource_manager Resource manager. - */ - fxaa_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager); - - /** - * Destructs an FXAA pass. - */ - virtual ~fxaa_pass(); - - /** - * Renders FXAA. - * - * @param ctx Render context. - * @param queue Render queue. - */ - virtual void render(const render::context& ctx, render::queue& queue) const final; - - /** - * Sets the FXAA source texture. - * - * @param texture FXAA source texture. - */ - void set_source_texture(const gl::texture_2d* texture); - -private: - const gl::texture_2d* source_texture; - - render::shader_template* shader_template; - gl::shader_program* shader; - const gl::shader_input* source_texture_input; - const gl::shader_input* texel_size_input; - - gl::vertex_buffer* quad_vbo; - gl::vertex_array* quad_vao; -}; - -} // namespace render - -#endif // ANTKEEPER_RENDER_FXAA_PASS_HPP diff --git a/src/render/passes/ground-pass.cpp b/src/render/passes/ground-pass.cpp deleted file mode 100644 index c874a67..0000000 --- a/src/render/passes/ground-pass.cpp +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "render/passes/ground-pass.hpp" -#include "resources/resource-manager.hpp" -#include "gl/rasterizer.hpp" -#include "gl/framebuffer.hpp" -#include "gl/shader-program.hpp" -#include "gl/shader-input.hpp" -#include "gl/vertex-buffer.hpp" -#include "gl/vertex-array.hpp" -#include "gl/vertex-attribute.hpp" -#include "gl/drawing-mode.hpp" -#include "gl/texture-2d.hpp" -#include "gl/texture-wrapping.hpp" -#include "gl/texture-filter.hpp" -#include "render/vertex-attribute.hpp" -#include "render/context.hpp" -#include "render/model.hpp" -#include "render/material.hpp" -#include "scene/camera.hpp" -#include "scene/collection.hpp" -#include "scene/directional-light.hpp" -#include "scene/ambient-light.hpp" -#include "utility/fundamental-types.hpp" -#include "color/color.hpp" -#include "math/interpolation.hpp" -#include -#include -#include - -namespace render { - -ground_pass::ground_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager): - pass(rasterizer, framebuffer), - ground_model(nullptr), - ground_model_vao(nullptr), - ground_material(nullptr), - shader_program(nullptr) -{} - -ground_pass::~ground_pass() -{} - -void ground_pass::render(const render::context& ctx, render::queue& queue) const -{ - if (!ground_model) - return; - - rasterizer->use_framebuffer(*framebuffer); - - glDisable(GL_BLEND); - glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_ALWAYS); - glDepthMask(GL_TRUE); - glDepthRange(-1.0f, 1.0f); - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); - glDisable(GL_STENCIL_TEST); - glStencilMask(0); - - auto viewport = framebuffer->get_dimensions(); - rasterizer->set_viewport(0, 0, std::get<0>(viewport), std::get<1>(viewport)); - - float2 resolution = {static_cast(std::get<0>(viewport)), static_cast(std::get<1>(viewport))}; - - const scene::camera& camera = *ctx.camera; - float clip_near = camera.get_clip_near_tween().interpolate(ctx.alpha); - float clip_far = camera.get_clip_far_tween().interpolate(ctx.alpha); - float3 model_scale = float3{1.0f, 1.0f, 1.0f} * (clip_near + clip_far) * 0.5f; - float4x4 model = math::scale(math::matrix4::identity(), model_scale); - float4x4 view = float4x4(float3x3(ctx.view)); - float4x4 model_view = view * model; - const float4x4& projection = ctx.projection; - const float4x4& view_projection = ctx.view_projection; - float4x4 model_view_projection = projection * model_view; - - - - float3 ambient_light_color = {0.0f, 0.0f, 0.0f}; - float3 directional_light_color = {0.0f, 0.0f, 0.0f}; - float3 directional_light_direction = {0.0f, 0.0f, 0.0f}; - - // Collect lights - const std::list* lights = ctx.collection->get_objects(scene::light::object_type_id); - for (const scene::object_base* object: *lights) - { - // Skip inactive lights - if (!object->is_active()) - continue; - - const scene::light* light = static_cast(object); - switch (light->get_light_type()) - { - // Add ambient light - case scene::light_type::ambient: - { - // Pre-expose light - ambient_light_color = light->get_scaled_color_tween().interpolate(ctx.alpha) * ctx.exposure; - break; - } - - // Add directional light - case scene::light_type::directional: - { - const scene::directional_light* directional_light = static_cast(light); - - // Pre-expose light - float3 light_color = light->get_scaled_color_tween().interpolate(ctx.alpha) * ctx.exposure; - if (light_color.x() < directional_light_color.x()) - break; - - directional_light_color = light_color; - - directional_light_direction = static_cast(light)->get_direction_tween().interpolate(ctx.alpha); - break; - } - - default: - break; - } - } - - // Draw ground - rasterizer->use_program(*shader_program); - - if (model_view_projection_input) - model_view_projection_input->upload(model_view_projection); - if (view_projection_input) - view_projection_input->upload(view_projection); - if (camera_position_input) - camera_position_input->upload(ctx.camera_transform.translation); - if (directional_light_colors_input) - directional_light_colors_input->upload(0, &directional_light_color, 1); - if (directional_light_directions_input) - directional_light_directions_input->upload(0, &directional_light_direction, 1); - if (ambient_light_colors_input) - ambient_light_colors_input->upload(0, &ambient_light_color, 1); - - ground_material->upload(ctx.alpha); - - - - rasterizer->draw_arrays(*ground_model_vao, ground_model_drawing_mode, ground_model_start_index, ground_model_index_count); -} - -void ground_pass::set_ground_model(const model* model) -{ - ground_model = model; - - if (ground_model) - { - ground_model_vao = model->get_vertex_array(); - - const std::vector& groups = *model->get_groups(); - for (model_group* group: groups) - { - ground_material = group->get_material(); - ground_model_drawing_mode = group->get_drawing_mode(); - ground_model_start_index = group->get_start_index(); - ground_model_index_count = group->get_index_count(); - } - - if (ground_material) - { - shader_program = ground_material->get_shader_program(); - - if (shader_program) - { - model_view_projection_input = shader_program->get_input("model_view_projection"); - view_projection_input = shader_program->get_input("view_projection"); - camera_position_input = shader_program->get_input("camera.position"); - directional_light_colors_input = shader_program->get_input("directional_light_colors"); - directional_light_directions_input = shader_program->get_input("directional_light_directions"); - ambient_light_colors_input = shader_program->get_input("ambient_light_colors"); - } - } - } - else - { - ground_model_vao = nullptr; - } -} - -} // namespace render diff --git a/src/render/passes/ground-pass.hpp b/src/render/passes/ground-pass.hpp deleted file mode 100644 index 5876d4f..0000000 --- a/src/render/passes/ground-pass.hpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_RENDER_GROUND_PASS_HPP -#define ANTKEEPER_RENDER_GROUND_PASS_HPP - -#include "render/pass.hpp" -#include "utility/fundamental-types.hpp" -#include "animation/tween.hpp" -#include "gl/shader-program.hpp" -#include "gl/shader-input.hpp" -#include "gl/vertex-buffer.hpp" -#include "gl/vertex-array.hpp" -#include "gl/drawing-mode.hpp" - -class resource_manager; - -namespace render { - -class material; -class model; - -/** - * - */ -class ground_pass: public pass -{ -public: - ground_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager); - virtual ~ground_pass(); - virtual void render(const render::context& ctx, render::queue& queue) const final; - - void set_ground_model(const model* model); - -private: - gl::shader_program* shader_program; - const gl::shader_input* model_view_projection_input; - const gl::shader_input* view_projection_input; - const gl::shader_input* camera_position_input; - const gl::shader_input* directional_light_colors_input; - const gl::shader_input* directional_light_directions_input; - const gl::shader_input* ambient_light_colors_input; - - const model* ground_model; - const material* ground_material; - const gl::vertex_array* ground_model_vao; - gl::drawing_mode ground_model_drawing_mode; - std::size_t ground_model_start_index; - std::size_t ground_model_index_count; -}; - -} // namespace render - -#endif // ANTKEEPER_RENDER_GROUND_PASS_HPP diff --git a/src/render/passes/material-pass.cpp b/src/render/passes/material-pass.cpp deleted file mode 100644 index e68e29f..0000000 --- a/src/render/passes/material-pass.cpp +++ /dev/null @@ -1,732 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "render/passes/material-pass.hpp" -#include "config.hpp" -#include "resources/resource-manager.hpp" -#include "gl/rasterizer.hpp" -#include "gl/framebuffer.hpp" -#include "gl/shader-program.hpp" -#include "gl/shader-input.hpp" -#include "gl/vertex-buffer.hpp" -#include "gl/vertex-array.hpp" -#include "gl/vertex-attribute.hpp" -#include "gl/drawing-mode.hpp" -#include "gl/texture-2d.hpp" -#include "gl/texture-wrapping.hpp" -#include "gl/texture-filter.hpp" -#include "render/vertex-attribute.hpp" -#include "render/material-flags.hpp" -#include "render/model.hpp" -#include "render/context.hpp" -#include "scene/camera.hpp" -#include "scene/collection.hpp" -#include "scene/ambient-light.hpp" -#include "scene/directional-light.hpp" -#include "scene/point-light.hpp" -#include "scene/spot-light.hpp" -#include "config.hpp" -#include "math/quaternion.hpp" -#include "math/projection.hpp" -#include -#include - -namespace render { - -static bool operation_compare(const render::operation& a, const render::operation& b); - -material_pass::material_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager): - pass(rasterizer, framebuffer), - fallback_material(nullptr), - mouse_position({0.0f, 0.0f}) -{ - max_ambient_light_count = MATERIAL_PASS_MAX_AMBIENT_LIGHT_COUNT; - max_point_light_count = MATERIAL_PASS_MAX_POINT_LIGHT_COUNT; - max_directional_light_count = MATERIAL_PASS_MAX_DIRECTIONAL_LIGHT_COUNT; - max_spot_light_count = MATERIAL_PASS_MAX_SPOTLIGHT_COUNT; - - ambient_light_colors = new float3[max_ambient_light_count]; - point_light_colors = new float3[max_point_light_count]; - point_light_positions = new float3[max_point_light_count]; - point_light_attenuations = new float3[max_point_light_count]; - directional_light_colors = new float3[max_directional_light_count]; - directional_light_directions = new float3[max_directional_light_count]; - directional_light_textures = new const gl::texture_2d*[max_directional_light_count]; - directional_light_texture_matrices = new float4x4[max_directional_light_count]; - directional_light_texture_opacities = new float[max_directional_light_count]; - - spot_light_colors = new float3[max_spot_light_count]; - spot_light_positions = new float3[max_spot_light_count]; - spot_light_directions = new float3[max_spot_light_count]; - spot_light_attenuations = new float3[max_spot_light_count]; - spot_light_cutoffs = new float2[max_spot_light_count]; -} - -material_pass::~material_pass() -{ - delete[] ambient_light_colors; - delete[] point_light_colors; - delete[] point_light_positions; - delete[] point_light_attenuations; - delete[] directional_light_colors; - delete[] directional_light_directions; - delete[] directional_light_textures; - delete[] directional_light_texture_matrices; - delete[] directional_light_texture_opacities; - delete[] spot_light_colors; - delete[] spot_light_positions; - delete[] spot_light_directions; - delete[] spot_light_attenuations; - delete[] spot_light_cutoffs; -} - -void material_pass::render(const render::context& ctx, render::queue& queue) const -{ - rasterizer->use_framebuffer(*framebuffer); - - glDisable(GL_BLEND); - glEnable(GL_DEPTH_TEST); - glDepthMask(GL_TRUE); - glDepthFunc(GL_GREATER); - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); - glDisable(GL_STENCIL_TEST); - glStencilMask(0x00); - - // For half-z buffer - glDepthRange(-1.0f, 1.0f); - - auto viewport = framebuffer->get_dimensions(); - rasterizer->set_viewport(0, 0, std::get<0>(viewport), std::get<1>(viewport)); - - float2 resolution = {static_cast(std::get<0>(viewport)), static_cast(std::get<1>(viewport))}; - - const float3& camera_position = ctx.camera_transform.translation; - const float4x4& view = ctx.view; - const float4x4& projection = ctx.projection; - const float4x4& view_projection = ctx.view_projection; - float4x4 model_view_projection; - float4x4 model; - float4x4 model_view; - float3x3 normal_model; - float3x3 normal_model_view; - float2 clip_depth; - clip_depth[0] = ctx.camera->get_clip_near_tween().interpolate(ctx.alpha); - clip_depth[1] = ctx.camera->get_clip_far_tween().interpolate(ctx.alpha); - float log_depth_coef = 2.0f / std::log2(clip_depth[1] + 1.0f); - - int active_material_flags = 0; - const gl::shader_program* active_shader_program = nullptr; - const render::material* active_material = nullptr; - const parameter_set* parameters = nullptr; - blend_mode active_blend_mode = blend_mode::opaque; - bool active_two_sided = false; - - // Reset light counts - ambient_light_count = 0; - point_light_count = 0; - directional_light_count = 0; - spot_light_count = 0; - const gl::texture_2d* shadow_map_texture = nullptr; - unsigned int shadow_cascade_count = 0; - const float* shadow_splits_directional = nullptr; - const float4x4* shadow_matrices_directional = nullptr; - float shadow_bias_directional = 0.0f; - - // Collect lights - const std::list* lights = ctx.collection->get_objects(scene::light::object_type_id); - for (const scene::object_base* object: *lights) - { - // Skip inactive lights - if (!object->is_active()) - continue; - - const scene::light* light = static_cast(object); - switch (light->get_light_type()) - { - // Add ambient light - case scene::light_type::ambient: - { - if (ambient_light_count < max_ambient_light_count) - { - // Pre-expose light - ambient_light_colors[ambient_light_count] = light->get_scaled_color_tween().interpolate(ctx.alpha) * ctx.exposure; - ++ambient_light_count; - } - break; - } - - // Add point light - case scene::light_type::point: - { - if (point_light_count < max_point_light_count) - { - // Pre-expose light - point_light_colors[point_light_count] = light->get_scaled_color_tween().interpolate(ctx.alpha) * ctx.exposure; - - float3 position = light->get_transform_tween().interpolate(ctx.alpha).translation; - point_light_positions[point_light_count] = position; - - point_light_attenuations[point_light_count] = static_cast(light)->get_attenuation_tween().interpolate(ctx.alpha); - ++point_light_count; - } - break; - } - - // Add directional light - case scene::light_type::directional: - { - if (directional_light_count < max_directional_light_count) - { - const scene::directional_light* directional_light = static_cast(light); - - // Pre-expose light - directional_light_colors[directional_light_count] = light->get_scaled_color_tween().interpolate(ctx.alpha) * ctx.exposure; - - float3 direction = static_cast(light)->get_direction_tween().interpolate(ctx.alpha); - directional_light_directions[directional_light_count] = direction; - - if (directional_light->is_shadow_caster()) - { - if (directional_light->get_shadow_framebuffer()) - shadow_map_texture = directional_light->get_shadow_framebuffer()->get_depth_attachment(); - shadow_bias_directional = directional_light->get_shadow_bias(); - shadow_cascade_count = directional_light->get_shadow_cascade_count(); - shadow_splits_directional = directional_light->get_shadow_cascade_distances(); - shadow_matrices_directional = directional_light->get_shadow_cascade_matrices(); - } - - if (directional_light->get_light_texture()) - { - directional_light_textures[directional_light_count] = directional_light->get_light_texture(); - directional_light_texture_opacities[directional_light_count] = directional_light->get_light_texture_opacity_tween().interpolate(ctx.alpha); - - math::transform light_transform = light->get_transform_tween().interpolate(ctx.alpha); - float3 forward = light_transform.rotation * config::global_forward; - float3 up = light_transform.rotation * config::global_up; - float4x4 light_view = math::look_at(light_transform.translation, light_transform.translation + forward, up); - - float2 scale = directional_light->get_light_texture_scale_tween().interpolate(ctx.alpha); - float4x4 light_projection = math::ortho(-scale.x(), scale.x(), -scale.y(), scale.y(), -1.0f, 1.0f); - - directional_light_texture_matrices[directional_light_count] = light_projection * light_view; - } - else - { - directional_light_textures[directional_light_count] = nullptr; - directional_light_texture_opacities[directional_light_count] = 0.0f; - } - - ++directional_light_count; - } - break; - } - - // Add spot_light - case scene::light_type::spot: - { - if (spot_light_count < max_spot_light_count) - { - const scene::spot_light* spot_light = static_cast(light); - - // Pre-expose light - spot_light_colors[spot_light_count] = light->get_scaled_color_tween().interpolate(ctx.alpha) * ctx.exposure; - - float3 position = light->get_transform_tween().interpolate(ctx.alpha).translation; - spot_light_positions[spot_light_count] = position; - - float3 direction = spot_light->get_direction_tween().interpolate(ctx.alpha); - spot_light_directions[spot_light_count] = direction; - - spot_light_attenuations[spot_light_count] = spot_light->get_attenuation_tween().interpolate(ctx.alpha); - spot_light_cutoffs[spot_light_count] = spot_light->get_cosine_cutoff_tween().interpolate(ctx.alpha); - - ++spot_light_count; - } - break; - } - - default: - break; - } - } - - // Sort render queue - queue.sort(operation_compare); - - for (const render::operation& operation: queue) - { - // Get operation material - const render::material* material = operation.material; - if (!material) - { - if (fallback_material) - { - // No material specified, use fallback material - material = fallback_material; - } - else - { - // No material specified and no fallback material, skip operation - continue; - } - } - - // Switch materials if necessary - if (active_material != material) - { - active_material = material; - - // Set blend mode - const blend_mode material_blend_mode = active_material->get_blend_mode(); - if (material_blend_mode != active_blend_mode) - { - if (material_blend_mode == blend_mode::translucent) - { - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - } - else if (active_blend_mode == blend_mode::translucent && (material_blend_mode == blend_mode::opaque || material_blend_mode == blend_mode::masked)) - { - glDisable(GL_BLEND); - } - - active_blend_mode = material_blend_mode; - } - - // Set back-face culling mode - const bool material_two_sided = active_material->is_two_sided(); - if (material_two_sided != active_two_sided) - { - if (material_two_sided) - { - glDisable(GL_CULL_FACE); - } - else - { - glEnable(GL_CULL_FACE); - } - - active_two_sided = material_two_sided; - } - - // Change rasterizer state according to material flags - std::uint32_t material_flags = active_material->get_flags(); - if (active_material_flags != material_flags) - { - if ((material_flags & MATERIAL_FLAG_X_RAY) != (active_material_flags & MATERIAL_FLAG_X_RAY)) - { - if (material_flags & MATERIAL_FLAG_X_RAY) - { - glDisable(GL_DEPTH_TEST); - - } - else - { - glEnable(GL_DEPTH_TEST); - } - } - - if ((material_flags & MATERIAL_FLAG_DECAL_SURFACE) != (active_material_flags & MATERIAL_FLAG_DECAL_SURFACE)) - { - if (material_flags & MATERIAL_FLAG_DECAL_SURFACE) - { - glEnable(GL_STENCIL_TEST); - glStencilFunc(GL_ALWAYS, 1, ~0); - glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); - glStencilMask(~0); - } - else - { - glDisable(GL_STENCIL_TEST); - glStencilMask(0); - } - } - - if ((material_flags & MATERIAL_FLAG_DECAL) != (active_material_flags & MATERIAL_FLAG_DECAL)) - { - if (material_flags & MATERIAL_FLAG_DECAL) - { - glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_GEQUAL); - glDepthMask(GL_FALSE); - - glEnable(GL_STENCIL_TEST); - glStencilFunc(GL_EQUAL, 1, ~0); - //glStencilOp(GL_KEEP, GL_KEEP, GL_ZERO); - //glStencilMask(~0); - glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); - glStencilMask(0); - } - else - { - glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_GREATER); - glDepthMask(GL_TRUE); - glDisable(GL_STENCIL_TEST); - glStencilMask(0); - } - } - - /* - if ((material_flags & MATERIAL_FLAG_OUTLINE) != (active_material_flags & MATERIAL_FLAG_OUTLINE)) - { - if (material_flags & MATERIAL_FLAG_OUTLINE) - { - glEnable(GL_STENCIL_TEST); - glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); - glStencilFunc(GL_ALWAYS, 2, 0xFF); - glStencilMask(0xFF); - } - else - { - glDisable(GL_STENCIL_TEST); - glStencilMask(0x00); - } - } - */ - - active_material_flags = material_flags; - } - - // Switch shaders if necessary - const gl::shader_program* shader_program = active_material->get_shader_program(); - if (active_shader_program != shader_program) - { - active_shader_program = shader_program; - if (!active_shader_program) - { - continue; - } - - // Change shader program - rasterizer->use_program(*active_shader_program); - - // Get set of known shader input parameters - if (auto it = parameter_sets.find(active_shader_program); it != parameter_sets.end()) - { - parameters = it->second; - } - else - { - parameters = load_parameter_set(active_shader_program); - } - - // Upload context-dependent shader parameters - if (parameters->time) - parameters->time->upload(ctx.t); - if (parameters->mouse) - parameters->mouse->upload(mouse_position); - if (parameters->resolution) - parameters->resolution->upload(resolution); - if (parameters->camera_position) - parameters->camera_position->upload(camera_position); - if (parameters->camera_exposure) - parameters->camera_exposure->upload(ctx.exposure); - if (parameters->view) - parameters->view->upload(view); - if (parameters->view_projection) - parameters->view_projection->upload(view_projection); - if (parameters->ambient_light_count) - parameters->ambient_light_count->upload(ambient_light_count); - if (parameters->ambient_light_colors) - parameters->ambient_light_colors->upload(0, ambient_light_colors, ambient_light_count); - if (parameters->point_light_count) - parameters->point_light_count->upload(point_light_count); - if (parameters->point_light_colors) - parameters->point_light_colors->upload(0, point_light_colors, point_light_count); - if (parameters->point_light_positions) - parameters->point_light_positions->upload(0, point_light_positions, point_light_count); - if (parameters->point_light_attenuations) - parameters->point_light_attenuations->upload(0, point_light_attenuations, point_light_count); - if (parameters->directional_light_count) - parameters->directional_light_count->upload(directional_light_count); - if (parameters->directional_light_colors) - parameters->directional_light_colors->upload(0, directional_light_colors, directional_light_count); - if (parameters->directional_light_directions) - parameters->directional_light_directions->upload(0, directional_light_directions, directional_light_count); - - if (parameters->directional_light_textures) - parameters->directional_light_textures->upload(0, directional_light_textures, directional_light_count); - if (parameters->directional_light_texture_matrices) - parameters->directional_light_texture_matrices->upload(0, directional_light_texture_matrices, directional_light_count); - if (parameters->directional_light_texture_opacities) - parameters->directional_light_texture_opacities->upload(0, directional_light_texture_opacities, directional_light_count); - - if (parameters->shadow_map_directional && shadow_map_texture) - parameters->shadow_map_directional->upload(shadow_map_texture); - if (parameters->shadow_bias_directional) - parameters->shadow_bias_directional->upload(shadow_bias_directional); - if (parameters->shadow_matrices_directional) - parameters->shadow_matrices_directional->upload(0, shadow_matrices_directional, shadow_cascade_count); - if (parameters->shadow_splits_directional) - parameters->shadow_splits_directional->upload(0, shadow_splits_directional, shadow_cascade_count); - - if (parameters->spot_light_count) - parameters->spot_light_count->upload(spot_light_count); - if (parameters->spot_light_colors) - parameters->spot_light_colors->upload(0, spot_light_colors, spot_light_count); - if (parameters->spot_light_positions) - parameters->spot_light_positions->upload(0, spot_light_positions, spot_light_count); - if (parameters->spot_light_directions) - parameters->spot_light_directions->upload(0, spot_light_directions, spot_light_count); - if (parameters->spot_light_attenuations) - parameters->spot_light_attenuations->upload(0, spot_light_attenuations, spot_light_count); - if (parameters->spot_light_cutoffs) - parameters->spot_light_cutoffs->upload(0, spot_light_cutoffs, spot_light_count); - } - - // Upload material properties to shader - active_material->upload(ctx.alpha); - } - - // Calculate operation-dependent parameters - model = operation.transform; - model_view_projection = view_projection * model; - model_view = view * model; - normal_model = math::transpose(math::inverse(math::matrix(model))); - normal_model_view = math::transpose(math::inverse(math::matrix(model_view))); - - // Skinning palette - if (operation.bone_count && parameters->skinning_palette) - { - parameters->skinning_palette->upload(0, operation.skinning_palette, operation.bone_count); - } - - // Upload operation-dependent parameters - if (parameters->model) - parameters->model->upload(model); - if (parameters->model_view) - parameters->model_view->upload(model_view); - if (parameters->model_view_projection) - parameters->model_view_projection->upload(model_view_projection); - if (parameters->normal_model) - parameters->normal_model->upload(normal_model); - if (parameters->normal_model_view) - parameters->normal_model_view->upload(normal_model_view); - if (parameters->clip_depth) - parameters->clip_depth->upload(clip_depth); - if (parameters->log_depth_coef) - parameters->log_depth_coef->upload(log_depth_coef); - - // Draw geometry - if (operation.instance_count) - rasterizer->draw_arrays_instanced(*operation.vertex_array, operation.drawing_mode, operation.start_index, operation.index_count, operation.instance_count); - else - rasterizer->draw_arrays(*operation.vertex_array, operation.drawing_mode, operation.start_index, operation.index_count); - } -} - -void material_pass::set_fallback_material(const material* fallback) -{ - this->fallback_material = fallback; -} - -const material_pass::parameter_set* material_pass::load_parameter_set(const gl::shader_program* program) const -{ - // Allocate a new parameter set - parameter_set* parameters = new parameter_set(); - - // Connect inputs - parameters->time = program->get_input("time"); - parameters->mouse = program->get_input("mouse"); - parameters->resolution = program->get_input("resolution"); - parameters->camera_position = program->get_input("camera.position"); - parameters->camera_exposure = program->get_input("camera.exposure"); - parameters->model = program->get_input("model"); - parameters->view = program->get_input("view"); - parameters->projection = program->get_input("projection"); - parameters->model_view = program->get_input("model_view"); - parameters->view_projection = program->get_input("view_projection"); - parameters->model_view_projection = program->get_input("model_view_projection"); - parameters->normal_model = program->get_input("normal_model"); - parameters->normal_model_view = program->get_input("normal_model_view"); - parameters->clip_depth = program->get_input("clip_depth"); - parameters->log_depth_coef = program->get_input("log_depth_coef"); - parameters->ambient_light_count = program->get_input("ambient_light_count"); - parameters->ambient_light_colors = program->get_input("ambient_light_colors"); - parameters->point_light_count = program->get_input("point_light_count"); - parameters->point_light_colors = program->get_input("point_light_colors"); - parameters->point_light_positions = program->get_input("point_light_positions"); - parameters->point_light_attenuations = program->get_input("point_light_attenuations"); - parameters->directional_light_count = program->get_input("directional_light_count"); - parameters->directional_light_colors = program->get_input("directional_light_colors"); - parameters->directional_light_directions = program->get_input("directional_light_directions"); - parameters->directional_light_textures = program->get_input("directional_light_textures"); - parameters->directional_light_texture_matrices = program->get_input("directional_light_texture_matrices"); - parameters->directional_light_texture_opacities = program->get_input("directional_light_texture_opacities"); - parameters->spot_light_count = program->get_input("spot_light_count"); - parameters->spot_light_colors = program->get_input("spot_light_colors"); - parameters->spot_light_positions = program->get_input("spot_light_positions"); - parameters->spot_light_directions = program->get_input("spot_light_directions"); - parameters->spot_light_attenuations = program->get_input("spot_light_attenuations"); - parameters->spot_light_cutoffs = program->get_input("spot_light_cutoffs"); - parameters->shadow_map_directional = program->get_input("shadow_map_directional"); - parameters->shadow_bias_directional = program->get_input("shadow_bias_directional"); - parameters->shadow_splits_directional = program->get_input("shadow_splits_directional"); - parameters->shadow_matrices_directional = program->get_input("shadow_matrices_directional"); - parameters->skinning_palette = program->get_input("skinning_palette"); - - // Add parameter set to map of parameter sets - parameter_sets[program] = parameters; - - return parameters; -} - -bool operation_compare(const render::operation& a, const render::operation& b) -{ - if (!a.material) - return false; - else if (!b.material) - return true; - - bool xray_a = a.material->get_flags() & MATERIAL_FLAG_X_RAY; - bool xray_b = b.material->get_flags() & MATERIAL_FLAG_X_RAY; - - const bool two_sided_a = (a.material) ? a.material->is_two_sided() : false; - const bool two_sided_b = (b.material) ? b.material->is_two_sided() : false; - - if (xray_a) - { - if (xray_b) - { - // A and B are both xray, render back to front - return (a.depth > b.depth); - } - else - { - // A is xray, B is not. Render B first - return false; - } - } - else - { - if (xray_b) - { - // A is opaque, B is xray. Render A first - return true; - } - else - { - // Determine transparency - bool transparent_a = a.material->get_blend_mode() == blend_mode::translucent; - bool transparent_b = b.material->get_blend_mode() == blend_mode::translucent; - - if (transparent_a) - { - if (transparent_b) - { - // Determine decal status - bool decal_a = a.material->get_flags() & MATERIAL_FLAG_DECAL; - bool decal_b = b.material->get_flags() & MATERIAL_FLAG_DECAL; - - if (decal_a) - { - if (decal_b) - { - // A and B are both transparent decals, render back to front - return (a.depth > b.depth); - } - else - { - // A is a transparent decal, B is transparent but not a decal, render A first - return true; - } - } - else - { - if (decal_b) - { - // A is transparent but not a decal, B is a transparent decal, render B first - return false; - } - else - { - // A and B are both transparent, but not decals, render back to front - return (a.depth < b.depth); - } - } - } - else - { - // A is transparent, B is opaque. Render B first - return false; - } - } - else - { - if (transparent_b) - { - // A is opaque, B is transparent. Render A first - return true; - } - else - { - // A and B are both opaque - if (a.material->get_shader_program() == b.material->get_shader_program()) - { - // A and B have the same shader - if (a.vertex_array == b.vertex_array) - { - // A and B have the same VAO, render front to back - return (a.depth < b.depth); - } - else - { - // A and B have different VAOs, sort by two-sided - if (two_sided_a) - { - if (two_sided_b) - { - // A and B are both two-sided, sort by VAO - return (a.vertex_array < b.vertex_array); - } - else - { - // A is two-sided, B is one-sided. Render B first - return false; - } - } - else - { - if (two_sided_b) - { - // A is one-sided, B is two-sided. Render A first - return true; - } - else - { - // A and B are both one-sided, sort by VAO - return (a.vertex_array < b.vertex_array); - } - } - } - } - else - { - // A and B are both opaque and have different shaders, sort by shader - return (a.material->get_shader_program() < b.material->get_shader_program()); - } - } - } - } - } -} - -} // namespace render diff --git a/src/render/passes/material-pass.hpp b/src/render/passes/material-pass.hpp deleted file mode 100644 index 0ff4ea7..0000000 --- a/src/render/passes/material-pass.hpp +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_RENDER_MATERIAL_PASS_HPP -#define ANTKEEPER_RENDER_MATERIAL_PASS_HPP - -#include "render/pass.hpp" -#include "render/material.hpp" -#include "utility/fundamental-types.hpp" -#include "gl/shader-program.hpp" -#include "gl/shader-input.hpp" -#include "gl/texture-2d.hpp" -#include - -class resource_manager; - -namespace render { - -/** - * Renders scene objects using their material-specified shaders and properties. - */ -class material_pass: public pass -{ -public: - material_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager); - virtual ~material_pass(); - virtual void render(const render::context& ctx, render::queue& queue) const final; - - /// Sets the material to be used when a render operation is missing a material. If no fallback material is specified, render operations without materials will not be processed. - void set_fallback_material(const material* fallback); - -private: - /** - * Sets of known shader input parameters. Each time a new shader is encountered, a parameter set will be created and its inputs connected to the shader program. A null input indiciates that the shader doesn't have that parameter. - */ - struct parameter_set - { - const gl::shader_input* time; - const gl::shader_input* mouse; - const gl::shader_input* resolution; - const gl::shader_input* camera_position; - const gl::shader_input* camera_exposure; - const gl::shader_input* model; - const gl::shader_input* view; - const gl::shader_input* projection; - const gl::shader_input* model_view; - const gl::shader_input* view_projection; - const gl::shader_input* model_view_projection; - const gl::shader_input* normal_model; - const gl::shader_input* normal_model_view; - const gl::shader_input* clip_depth; - const gl::shader_input* log_depth_coef; - - const gl::shader_input* ambient_light_count; - const gl::shader_input* ambient_light_colors; - const gl::shader_input* point_light_count; - const gl::shader_input* point_light_colors; - const gl::shader_input* point_light_positions; - const gl::shader_input* point_light_attenuations; - const gl::shader_input* directional_light_count; - const gl::shader_input* directional_light_colors; - const gl::shader_input* directional_light_directions; - const gl::shader_input* directional_light_textures; - const gl::shader_input* directional_light_texture_matrices; - const gl::shader_input* directional_light_texture_opacities; - const gl::shader_input* spot_light_count; - const gl::shader_input* spot_light_colors; - const gl::shader_input* spot_light_positions; - const gl::shader_input* spot_light_directions; - const gl::shader_input* spot_light_attenuations; - const gl::shader_input* spot_light_cutoffs; - - const gl::shader_input* shadow_map_directional; - const gl::shader_input* shadow_bias_directional; - const gl::shader_input* shadow_splits_directional; - const gl::shader_input* shadow_matrices_directional; - - const gl::shader_input* skinning_palette; - }; - - const parameter_set* load_parameter_set(const gl::shader_program* program) const; - - mutable std::unordered_map parameter_sets; - const material* fallback_material; - float2 mouse_position; - - int max_ambient_light_count; - int max_point_light_count; - int max_directional_light_count; - int max_spot_light_count; - int max_bone_count; - - mutable int ambient_light_count; - mutable int point_light_count; - mutable int directional_light_count; - mutable int spot_light_count; - - float3* ambient_light_colors; - float3* point_light_colors; - float3* point_light_positions; - float3* point_light_attenuations; - float3* directional_light_colors; - float3* directional_light_directions; - const gl::texture_2d** directional_light_textures; - float4x4* directional_light_texture_matrices; - float* directional_light_texture_opacities; - float3* spot_light_colors; - float3* spot_light_positions; - float3* spot_light_directions; - float3* spot_light_attenuations; - float2* spot_light_cutoffs; -}; - -} // namespace render - -#endif // ANTKEEPER_RENDER_MATERIAL_PASS_HPP diff --git a/src/render/passes/outline-pass.cpp b/src/render/passes/outline-pass.cpp deleted file mode 100644 index 76d38bd..0000000 --- a/src/render/passes/outline-pass.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "render/passes/outline-pass.hpp" -#include "resources/resource-manager.hpp" -#include "gl/rasterizer.hpp" -#include "gl/framebuffer.hpp" -#include "gl/shader-program.hpp" -#include "gl/shader-input.hpp" -#include "gl/vertex-buffer.hpp" -#include "gl/vertex-array.hpp" -#include "gl/vertex-attribute.hpp" -#include "gl/drawing-mode.hpp" -#include "render/vertex-attribute.hpp" -#include "render/context.hpp" -#include "render/material.hpp" -#include "render/material-flags.hpp" -#include "scene/camera.hpp" -#include -#include - -namespace render { - -outline_pass::outline_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager): - pass(rasterizer, framebuffer), - fill_shader(nullptr), - stroke_shader(nullptr) -{ - // Load fill shader - fill_shader = resource_manager->load("outline-fill-unskinned.glsl"); - fill_model_view_projection_input = fill_shader->get_input("model_view_projection"); - - // Load stroke shader - stroke_shader = resource_manager->load("outline-stroke-unskinned.glsl"); - stroke_model_view_projection_input = stroke_shader->get_input("model_view_projection"); - stroke_width_input = stroke_shader->get_input("width"); - stroke_color_input = stroke_shader->get_input("color"); -} - -outline_pass::~outline_pass() -{} - -void outline_pass::render(const render::context& ctx, render::queue& queue) const -{ - rasterizer->use_framebuffer(*framebuffer); - - // Determine viewport based on framebuffer resolution - auto viewport = framebuffer->get_dimensions(); - rasterizer->set_viewport(0, 0, std::get<0>(viewport), std::get<1>(viewport)); - - // Get camera matrices - float4x4 view = ctx.camera->get_view_tween().interpolate(ctx.alpha); - float4x4 view_projection = ctx.camera->get_view_projection_tween().interpolate(ctx.alpha); - - float4x4 model_view_projection; - - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); - glDisable(GL_DEPTH_TEST); - glEnable(GL_STENCIL_TEST); - - // Render fill - { - glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); - glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); - glStencilFunc(GL_ALWAYS, 2, 0xFF); - glStencilMask(0xFF); - glDisable(GL_BLEND); - - // Setup fill shader - rasterizer->use_program(*fill_shader); - - // Render fills - for (const render::operation& operation: queue) - { - const render::material* material = operation.material; - if (!material || !(material->get_flags() & MATERIAL_FLAG_OUTLINE)) - continue; - - model_view_projection = view_projection * operation.transform; - fill_model_view_projection_input->upload(model_view_projection); - - rasterizer->draw_arrays(*operation.vertex_array, operation.drawing_mode, operation.start_index, operation.index_count); - } - } - - // Render stroke - { - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - - if (outline_color[3] < 1.0f) - { - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - } - else - { - glDisable(GL_BLEND); - } - - glStencilFunc(GL_NOTEQUAL, 2, 0xFF); - glStencilMask(0x00); - - // Setup stroke shader - rasterizer->use_program(*stroke_shader); - stroke_width_input->upload(outline_width); - stroke_color_input->upload(outline_color); - - // Render strokes - for (const render::operation& operation: queue) - { - const render::material* material = operation.material; - if (!material || !(material->get_flags() & MATERIAL_FLAG_OUTLINE)) - continue; - - model_view_projection = view_projection * operation.transform; - stroke_model_view_projection_input->upload(model_view_projection); - - rasterizer->draw_arrays(*operation.vertex_array, operation.drawing_mode, operation.start_index, operation.index_count); - } - } - - glDisable(GL_STENCIL_TEST); -} - -void outline_pass::set_outline_width(float width) -{ - outline_width = width; -} - -void outline_pass::set_outline_color(const float4& color) -{ - outline_color = color; -} - -} // namespace render diff --git a/src/render/passes/outline-pass.hpp b/src/render/passes/outline-pass.hpp deleted file mode 100644 index 754afed..0000000 --- a/src/render/passes/outline-pass.hpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_RENDER_OUTLINE_PASS_HPP -#define ANTKEEPER_RENDER_OUTLINE_PASS_HPP - -#include "render/pass.hpp" -#include "utility/fundamental-types.hpp" -#include "gl/shader-program.hpp" -#include "gl/shader-input.hpp" - -class resource_manager; - -namespace render { - -/** - * - */ -class outline_pass: public pass -{ -public: - outline_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager); - virtual ~outline_pass(); - virtual void render(const render::context& ctx, render::queue& queue) const final; - - void set_outline_width(float width); - void set_outline_color(const float4& color); - -private: - gl::shader_program* fill_shader; - const gl::shader_input* fill_model_view_projection_input; - - gl::shader_program* stroke_shader; - const gl::shader_input* stroke_model_view_projection_input; - const gl::shader_input* stroke_width_input; - const gl::shader_input* stroke_color_input; - - float outline_width; - float4 outline_color; -}; - -} // namespace render - -#endif // ANTKEEPER_RENDER_OUTLINE_PASS_HPP diff --git a/src/render/passes/resample-pass.cpp b/src/render/passes/resample-pass.cpp deleted file mode 100644 index f90c566..0000000 --- a/src/render/passes/resample-pass.cpp +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "render/passes/resample-pass.hpp" -#include "resources/resource-manager.hpp" -#include "gl/rasterizer.hpp" -#include "gl/framebuffer.hpp" -#include "gl/shader-program.hpp" -#include "gl/shader-input.hpp" -#include "gl/vertex-buffer.hpp" -#include "gl/vertex-array.hpp" -#include "gl/vertex-attribute.hpp" -#include "gl/drawing-mode.hpp" -#include "gl/texture-2d.hpp" -#include "render/vertex-attribute.hpp" -#include "render/context.hpp" -#include - -namespace render { - -resample_pass::resample_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager): - pass(rasterizer, framebuffer), - source_texture(nullptr) -{ - // Load resample shader template - shader_template = resource_manager->load("resample.glsl"); - - // Build resample shader program - shader = shader_template->build(); - source_texture_input = shader->get_input("source_texture"); - - const float vertex_data[] = - { - -1.0f, 1.0f, - -1.0f, -1.0f, - 1.0f, 1.0f, - 1.0f, 1.0f, - -1.0f, -1.0f, - 1.0f, -1.0f - }; - - std::size_t vertex_size = 2; - std::size_t vertex_stride = sizeof(float) * vertex_size; - std::size_t vertex_count = 6; - - quad_vbo = new gl::vertex_buffer(sizeof(float) * vertex_size * vertex_count, vertex_data); - quad_vao = new gl::vertex_array(); - - // Define position vertex attribute - gl::vertex_attribute position_attribute; - position_attribute.buffer = quad_vbo; - position_attribute.offset = 0; - position_attribute.stride = vertex_stride; - position_attribute.type = gl::vertex_attribute_type::float_32; - position_attribute.components = 2; - - // Bind vertex attributes to VAO - quad_vao->bind(render::vertex_attribute::position, position_attribute); -} - -resample_pass::~resample_pass() -{ - delete quad_vao; - delete quad_vbo; - - delete shader; - - /// @TODO - // resource_manager->unload("resample.glsl"); -} - -void resample_pass::render(const render::context& ctx, render::queue& queue) const -{ - if (!source_texture) - return; - - // Set rasterizer state - glDisable(GL_DEPTH_TEST); - glDepthMask(GL_FALSE); - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); - glDisable(GL_BLEND); - - // Render FXAA - rasterizer->use_framebuffer(*framebuffer); - rasterizer->set_viewport(0, 0, framebuffer->get_dimensions()[0], framebuffer->get_dimensions()[1]); - rasterizer->use_program(*shader); - source_texture_input->upload(source_texture); - rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6); -} - -void resample_pass::set_source_texture(const gl::texture_2d* texture) -{ - source_texture = texture; -} - -} // namespace render diff --git a/src/render/passes/resample-pass.hpp b/src/render/passes/resample-pass.hpp deleted file mode 100644 index 653e421..0000000 --- a/src/render/passes/resample-pass.hpp +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_RENDER_RESAMPLE_PASS_HPP -#define ANTKEEPER_RENDER_RESAMPLE_PASS_HPP - -#include "render/pass.hpp" -#include "render/shader-template.hpp" -#include "gl/shader-program.hpp" -#include "gl/shader-input.hpp" -#include "gl/vertex-buffer.hpp" -#include "gl/vertex-array.hpp" -#include "gl/texture-2d.hpp" - -class resource_manager; - -namespace render { - -/** - * Resamples a texture. - */ -class resample_pass: public pass -{ -public: - /** - * Constructs a resample pass. - * - * @param rasterizer Rasterizer. - * @param framebuffer Target framebuffer. - * @param resource_manager Resource manager. - */ - resample_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager); - - /** - * Destructs a resample pass. - */ - virtual ~resample_pass(); - - /** - * Resamples a texture. - * - * @param ctx Render context. - * @param queue Render queue. - */ - virtual void render(const render::context& ctx, render::queue& queue) const final; - - /** - * Sets the resample source texture. - * - * @param texture Texture to resample. - */ - void set_source_texture(const gl::texture_2d* texture); - -private: - const gl::texture_2d* source_texture; - - render::shader_template* shader_template; - gl::shader_program* shader; - const gl::shader_input* source_texture_input; - - gl::vertex_buffer* quad_vbo; - gl::vertex_array* quad_vao; -}; - -} // namespace render - -#endif // ANTKEEPER_RENDER_RESAMPLE_PASS_HPP diff --git a/src/render/passes/shadow-map-pass.cpp b/src/render/passes/shadow-map-pass.cpp deleted file mode 100644 index a0dfdf9..0000000 --- a/src/render/passes/shadow-map-pass.cpp +++ /dev/null @@ -1,368 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "render/passes/shadow-map-pass.hpp" -#include "resources/resource-manager.hpp" -#include "gl/rasterizer.hpp" -#include "gl/framebuffer.hpp" -#include "gl/shader-program.hpp" -#include "gl/shader-input.hpp" -#include "gl/drawing-mode.hpp" -#include "render/context.hpp" -#include "render/material.hpp" -#include "scene/camera.hpp" -#include "scene/collection.hpp" -#include "scene/light.hpp" -#include "geom/view-frustum.hpp" -#include "geom/aabb.hpp" -#include "config.hpp" -#include "math/interpolation.hpp" -#include "math/vector.hpp" -#include "math/matrix.hpp" -#include "math/quaternion.hpp" -#include "math/projection.hpp" -#include -#include - -namespace render { - -static bool operation_compare(const render::operation& a, const render::operation& b); - -shadow_map_pass::shadow_map_pass(gl::rasterizer* rasterizer, resource_manager* resource_manager): - pass(rasterizer, nullptr) -{ - // Load skinned shader program - unskinned_shader_program = resource_manager->load("depth-unskinned.glsl"); - unskinned_model_view_projection_input = unskinned_shader_program->get_input("model_view_projection"); - - // Load unskinned shader program - skinned_shader_program = resource_manager->load("depth-skinned.glsl"); - skinned_model_view_projection_input = skinned_shader_program->get_input("model_view_projection"); - - // Calculate bias-tile matrices - float4x4 bias_matrix = math::translate(math::matrix4::identity(), float3{0.5f, 0.5f, 0.5f}) * math::scale(math::matrix4::identity(), float3{0.5f, 0.5f, 0.5f}); - float4x4 tile_scale = math::scale(math::matrix4::identity(), float3{0.5f, 0.5f, 1.0f}); - for (int i = 0; i < 4; ++i) - { - float x = static_cast(i % 2) * 0.5f; - float y = static_cast(i / 2) * 0.5f; - float4x4 tile_matrix = math::translate(math::matrix4::identity(), float3{x, y, 0.0f}) * tile_scale; - bias_tile_matrices[i] = tile_matrix * bias_matrix; - } -} - -shadow_map_pass::~shadow_map_pass() -{} - -void shadow_map_pass::render(const render::context& ctx, render::queue& queue) const -{ - // Collect lights - const std::list* lights = ctx.collection->get_objects(scene::light::object_type_id); - for (const scene::object_base* object: *lights) - { - // Ignore inactive lights - if (!object->is_active()) - continue; - - // Ignore non-directional lights - const scene::light* light = static_cast(object); - if (light->get_light_type() != scene::light_type::directional) - continue; - - // Ignore non-shadow casters - const scene::directional_light* directional_light = static_cast(light); - if (!directional_light->is_shadow_caster()) - continue; - - // Ignore improperly-configured lights - if (!directional_light->get_shadow_cascade_count() || !directional_light->get_shadow_framebuffer()) - continue; - - // Render cascaded shadow maps for light - render_csm(*directional_light, ctx, queue); - } -} - -void shadow_map_pass::render_csm(const scene::directional_light& light, const render::context& ctx, render::queue& queue) const -{ - rasterizer->use_framebuffer(*light.get_shadow_framebuffer()); - - // Disable blending - glDisable(GL_BLEND); - - // Enable depth testing - glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_LESS); - glDepthMask(GL_TRUE); - - // Enable back-face culling - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); - bool two_sided = false; - - // For half-z buffer - glDepthRange(-1.0f, 1.0f); - - // Get camera - const scene::camera& camera = *ctx.camera; - - // Get tweened camera parameters - const float camera_fov = camera.get_fov_tween().interpolate(ctx.alpha); - const float camera_aspect_ratio = camera.get_aspect_ratio_tween().interpolate(ctx.alpha); - const float camera_clip_near = camera.get_clip_near_tween().interpolate(ctx.alpha); - const float camera_clip_far = camera.get_clip_far_tween().interpolate(ctx.alpha); - - // Calculate distance to shadow cascade depth clipping planes - const float shadow_clip_far = math::lerp(camera_clip_near, camera_clip_far, light.get_shadow_cascade_coverage()); - - const unsigned int cascade_count = light.get_shadow_cascade_count(); - float* cascade_distances = light.get_shadow_cascade_distances(); - float4x4* cascade_matrices = light.get_shadow_cascade_matrices(); - - // Calculate cascade far clipping plane distances - cascade_distances[cascade_count - 1] = shadow_clip_far; - for (unsigned int i = 0; i < cascade_count - 1; ++i) - { - const float weight = static_cast(i + 1) / static_cast(cascade_count); - - // Calculate linear and logarithmic distribution distances - const float linear_distance = math::lerp(camera_clip_near, shadow_clip_far, weight); - const float log_distance = math::log_lerp(camera_clip_near, shadow_clip_far, weight); - - // Interpolate between linear and logarithmic distribution distances - cascade_distances[i] = math::lerp(linear_distance, log_distance, light.get_shadow_cascade_distribution()); - } - - // Calculate viewports for each shadow map - const int shadow_map_resolution = static_cast(light.get_shadow_framebuffer()->get_depth_attachment()->get_width()); - const int cascade_resolution = shadow_map_resolution / 2; - int4 shadow_map_viewports[4]; - for (int i = 0; i < 4; ++i) - { - int x = i % 2; - int y = i / 2; - - int4& viewport = shadow_map_viewports[i]; - viewport[0] = x * cascade_resolution; - viewport[1] = y * cascade_resolution; - viewport[2] = cascade_resolution; - viewport[3] = cascade_resolution; - } - - // Calculate a view-projection matrix from the directional light's transform - math::transform light_transform = light.get_transform_tween().interpolate(ctx.alpha); - float3 forward = light_transform.rotation * config::global_forward; - float3 up = light_transform.rotation * config::global_up; - float4x4 light_view = math::look_at(light_transform.translation, light_transform.translation + forward, up); - float4x4 light_projection = math::ortho(-1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f); - float4x4 light_view_projection = light_projection * light_view; - - float4x4 cropped_view_projection; - float4x4 model_view_projection; - - // Sort render queue - queue.sort(operation_compare); - - gl::shader_program* active_shader_program = nullptr; - - for (unsigned int i = 0; i < cascade_count; ++i) - { - // Set viewport for this shadow map - const int4& viewport = shadow_map_viewports[i]; - rasterizer->set_viewport(viewport[0], viewport[1], viewport[2], viewport[3]); - - // Calculate projection matrix for view camera subfrustum - const float subfrustum_near = (i) ? cascade_distances[i - 1] : camera_clip_near; - const float subfrustum_far = cascade_distances[i]; - float4x4 subfrustum_projection = math::perspective_half_z(camera_fov, camera_aspect_ratio, subfrustum_near, subfrustum_far); - - // Calculate view camera subfrustum - geom::view_frustum subfrustum(subfrustum_projection * ctx.view); - - // Create AABB containing the view camera subfrustum corners - const std::array& subfrustum_corners = subfrustum.get_corners(); - geom::aabb subfrustum_aabb = {subfrustum_corners[0], subfrustum_corners[0]}; - for (int j = 1; j < 8; ++j) - { - subfrustum_aabb.min_point = math::min(subfrustum_aabb.min_point, subfrustum_corners[j]); - subfrustum_aabb.max_point = math::max(subfrustum_aabb.max_point, subfrustum_corners[j]); - } - - // Transform subfrustum AABB into the light clip-space - geom::aabb cropping_bounds = geom::aabb::transform(subfrustum_aabb, light_view_projection); - - // Quantize clip-space coordinates - const float texel_scale_x = (cropping_bounds.max_point.x() - cropping_bounds.min_point.x()) / static_cast(cascade_resolution); - const float texel_scale_y = (cropping_bounds.max_point.y() - cropping_bounds.min_point.y()) / static_cast(cascade_resolution); - cropping_bounds.min_point.x() = std::floor(cropping_bounds.min_point.x() / texel_scale_x) * texel_scale_x; - cropping_bounds.max_point.x() = std::floor(cropping_bounds.max_point.x() / texel_scale_x) * texel_scale_x; - cropping_bounds.min_point.y() = std::floor(cropping_bounds.min_point.y() / texel_scale_y) * texel_scale_y; - cropping_bounds.max_point.y() = std::floor(cropping_bounds.max_point.y() / texel_scale_y) * texel_scale_y; - - // Recalculate light projection matrix with quantized coordinates - light_projection = math::ortho_half_z - ( - cropping_bounds.min_point.x(), cropping_bounds.max_point.x(), - cropping_bounds.min_point.y(), cropping_bounds.max_point.y(), - cropping_bounds.min_point.z(), cropping_bounds.max_point.z() - ); - - // Calculate cropped view projection matrix - cropped_view_projection = light_projection * light_view; - - // Calculate world-space to cascade texture-space transformation matrix - cascade_matrices[i] = bias_tile_matrices[i] * cropped_view_projection; - - for (const render::operation& operation: queue) - { - const render::material* material = operation.material; - if (material) - { - // Skip materials which don't cast shadows - if (material->get_shadow_mode() == shadow_mode::none) - continue; - - if (material->is_two_sided() != two_sided) - { - if (material->is_two_sided()) - { - glDisable(GL_CULL_FACE); - } - else - { - glEnable(GL_CULL_FACE); - } - - two_sided = material->is_two_sided(); - } - } - - // Switch shader programs if necessary - gl::shader_program* shader_program = (operation.bone_count) ? skinned_shader_program : unskinned_shader_program; - if (active_shader_program != shader_program) - { - active_shader_program = shader_program; - rasterizer->use_program(*active_shader_program); - } - - // Calculate model-view-projection matrix - model_view_projection = cropped_view_projection * operation.transform; - - // Upload operation-dependent parameters to shader program - if (active_shader_program == unskinned_shader_program) - { - unskinned_model_view_projection_input->upload(model_view_projection); - } - else if (active_shader_program == skinned_shader_program) - { - skinned_model_view_projection_input->upload(model_view_projection); - } - - // Draw geometry - rasterizer->draw_arrays(*operation.vertex_array, operation.drawing_mode, operation.start_index, operation.index_count); - } - } -} - -bool operation_compare(const render::operation& a, const render::operation& b) -{ - const bool skinned_a = (a.bone_count); - const bool skinned_b = (b.bone_count); - const bool two_sided_a = (a.material) ? a.material->is_two_sided() : false; - const bool two_sided_b = (b.material) ? b.material->is_two_sided() : false; - - if (skinned_a) - { - if (skinned_b) - { - // A and B are both skinned, sort by two-sided - if (two_sided_a) - { - if (two_sided_b) - { - // A and B are both two-sided, sort by VAO - return (a.vertex_array < b.vertex_array); - } - else - { - // A is two-sided, B is one-sided. Render B first - return false; - } - } - else - { - if (two_sided_b) - { - // A is one-sided, B is two-sided. Render A first - return true; - } - else - { - // A and B are both one-sided, sort by VAO - return (a.vertex_array < b.vertex_array); - } - } - } - else - { - // A is skinned, B is unskinned. Render B first - return false; - } - } - else - { - if (skinned_b) - { - // A is unskinned, B is skinned. Render A first - return true; - } - else - { - // A and B are both unskinned, sort by two-sided - if (two_sided_a) - { - if (two_sided_b) - { - // A and B are both two-sided, sort by VAO - return (a.vertex_array < b.vertex_array); - } - else - { - // A is two-sided, B is one-sided. Render B first - return false; - } - } - else - { - if (two_sided_b) - { - // A is one-sided, B is two-sided. Render A first - return true; - } - else - { - // A and B are both one-sided, sort by VAO - return (a.vertex_array < b.vertex_array); - } - } - } - } -} - -} // namespace render diff --git a/src/render/passes/shadow-map-pass.hpp b/src/render/passes/shadow-map-pass.hpp deleted file mode 100644 index cd056ee..0000000 --- a/src/render/passes/shadow-map-pass.hpp +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_RENDER_SHADOW_MAP_PASS_HPP -#define ANTKEEPER_RENDER_SHADOW_MAP_PASS_HPP - -#include "render/pass.hpp" -#include "utility/fundamental-types.hpp" -#include "scene/directional-light.hpp" -#include "gl/shader-program.hpp" -#include "gl/shader-input.hpp" - -class resource_manager; - -namespace render { - -/** - * Renders shadow maps. - */ -class shadow_map_pass: public pass -{ -public: - /** - * Constructs a shadow map pass. - * - * @param rasterizer Rasterizer. - * @param framebuffer Shadow map framebuffer. - * @param resource_manage Resource manager. - */ - shadow_map_pass(gl::rasterizer* rasterizer, resource_manager* resource_manager); - - /** - * Destructs a shadow map pass. - */ - virtual ~shadow_map_pass(); - - /** - * Renders shadow maps for a single camera. - * - * @param ctx Render context. - * @param queue Render queue. - */ - virtual void render(const render::context& ctx, render::queue& queue) const final; - -private: - /** - * Renders cascaded shadow maps for a single directional light. - * - * @param light Shadow-casting directional light. - * @param ctx Render context. - * @param queue Render queue. - */ - void render_csm(const scene::directional_light& light, const render::context& ctx, render::queue& queue) const; - - gl::shader_program* unskinned_shader_program; - const gl::shader_input* unskinned_model_view_projection_input; - - gl::shader_program* skinned_shader_program; - const gl::shader_input* skinned_model_view_projection_input; - - float4x4 bias_tile_matrices[4]; -}; - -} // namespace render - -#endif // ANTKEEPER_RENDER_SHADOW_MAP_PASS_HPP diff --git a/src/render/passes/sky-pass.cpp b/src/render/passes/sky-pass.cpp deleted file mode 100644 index 459a8f2..0000000 --- a/src/render/passes/sky-pass.cpp +++ /dev/null @@ -1,665 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "render/passes/sky-pass.hpp" -#include "resources/resource-manager.hpp" -#include "gl/rasterizer.hpp" -#include "gl/framebuffer.hpp" -#include "gl/shader-program.hpp" -#include "gl/shader-input.hpp" -#include "gl/vertex-buffer.hpp" -#include "gl/vertex-array.hpp" -#include "gl/vertex-attribute.hpp" -#include "gl/drawing-mode.hpp" -#include "gl/texture-2d.hpp" -#include "gl/texture-wrapping.hpp" -#include "gl/texture-filter.hpp" -#include "render/vertex-attribute.hpp" -#include "render/context.hpp" -#include "render/model.hpp" -#include "render/material.hpp" -#include "scene/camera.hpp" -#include "utility/fundamental-types.hpp" -#include "color/color.hpp" -#include "math/interpolation.hpp" -#include "geom/cartesian.hpp" -#include "geom/spherical.hpp" -#include "physics/orbit/orbit.hpp" -#include "physics/light/photometry.hpp" -#include -#include -#include - -namespace render { - -sky_pass::sky_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager): - pass(rasterizer, framebuffer), - mouse_position({0.0f, 0.0f}), - sky_model(nullptr), - sky_material(nullptr), - sky_model_vao(nullptr), - sky_shader_program(nullptr), - moon_model(nullptr), - moon_model_vao(nullptr), - moon_material(nullptr), - moon_shader_program(nullptr), - stars_model(nullptr), - stars_model_vao(nullptr), - star_material(nullptr), - star_shader_program(nullptr), - observer_position_tween({0, 0, 0}, math::lerp), - sun_position_tween(float3{1.0f, 0.0f, 0.0f}, math::lerp), - sun_luminance_tween(float3{0.0f, 0.0f, 0.0f}, math::lerp), - sun_illuminance_tween(float3{0.0f, 0.0f, 0.0f}, math::lerp), - icrf_to_eus_translation({0, 0, 0}, math::lerp), - icrf_to_eus_rotation(math::quaternion::identity(), math::nlerp), - moon_position_tween(float3{0, 0, 0}, math::lerp), - moon_rotation_tween(math::quaternion::identity(), math::nlerp), - moon_angular_radius_tween(0.0f, math::lerp), - moon_sunlight_direction_tween(float3{0, 0, 0}, math::lerp), - moon_sunlight_illuminance_tween(float3{0, 0, 0}, math::lerp), - moon_planetlight_direction_tween(float3{0, 0, 0}, math::lerp), - moon_planetlight_illuminance_tween(float3{0, 0, 0}, math::lerp), - moon_illuminance_tween(float3{0.0f, 0.0f, 0.0f}, math::lerp), - render_transmittance_lut(false), - magnification(1.0f) -{ - // Build quad VBO and VAO - const float quad_vertex_data[] = - { - -1.0f, 1.0f, 0.0f, - -1.0f, -1.0f, 0.0f, - 1.0f, 1.0f, 0.0f, - 1.0f, 1.0f, 0.0f, - -1.0f, -1.0f, 0.0f, - 1.0f, -1.0f, 0.0f - }; - std::size_t quad_vertex_size = 3; - std::size_t quad_vertex_stride = sizeof(float) * quad_vertex_size; - std::size_t quad_vertex_count = 6; - quad_vbo = new gl::vertex_buffer(sizeof(float) * quad_vertex_size * quad_vertex_count, quad_vertex_data); - quad_vao = new gl::vertex_array(); - gl::vertex_attribute quad_position_attribute; - quad_position_attribute.buffer = quad_vbo; - quad_position_attribute.offset = 0; - quad_position_attribute.stride = quad_vertex_stride; - quad_position_attribute.type = gl::vertex_attribute_type::float_32; - quad_position_attribute.components = 3; - quad_vao->bind(render::vertex_attribute::position, quad_position_attribute); - - // Create transmittance LUT texture and framebuffer (32F color, no depth) - transmittance_lut_texture = new gl::texture_2d(256, 64, gl::pixel_type::float_32, gl::pixel_format::rgb); - transmittance_lut_texture->set_wrapping(gl::texture_wrapping::extend, gl::texture_wrapping::extend); - transmittance_lut_texture->set_filters(gl::texture_min_filter::linear, gl::texture_mag_filter::linear); - transmittance_lut_texture->set_max_anisotropy(0.0f); - transmittance_lut_framebuffer = new gl::framebuffer({transmittance_lut_texture->get_width(), transmittance_lut_texture->get_height()}); - transmittance_lut_framebuffer->attach(gl::framebuffer_attachment_type::color, transmittance_lut_texture); - transmittance_lut_resolution = {static_cast(transmittance_lut_texture->get_width()), static_cast(transmittance_lut_texture->get_height())}; - - // Load transmittance LUT shader template - transmittance_shader_template = resource_manager->load("transmittance-lut.glsl"); - - // Build transmittance LUT shader program - transmittance_shader_program = transmittance_shader_template->build(); - transmittance_atmosphere_radii_input = transmittance_shader_program->get_input("atmosphere_radii"); - transmittance_rayleigh_parameters_input = transmittance_shader_program->get_input("rayleigh_parameters"); - transmittance_mie_parameters_input = transmittance_shader_program->get_input("mie_parameters"); - transmittance_ozone_distribution_input = transmittance_shader_program->get_input("ozone_distribution"); - transmittance_ozone_absorption_input = transmittance_shader_program->get_input("ozone_absorption"); - transmittance_resolution_input = transmittance_shader_program->get_input("resolution"); - - // Create sky LUT texture and framebuffer (32F color, no depth) - int sky_lut_width = 200; - int sky_lut_height = 100; - sky_lut_resolution = {static_cast(sky_lut_width), static_cast(sky_lut_height)}; - sky_lut_texture = new gl::texture_2d(sky_lut_width, sky_lut_height, gl::pixel_type::float_32, gl::pixel_format::rgb); - sky_lut_texture->set_wrapping(gl::texture_wrapping::extend, gl::texture_wrapping::extend); - sky_lut_texture->set_filters(gl::texture_min_filter::linear, gl::texture_mag_filter::linear); - sky_lut_texture->set_max_anisotropy(0.0f); - sky_lut_framebuffer = new gl::framebuffer(sky_lut_width, sky_lut_height); - sky_lut_framebuffer->attach(gl::framebuffer_attachment_type::color, sky_lut_texture); - - // Load sky LUT shader template - sky_lut_shader_template = resource_manager->load("sky-illuminance-lut.glsl"); - - // Build sky LUT shader program - sky_lut_shader_program = sky_lut_shader_template->build(); - sky_lut_light_direction_input = sky_lut_shader_program->get_input("light_direction"); - sky_lut_light_illuminance_input = sky_lut_shader_program->get_input("light_illuminance"); - sky_lut_atmosphere_radii_input = sky_lut_shader_program->get_input("atmosphere_radii"); - sky_lut_observer_position_input = sky_lut_shader_program->get_input("observer_position"); - sky_lut_rayleigh_parameters_input = sky_lut_shader_program->get_input("rayleigh_parameters"); - sky_lut_mie_parameters_input = sky_lut_shader_program->get_input("mie_parameters"); - sky_lut_ozone_distribution_input = sky_lut_shader_program->get_input("ozone_distribution"); - sky_lut_ozone_absorption_input = sky_lut_shader_program->get_input("ozone_absorption"); - sky_lut_airglow_illuminance_input = sky_lut_shader_program->get_input("airglow_illuminance"); - sky_lut_resolution_input = sky_lut_shader_program->get_input("resolution"); - sky_lut_transmittance_lut_input = sky_lut_shader_program->get_input("transmittance_lut"); - sky_lut_transmittance_lut_resolution_input = sky_lut_shader_program->get_input("transmittance_lut_resolution"); -} - -sky_pass::~sky_pass() -{ - delete sky_lut_framebuffer; - delete sky_lut_texture; - delete transmittance_lut_framebuffer; - delete transmittance_lut_texture; - delete quad_vao; - delete quad_vbo; - - delete transmittance_shader_program; - delete sky_lut_shader_program; - - /// @TODO - // resource_maanger->unload("transmittance-lut.glsl"); - // resource_maanger->unload("sky-illuminance-lut.glsl"); -} - -void sky_pass::render(const render::context& ctx, render::queue& queue) const -{ - glDisable(GL_BLEND); - glDisable(GL_DEPTH_TEST); - glDepthMask(GL_FALSE); - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); - - // Render transmittance LUT if transmittance parameters have been altered. - if (render_transmittance_lut) - { - // Render transmittance LUT - rasterizer->set_viewport(0, 0, transmittance_lut_texture->get_width(), transmittance_lut_texture->get_height()); - rasterizer->use_framebuffer(*transmittance_lut_framebuffer); - rasterizer->use_program(*transmittance_shader_program); - transmittance_atmosphere_radii_input->upload(atmosphere_radii); - transmittance_rayleigh_parameters_input->upload(rayleigh_parameters); - transmittance_mie_parameters_input->upload(mie_parameters); - transmittance_ozone_distribution_input->upload(ozone_distribution); - transmittance_ozone_absorption_input->upload(ozone_absorption); - if (transmittance_resolution_input) - transmittance_resolution_input->upload(transmittance_lut_resolution); - rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6); - - // Don't render transmittance LUT next frame unless parameters have changed. - render_transmittance_lut = false; - } - - // Construct matrices - const scene::camera& camera = *ctx.camera; - float clip_near = camera.get_clip_near_tween().interpolate(ctx.alpha); - float clip_far = camera.get_clip_far_tween().interpolate(ctx.alpha); - float3 model_scale = float3{1.0f, 1.0f, 1.0f} * (clip_near + clip_far) * 0.5f; - float4x4 model = math::scale(math::matrix4::identity(), model_scale); - float4x4 view = float4x4(float3x3(ctx.view)); - float4x4 model_view = view * model; - const float4x4& projection = ctx.projection; - float4x4 view_projection = projection * view; - float4x4 model_view_projection = projection * model_view; - - // Interpolate observer position - float3 observer_position = observer_position_tween.interpolate(ctx.alpha); - - // Construct tweened ICRF to EUS transformation - math::transformation::se3 icrf_to_eus = - { - icrf_to_eus_translation.interpolate(ctx.alpha), - icrf_to_eus_rotation.interpolate(ctx.alpha) - }; - - // Get EUS direction to sun - float3 sun_position = sun_position_tween.interpolate(ctx.alpha); - float3 sun_direction = math::normalize(sun_position); - - // Interpolate and expose sun luminance and illuminance - float3 sun_illuminance = sun_illuminance_tween.interpolate(ctx.alpha) * ctx.exposure; - float3 sun_luminance = sun_luminance_tween.interpolate(ctx.alpha) * ctx.exposure; - - float3 moon_position = moon_position_tween.interpolate(ctx.alpha); - float3 moon_direction = math::normalize(moon_position); - float3 moon_illuminance = moon_illuminance_tween.interpolate(ctx.alpha) * ctx.exposure; - float moon_angular_radius = moon_angular_radius_tween.interpolate(ctx.alpha) * magnification; - - float sun_y = color::aces::ap1.luminance(sun_transmitted_illuminance); - float moon_y = color::aces::ap1.luminance(moon_transmitted_illuminance); - float3 dominant_light_direction = (sun_y > moon_y) ? sun_direction : moon_direction; - float3 dominant_light_illuminance = (sun_y > moon_y) ? sun_illuminance : moon_illuminance; - - if (moon_y > sun_y) - sun_luminance *= 0.0f; - - // Render sky illuminance LUT - auto sky_lut_viewport = sky_lut_framebuffer->get_dimensions(); - rasterizer->set_viewport(0, 0, std::get<0>(sky_lut_viewport), std::get<1>(sky_lut_viewport)); - rasterizer->use_framebuffer(*sky_lut_framebuffer); - rasterizer->use_program(*sky_lut_shader_program); - sky_lut_light_direction_input->upload(dominant_light_direction); - sky_lut_light_illuminance_input->upload(dominant_light_illuminance); - sky_lut_atmosphere_radii_input->upload(atmosphere_radii); - sky_lut_observer_position_input->upload(observer_position); - sky_lut_rayleigh_parameters_input->upload(rayleigh_parameters); - sky_lut_mie_parameters_input->upload(mie_parameters); - sky_lut_ozone_distribution_input->upload(ozone_distribution); - sky_lut_ozone_absorption_input->upload(ozone_absorption); - sky_lut_airglow_illuminance_input->upload(airglow_illuminance * ctx.exposure); - if (sky_lut_resolution_input) - sky_lut_resolution_input->upload(sky_lut_resolution); - sky_lut_transmittance_lut_input->upload(transmittance_lut_texture); - if (sky_lut_transmittance_lut_resolution_input) - sky_lut_transmittance_lut_resolution_input->upload(transmittance_lut_resolution); - rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6); - - - - rasterizer->use_framebuffer(*framebuffer); - auto viewport = framebuffer->get_dimensions(); - rasterizer->set_viewport(0, 0, std::get<0>(viewport), std::get<1>(viewport)); - float2 resolution = {static_cast(std::get<0>(viewport)), static_cast(std::get<1>(viewport))}; - - // Draw atmosphere - if (sky_model) - { - rasterizer->use_program(*sky_shader_program); - - // Upload shader parameters - if (model_view_projection_input) - model_view_projection_input->upload(model_view_projection); - if (mouse_input) - mouse_input->upload(mouse_position); - if (resolution_input) - resolution_input->upload(resolution); - if (light_direction_input) - light_direction_input->upload(dominant_light_direction); - if (sun_luminance_input) - sun_luminance_input->upload(sun_luminance); - if (sun_angular_radius_input) - sun_angular_radius_input->upload(sun_angular_radius * magnification); - if (atmosphere_radii_input) - atmosphere_radii_input->upload(atmosphere_radii); - if (observer_position_input) - observer_position_input->upload(observer_position); - if (sky_illuminance_lut_input) - sky_illuminance_lut_input->upload(sky_lut_texture); - if (sky_illuminance_lut_resolution_input) - sky_illuminance_lut_resolution_input->upload(sky_lut_resolution); - - sky_material->upload(ctx.alpha); - - rasterizer->draw_arrays(*sky_model_vao, sky_model_drawing_mode, sky_model_start_index, sky_model_index_count); - } - - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE); - //glBlendFunc(GL_ONE, GL_ONE); - - // Draw stars - if (stars_model) - { - float star_distance = (clip_near + clip_far) * 0.5f; - - model = float4x4(float3x3(icrf_to_eus.r)); - model = math::scale(model, {star_distance, star_distance, star_distance}); - - model_view = view * model; - - rasterizer->use_program(*star_shader_program); - if (star_model_view_input) - star_model_view_input->upload(model_view); - if (star_projection_input) - star_projection_input->upload(projection); - if (star_distance_input) - star_distance_input->upload(star_distance); - if (star_exposure_input) - star_exposure_input->upload(ctx.exposure); - - star_material->upload(ctx.alpha); - - rasterizer->draw_arrays(*stars_model_vao, stars_model_drawing_mode, stars_model_start_index, stars_model_index_count); - } - - // Draw moon model - //if (moon_position.y() >= -moon_angular_radius) - { - float moon_distance = (clip_near + clip_far) * 0.5f; - float moon_radius = moon_angular_radius * moon_distance; - - math::transform moon_transform; - moon_transform.translation = math::normalize(moon_position) * moon_distance; - moon_transform.rotation = moon_rotation_tween.interpolate(ctx.alpha); - moon_transform.scale = {moon_radius, moon_radius, moon_radius}; - - model = math::matrix_cast(moon_transform); - float3x3 normal_model = math::transpose(math::inverse(float3x3(model))); - - rasterizer->use_program(*moon_shader_program); - if (moon_model_input) - moon_model_input->upload(model); - if (moon_view_projection_input) - moon_view_projection_input->upload(view_projection); - if (moon_normal_model_input) - moon_normal_model_input->upload(normal_model); - if (moon_camera_position_input) - moon_camera_position_input->upload(ctx.camera_transform.translation); - if (moon_sunlight_direction_input) - moon_sunlight_direction_input->upload(math::normalize(moon_sunlight_direction_tween.interpolate(ctx.alpha))); - if (moon_sunlight_illuminance_input) - moon_sunlight_illuminance_input->upload(moon_sunlight_illuminance_tween.interpolate(ctx.alpha) * ctx.exposure); - if (moon_planetlight_direction_input) - moon_planetlight_direction_input->upload(math::normalize(moon_planetlight_direction_tween.interpolate(ctx.alpha))); - if (moon_planetlight_illuminance_input) - moon_planetlight_illuminance_input->upload(moon_planetlight_illuminance_tween.interpolate(ctx.alpha) * ctx.exposure); - - moon_material->upload(ctx.alpha); - rasterizer->draw_arrays(*moon_model_vao, moon_model_drawing_mode, moon_model_start_index, moon_model_index_count); - } -} - -void sky_pass::set_sky_model(const model* model) -{ - sky_model = model; - - if (sky_model) - { - sky_model_vao = model->get_vertex_array(); - - const std::vector& groups = *model->get_groups(); - for (model_group* group: groups) - { - sky_material = group->get_material(); - sky_model_drawing_mode = group->get_drawing_mode(); - sky_model_start_index = group->get_start_index(); - sky_model_index_count = group->get_index_count(); - } - - if (sky_material) - { - sky_shader_program = sky_material->get_shader_program(); - - if (sky_shader_program) - { - model_view_projection_input = sky_shader_program->get_input("model_view_projection"); - mouse_input = sky_shader_program->get_input("mouse"); - resolution_input = sky_shader_program->get_input("resolution"); - light_direction_input = sky_shader_program->get_input("light_direction"); - sun_luminance_input = sky_shader_program->get_input("sun_luminance"); - sun_angular_radius_input = sky_shader_program->get_input("sun_angular_radius"); - atmosphere_radii_input = sky_shader_program->get_input("atmosphere_radii"); - observer_position_input = sky_shader_program->get_input("observer_position"); - sky_illuminance_lut_input = sky_shader_program->get_input("sky_illuminance_lut"); - sky_illuminance_lut_resolution_input = sky_shader_program->get_input("sky_illuminance_lut_resolution"); - } - } - } - else - { - sky_model_vao = nullptr; - } -} - -void sky_pass::set_moon_model(const model* model) -{ - moon_model = model; - - if (moon_model) - { - moon_model_vao = model->get_vertex_array(); - - const std::vector& groups = *model->get_groups(); - for (model_group* group: groups) - { - moon_material = group->get_material(); - moon_model_drawing_mode = group->get_drawing_mode(); - moon_model_start_index = group->get_start_index(); - moon_model_index_count = group->get_index_count(); - } - - if (moon_material) - { - moon_shader_program = moon_material->get_shader_program(); - - if (moon_shader_program) - { - moon_model_input = moon_shader_program->get_input("model"); - moon_view_projection_input = moon_shader_program->get_input("view_projection"); - moon_normal_model_input = moon_shader_program->get_input("normal_model"); - moon_camera_position_input = moon_shader_program->get_input("camera_position"); - moon_sunlight_direction_input = moon_shader_program->get_input("sunlight_direction"); - moon_sunlight_illuminance_input = moon_shader_program->get_input("sunlight_illuminance"); - moon_planetlight_direction_input = moon_shader_program->get_input("planetlight_direction"); - moon_planetlight_illuminance_input = moon_shader_program->get_input("planetlight_illuminance"); - } - } - } - else - { - moon_model = nullptr; - } -} - -void sky_pass::set_stars_model(const model* model) -{ - stars_model = model; - - if (stars_model) - { - stars_model_vao = model->get_vertex_array(); - - const std::vector& groups = *model->get_groups(); - for (model_group* group: groups) - { - star_material = group->get_material(); - stars_model_drawing_mode = group->get_drawing_mode(); - stars_model_start_index = group->get_start_index(); - stars_model_index_count = group->get_index_count(); - } - - if (star_material) - { - star_shader_program = star_material->get_shader_program(); - - if (star_shader_program) - { - star_model_view_input = star_shader_program->get_input("model_view"); - star_projection_input = star_shader_program->get_input("projection"); - star_distance_input = star_shader_program->get_input("star_distance"); - star_exposure_input = star_shader_program->get_input("camera.exposure"); - } - } - } - else - { - stars_model = nullptr; - } -} - -void sky_pass::update_tweens() -{ - observer_position_tween.update(); - sun_position_tween.update(); - sun_luminance_tween.update(); - sun_illuminance_tween.update(); - icrf_to_eus_translation.update(); - icrf_to_eus_rotation.update(); - - moon_position_tween.update(); - moon_rotation_tween.update(); - moon_angular_radius_tween.update(); - moon_sunlight_direction_tween.update(); - moon_sunlight_illuminance_tween.update(); - moon_planetlight_direction_tween.update(); - moon_planetlight_illuminance_tween.update(); - moon_illuminance_tween.update(); -} - -void sky_pass::set_magnification(float magnification) -{ - this->magnification = magnification; -} - -void sky_pass::set_icrf_to_eus(const math::transformation::se3& transformation) -{ - icrf_to_eus_translation[1] = transformation.t; - icrf_to_eus_rotation[1] = transformation.r; -} - -void sky_pass::set_sun_position(const float3& position) -{ - sun_position_tween[1] = position; -} - -void sky_pass::set_sun_illuminance(const float3& illuminance, const float3& transmitted_illuminance) -{ - sun_illuminance_tween[1] = illuminance; - sun_transmitted_illuminance = transmitted_illuminance; -} - -void sky_pass::set_sun_luminance(const float3& luminance) -{ - sun_luminance_tween[1] = luminance; -} - -void sky_pass::set_sun_angular_radius(float radius) -{ - sun_angular_radius = radius; -} - -void sky_pass::set_planet_radius(float radius) -{ - atmosphere_radii.x() = radius; - atmosphere_radii.y() = atmosphere_radii.x() + atmosphere_upper_limit; - atmosphere_radii.z() = atmosphere_radii.y() * atmosphere_radii.y(); - observer_position_tween[1] = {0.0f, atmosphere_radii.x() + observer_elevation, 0.0f}; - - // Trigger transmittance LUT render - render_transmittance_lut = true; -} - -void sky_pass::set_atmosphere_upper_limit(float limit) -{ - atmosphere_upper_limit = limit; - atmosphere_radii.y() = atmosphere_radii.x() + atmosphere_upper_limit; - atmosphere_radii.z() = atmosphere_radii.y() * atmosphere_radii.y(); - - // Trigger transmittance LUT render - render_transmittance_lut = true; -} - -void sky_pass::set_observer_elevation(float elevation) -{ - observer_elevation = elevation; - observer_position_tween[1] = {0.0f, atmosphere_radii.x() + observer_elevation, 0.0f}; -} - -void sky_pass::set_rayleigh_parameters(float scale_height, const float3& scattering) -{ - rayleigh_parameters = - { - -1.0f / scale_height, - scattering.x(), - scattering.y(), - scattering.z() - }; - - // Trigger transmittance LUT render - render_transmittance_lut = true; -} - -void sky_pass::set_mie_parameters(float scale_height, float scattering, float extinction, float anisotropy) -{ - mie_parameters = - { - -1.0f / scale_height, - scattering, - extinction, - anisotropy - }; - - // Trigger transmittance LUT render - render_transmittance_lut = true; -} - -void sky_pass::set_ozone_parameters(float lower_limit, float upper_limit, float mode, const float3& absorption) -{ - ozone_distribution = - { - 1.0f / (lower_limit - mode), - 1.0f / (upper_limit - mode), - mode - }; - ozone_absorption = absorption; - - // Trigger transmittance LUT render - render_transmittance_lut = true; -} - -void sky_pass::set_airglow_illuminance(const float3& illuminance) -{ - airglow_illuminance = illuminance; -} - -void sky_pass::set_moon_position(const float3& position) -{ - moon_position_tween[1] = position; -} - -void sky_pass::set_moon_rotation(const math::quaternion& rotation) -{ - moon_rotation_tween[1] = rotation; -} - -void sky_pass::set_moon_angular_radius(float angular_radius) -{ - moon_angular_radius_tween[1] = angular_radius; -} - -void sky_pass::set_moon_sunlight_direction(const float3& direction) -{ - moon_sunlight_direction_tween[1] = direction; -} - -void sky_pass::set_moon_sunlight_illuminance(const float3& illuminance) -{ - moon_sunlight_illuminance_tween[1] = illuminance; -} - -void sky_pass::set_moon_planetlight_direction(const float3& direction) -{ - moon_planetlight_direction_tween[1] = direction; -} - -void sky_pass::set_moon_planetlight_illuminance(const float3& illuminance) -{ - moon_planetlight_illuminance_tween[1] = illuminance; -} - -void sky_pass::set_moon_illuminance(const float3& illuminance, const float3& transmitted_illuminance) -{ - moon_illuminance_tween[1] = illuminance; - moon_transmitted_illuminance = transmitted_illuminance; -} - -void sky_pass::set_transmittance_lut_resolution(std::uint16_t width, std::uint16_t height) -{ - transmittance_lut_texture->resize(width, height, nullptr); - transmittance_lut_framebuffer->resize({transmittance_lut_texture->get_width(), transmittance_lut_texture->get_height()}); - transmittance_lut_resolution = {static_cast(width), static_cast(height)}; - - // Trigger transmittance LUT render - render_transmittance_lut = true; -} - -} // namespace render diff --git a/src/render/passes/sky-pass.hpp b/src/render/passes/sky-pass.hpp deleted file mode 100644 index f3ea421..0000000 --- a/src/render/passes/sky-pass.hpp +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_RENDER_SKY_PASS_HPP -#define ANTKEEPER_RENDER_SKY_PASS_HPP - -#include "render/pass.hpp" -#include "render/shader-template.hpp" -#include "utility/fundamental-types.hpp" -#include "animation/tween.hpp" -#include "math/quaternion.hpp" -#include "gl/shader-program.hpp" -#include "gl/shader-input.hpp" -#include "gl/vertex-buffer.hpp" -#include "gl/vertex-array.hpp" -#include "gl/texture-2d.hpp" -#include "gl/drawing-mode.hpp" -#include "math/se3.hpp" -#include "scene/object.hpp" - -class resource_manager; - -namespace render { - -class material; -class model; - -/** - * - */ -class sky_pass: public pass -{ -public: - sky_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager); - virtual ~sky_pass(); - virtual void render(const render::context& ctx, render::queue& queue) const final; - - void update_tweens(); - - void set_magnification(float scale); - - void set_sky_model(const model* model); - void set_moon_model(const model* model); - void set_stars_model(const model* model); - - void set_icrf_to_eus(const math::transformation::se3& transformation); - - void set_sun_position(const float3& position); - void set_sun_luminance(const float3& luminance); - void set_sun_illuminance(const float3& illuminance, const float3& transmitted_illuminance); - void set_sun_angular_radius(float radius); - void set_planet_radius(float radius); - void set_atmosphere_upper_limit(float limit); - void set_observer_elevation(float elevation); - void set_rayleigh_parameters(float scale_height, const float3& scattering); - void set_mie_parameters(float scale_height, float scattering, float extinction, float anisotropy); - void set_ozone_parameters(float lower_limit, float upper_limit, float mode, const float3& absorption); - void set_airglow_illuminance(const float3& illuminance); - - void set_moon_position(const float3& position); - void set_moon_rotation(const math::quaternion& rotation); - void set_moon_angular_radius(float angular_radius); - void set_moon_sunlight_direction(const float3& direction); - void set_moon_sunlight_illuminance(const float3& illuminance); - void set_moon_planetlight_direction(const float3& direction); - void set_moon_planetlight_illuminance(const float3& illuminance); - void set_moon_illuminance(const float3& illuminance, const float3& transmitted_illuminance); - - /** - * Sets the resolution of transmittance LUT. - * - * @param width Transmittance LUT width, in pixels. - * @param height Transmittance LUT height, in pixels. - */ - void set_transmittance_lut_resolution(std::uint16_t width, std::uint16_t height); - -private: - gl::vertex_buffer* quad_vbo; - gl::vertex_array* quad_vao; - - gl::texture_2d* transmittance_lut_texture; - gl::framebuffer* transmittance_lut_framebuffer; - float2 transmittance_lut_resolution; - render::shader_template* transmittance_shader_template; - gl::shader_program* transmittance_shader_program; - const gl::shader_input* transmittance_atmosphere_radii_input; - const gl::shader_input* transmittance_rayleigh_parameters_input; - const gl::shader_input* transmittance_mie_parameters_input; - const gl::shader_input* transmittance_ozone_distribution_input; - const gl::shader_input* transmittance_ozone_absorption_input; - const gl::shader_input* transmittance_resolution_input; - mutable bool render_transmittance_lut; - - gl::texture_2d* sky_lut_texture; - gl::framebuffer* sky_lut_framebuffer; - render::shader_template* sky_lut_shader_template; - gl::shader_program* sky_lut_shader_program; - float2 sky_lut_resolution; - const gl::shader_input* sky_lut_light_direction_input; - const gl::shader_input* sky_lut_light_illuminance_input; - const gl::shader_input* sky_lut_atmosphere_radii_input; - const gl::shader_input* sky_lut_observer_position_input; - const gl::shader_input* sky_lut_rayleigh_parameters_input; - const gl::shader_input* sky_lut_mie_parameters_input; - const gl::shader_input* sky_lut_ozone_distribution_input; - const gl::shader_input* sky_lut_ozone_absorption_input; - const gl::shader_input* sky_lut_airglow_illuminance_input; - const gl::shader_input* sky_lut_resolution_input; - const gl::shader_input* sky_lut_transmittance_lut_input; - const gl::shader_input* sky_lut_transmittance_lut_resolution_input; - - gl::shader_program* sky_shader_program; - const gl::shader_input* model_view_projection_input; - const gl::shader_input* mouse_input; - const gl::shader_input* resolution_input; - const gl::shader_input* light_direction_input; - const gl::shader_input* sun_luminance_input; - const gl::shader_input* sun_angular_radius_input; - const gl::shader_input* atmosphere_radii_input; - const gl::shader_input* observer_position_input; - const gl::shader_input* sky_illuminance_lut_input; - const gl::shader_input* sky_illuminance_lut_resolution_input; - - gl::shader_program* moon_shader_program; - const gl::shader_input* moon_model_input; - const gl::shader_input* moon_view_projection_input; - const gl::shader_input* moon_normal_model_input; - const gl::shader_input* moon_camera_position_input; - const gl::shader_input* moon_sunlight_direction_input; - const gl::shader_input* moon_sunlight_illuminance_input; - const gl::shader_input* moon_planetlight_direction_input; - const gl::shader_input* moon_planetlight_illuminance_input; - - const model* sky_model; - const material* sky_material; - const gl::vertex_array* sky_model_vao; - gl::drawing_mode sky_model_drawing_mode; - std::size_t sky_model_start_index; - std::size_t sky_model_index_count; - - const model* moon_model; - const material* moon_material; - const gl::vertex_array* moon_model_vao; - gl::drawing_mode moon_model_drawing_mode; - std::size_t moon_model_start_index; - std::size_t moon_model_index_count; - - const model* stars_model; - const material* star_material; - const gl::vertex_array* stars_model_vao; - gl::drawing_mode stars_model_drawing_mode; - std::size_t stars_model_start_index; - std::size_t stars_model_index_count; - gl::shader_program* star_shader_program; - const gl::shader_input* star_model_view_input; - const gl::shader_input* star_projection_input; - const gl::shader_input* star_exposure_input; - const gl::shader_input* star_distance_input; - - float2 mouse_position; - - tween sun_position_tween; - tween sun_luminance_tween; - tween sun_illuminance_tween; - float3 sun_transmitted_illuminance; - tween icrf_to_eus_translation; - tween> icrf_to_eus_rotation; - - tween moon_position_tween; - tween> moon_rotation_tween; - tween moon_angular_radius_tween; - tween moon_sunlight_direction_tween; - tween moon_sunlight_illuminance_tween; - tween moon_planetlight_direction_tween; - tween moon_planetlight_illuminance_tween; - tween moon_illuminance_tween; - float3 moon_transmitted_illuminance; - - float sun_angular_radius; - float atmosphere_upper_limit; - float3 atmosphere_radii; - float observer_elevation; - tween observer_position_tween; - float4 rayleigh_parameters; - float4 mie_parameters; - float3 ozone_distribution; - float3 ozone_absorption; - float3 airglow_illuminance; - - float magnification; -}; - -} // namespace render - -#endif // ANTKEEPER_RENDER_SKY_PASS_HPP diff --git a/src/render/passes/ui-pass.cpp b/src/render/passes/ui-pass.cpp deleted file mode 100644 index d55ce9b..0000000 --- a/src/render/passes/ui-pass.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "render/passes/ui-pass.hpp" -#include "resources/resource-manager.hpp" -#include "gl/rasterizer.hpp" -#include "gl/framebuffer.hpp" -#include "gl/shader-program.hpp" -#include "gl/shader-input.hpp" -#include "gl/vertex-buffer.hpp" -#include "gl/vertex-array.hpp" -#include "gl/vertex-attribute.hpp" -#include "gl/drawing-mode.hpp" -#include "gl/texture-2d.hpp" -#include "gl/texture-wrapping.hpp" -#include "gl/texture-filter.hpp" -#include "render/vertex-attribute.hpp" -#include "render/material-flags.hpp" -#include "render/context.hpp" -#include "scene/camera.hpp" -#include "scene/collection.hpp" -#include "scene/ambient-light.hpp" -#include "scene/directional-light.hpp" -#include "scene/billboard.hpp" -#include -#include - -namespace render { - -ui_pass::ui_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager): - pass(rasterizer, framebuffer) -{} - -ui_pass::~ui_pass() -{} - -void ui_pass::render(const render::context& ctx, render::queue& queue) const -{ - glEnable(GL_BLEND); - glDisable(GL_DEPTH_TEST); - glDepthMask(GL_FALSE); - glCullFace(GL_BACK); - - auto viewport = framebuffer->get_dimensions(); - rasterizer->set_viewport(0, 0, std::get<0>(viewport), std::get<1>(viewport)); - - float4x4 view = ctx.camera->get_view_tween().interpolate(ctx.alpha); - float4x4 projection = ctx.camera->get_projection_tween().interpolate(ctx.alpha); - float4x4 view_projection = projection * view; - float4x4 model_view_projection; - - // Collect billboards - std::list billboards = *ctx.collection->get_objects(scene::billboard::object_type_id); - - // Sort billboards - - // Rebuild vertex buffer -} - -const ui_pass::parameter_set* ui_pass::load_parameter_set(const gl::shader_program* program) const -{ - // Allocate a new parameter set - parameter_set* parameters = new parameter_set(); - - // Connect inputs - parameters->time = program->get_input("time"); - parameters->model_view_projection = program->get_input("model_view_projection"); - - // Add parameter set to map of parameter sets - parameter_sets[program] = parameters; - - return parameters; -} - -} // namespace render diff --git a/src/render/passes/ui-pass.hpp b/src/render/passes/ui-pass.hpp deleted file mode 100644 index 378d9c6..0000000 --- a/src/render/passes/ui-pass.hpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_RENDER_UI_PASS_HPP -#define ANTKEEPER_RENDER_UI_PASS_HPP - -#include "render/pass.hpp" -#include "render/material.hpp" -#include "gl/shader-program.hpp" -#include "gl/shader-input.hpp" -#include "gl/texture-2d.hpp" -#include - -class resource_manager; - -namespace render { - -/** - * - */ -class ui_pass: public pass -{ -public: - ui_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager); - virtual ~ui_pass(); - virtual void render(const render::context& ctx, render::queue& queue) const final; - -private: - /** - * Sets of known shader input parameters. Each time a new shader is encountered, a parameter set will be created and its inputs connected to the shader program. A null input indiciates that the shader doesn't have that parameter. - */ - struct parameter_set - { - const gl::shader_input* time; - const gl::shader_input* model_view_projection; - }; - - const parameter_set* load_parameter_set(const gl::shader_program* program) const; - - mutable std::unordered_map parameter_sets; -}; - -} // namespace render - -#endif // ANTKEEPER_RENDER_UI_PASS_HPP diff --git a/src/render/queue.hpp b/src/render/queue.hpp deleted file mode 100644 index a06c77b..0000000 --- a/src/render/queue.hpp +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_RENDER_QUEUE_HPP -#define ANTKEEPER_RENDER_QUEUE_HPP - -#include "render/operation.hpp" -#include - -namespace render { - -/// Queue of render operations -typedef std::list queue; - -} // namespace render - -#endif // ANTKEEPER_RENDER_QUEUE_HPP diff --git a/src/render/renderer.cpp b/src/render/renderer.cpp deleted file mode 100644 index a11b8db..0000000 --- a/src/render/renderer.cpp +++ /dev/null @@ -1,247 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "render/renderer.hpp" -#include "render/context.hpp" -#include "render/compositor.hpp" -#include "scene/collection.hpp" -#include "scene/camera.hpp" -#include "scene/model-instance.hpp" -#include "scene/billboard.hpp" -#include "scene/lod-group.hpp" -#include "scene/text.hpp" -#include "render/model.hpp" -#include "gl/drawing-mode.hpp" -#include "math/matrix.hpp" -#include "geom/projection.hpp" -#include "config.hpp" -#include "math/quaternion.hpp" -#include "math/numbers.hpp" -#include -#include - -namespace render { - -renderer::renderer() -{ - // Setup billboard render operation - billboard_op.bone_count = 0; - billboard_op.skinning_palette = nullptr; - billboard_op.drawing_mode = gl::drawing_mode::triangles; - billboard_op.vertex_array = nullptr; - billboard_op.start_index = 0; - billboard_op.index_count = 6; - billboard_op.instance_count = 0; - - // Allocate skinning palette - skinning_palette = new float4x4[MATERIAL_PASS_MAX_BONE_COUNT]; - - // Construct culling stage - culling_stage = new render::culling_stage(); -} - -renderer::~renderer() -{ - delete[] skinning_palette; - delete culling_stage; -} - -void renderer::render(float t, float dt, float alpha, const scene::collection& collection) const -{ - // Get list of all objects in the collection - const std::list* objects = collection.get_objects(); - - // Build list of cameras to be sorted - const std::list* cameras = collection.get_objects(scene::camera::object_type_id); - std::list sorted_cameras; - for (scene::object_base* object: *cameras) - { - sorted_cameras.push_back(static_cast(object)); - } - - // Sort cameras according to their respective compositing indices - sorted_cameras.sort - ( - [](const scene::camera* a, const scene::camera* b) -> bool - { - return a->get_composite_index() < b->get_composite_index(); - } - ); - - // Init render context - render::context ctx; - ctx.collection = &collection; - ctx.t = t; - ctx.dt = dt; - ctx.alpha = alpha; - - // Process cameras in order - for (const scene::camera* camera: sorted_cameras) - { - // Skip inactive cameras - if (!camera->is_active()) - { - continue; - } - - // Skip cameras with no compositors - const compositor* compositor = camera->get_compositor(); - if (!compositor) - { - continue; - } - - // Update render context with camera parameters - ctx.camera = camera; - ctx.camera_transform = camera->get_transform_tween().interpolate(alpha); - ctx.camera_forward = ctx.camera_transform.rotation * config::global_forward; - ctx.camera_up = ctx.camera_transform.rotation * config::global_up; - ctx.clip_near = camera->get_view_frustum().get_near(); ///< @TODO: tween this - ctx.view = camera->get_view_tween().interpolate(alpha); - ctx.projection = camera->get_projection_tween().interpolate(alpha); - ctx.view_projection = ctx.projection * ctx.view; - ctx.exposure = std::exp2(-camera->get_exposure_tween().interpolate(alpha)); - - // Execute culling stage - culling_stage->execute(ctx); - - // Create render queue - render::queue queue; - - // Queue render operations for each visible scene object - for (const scene::object_base* object: ctx.visible_objects) - { - // Process object - process_object(ctx, queue, object); - } - - // Pass render context to the camera's compositor - compositor->composite(ctx, queue); - } -} - -void renderer::set_billboard_vao(gl::vertex_array* vao) -{ - billboard_op.vertex_array = vao; -} - -void renderer::process_object(const render::context& ctx, render::queue& queue, const scene::object_base* object) const -{ - std::size_t type = object->get_object_type_id(); - - if (type == scene::model_instance::object_type_id) - process_model_instance(ctx, queue, static_cast(object)); - else if (type == scene::billboard::object_type_id) - process_billboard(ctx, queue, static_cast(object)); - else if (type == scene::lod_group::object_type_id) - process_lod_group(ctx, queue, static_cast(object)); - else if (type == scene::text::object_type_id) - process_text(ctx, queue, static_cast(object)); -} - -void renderer::process_model_instance(const render::context& ctx, render::queue& queue, const scene::model_instance* model_instance) const -{ - const model* model = model_instance->get_model(); - if (!model) - return; - - const std::vector* instance_materials = model_instance->get_materials(); - const std::vector* groups = model->get_groups(); - - render::operation operation; - operation.transform = math::matrix_cast(model_instance->get_transform_tween().interpolate(ctx.alpha)); - operation.depth = ctx.clip_near.signed_distance(float3(operation.transform[3])); - operation.vertex_array = model->get_vertex_array(); - operation.instance_count = model_instance->get_instance_count(); - - // Skinning parameters - operation.bone_count = model_instance->get_pose().size(); - if (operation.bone_count) - { - operation.skinning_palette = skinning_palette; - ::matrix_palette(model->get_skeleton().inverse_bind_pose, model_instance->get_pose(), skinning_palette); - } - else - { - operation.skinning_palette = nullptr; - } - - for (model_group* group: *groups) - { - // Determine operation material - operation.material = group->get_material(); - if ((*instance_materials)[group->get_index()]) - { - // Override model group material with the instance's material - operation.material = (*instance_materials)[group->get_index()]; - } - - operation.drawing_mode = group->get_drawing_mode(); - operation.start_index = group->get_start_index(); - operation.index_count = group->get_index_count(); - - queue.push_back(operation); - } -} - -void renderer::process_billboard(const render::context& ctx, render::queue& queue, const scene::billboard* billboard) const -{ - math::transform billboard_transform = billboard->get_transform_tween().interpolate(ctx.alpha); - billboard_op.material = billboard->get_material(); - billboard_op.depth = ctx.clip_near.signed_distance(float3(billboard_transform.translation)); - - // Align billboard - if (billboard->get_billboard_type() == scene::billboard_type::spherical) - { - billboard_transform.rotation = math::normalize(math::look_rotation(ctx.camera_forward, ctx.camera_up) * billboard_transform.rotation); - } - else if (billboard->get_billboard_type() == scene::billboard_type::cylindrical) - { - const float3& alignment_axis = billboard->get_alignment_axis(); - float3 look = math::normalize(geom::project_on_plane(billboard_transform.translation - ctx.camera_transform.translation, {0.0f, 0.0f, 0.0f}, alignment_axis)); - float3 right = math::normalize(math::cross(alignment_axis, look)); - look = math::cross(right, alignment_axis); - float3 up = math::cross(look, right); - billboard_transform.rotation = math::normalize(math::look_rotation(look, up) * billboard_transform.rotation); - } - - billboard_op.transform = math::matrix_cast(billboard_transform); - - queue.push_back(billboard_op); -} - -void renderer::process_lod_group(const render::context& ctx, render::queue& queue, const scene::lod_group* lod_group) const -{ - // Select level of detail - std::size_t level = lod_group->select_lod(*ctx.camera); - - // Process all objects in the group with the selected level of detail - const std::list& objects = lod_group->get_objects(level); - for (const scene::object_base* object: objects) - { - process_object(ctx, queue, object); - } -} - -void renderer::process_text(const render::context& ctx, render::queue& queue, const scene::text* text) const -{ - text->render(ctx, queue); -} - -} // namespace render diff --git a/src/render/renderer.hpp b/src/render/renderer.hpp deleted file mode 100644 index 03b2f2b..0000000 --- a/src/render/renderer.hpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_RENDER_RENDERER_HPP -#define ANTKEEPER_RENDER_RENDERER_HPP - -#include "render/operation.hpp" -#include "render/context.hpp" -#include "render/queue.hpp" -#include "render/stage/culling-stage.hpp" -#include "gl/vertex-array.hpp" - -namespace scene -{ - class collection; - class object_base; - class model_instance; - class billboard; - class lod_group; - class text; -} - -namespace render { - -/** - * - */ -class renderer -{ -public: - renderer(); - ~renderer(); - - /** - * Renders a collection of scene objects. - * - * @param t Current time, in seconds. - * @param dt Timestep, in seconds. - * @param alpha Subframe interpolation factor. - * @param collection Collection of scene objects to render. - */ - void render(float t, float dt, float alpha, const scene::collection& collection) const; - - /** - * Sets the VAO to be used when generating render operations for billboards. - */ - void set_billboard_vao(gl::vertex_array* vao); - -private: - void process_object(const render::context& ctx, render::queue& queue, const scene::object_base* object) const; - void process_model_instance(const render::context& ctx, render::queue& queue, const scene::model_instance* model_instance) const; - void process_billboard(const render::context& ctx, render::queue& queue, const scene::billboard* billboard) const; - void process_lod_group(const render::context& ctx, render::queue& queue, const scene::lod_group* lod_group) const; - void process_text(const render::context& ctx, render::queue& queue, const scene::text* text) const; - - mutable render::operation billboard_op; - float4x4* skinning_palette; - - render::culling_stage* culling_stage; -}; - -} // namespace render - -#endif // ANTKEEPER_RENDER_RENDERER_HPP diff --git a/src/render/shader-template.cpp b/src/render/shader-template.cpp deleted file mode 100644 index 23254cf..0000000 --- a/src/render/shader-template.cpp +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "render/shader-template.hpp" -#include -#include - -namespace render { - -shader_template::shader_template(const std::string& source_code) -{ - source(source_code); -} - -shader_template::shader_template(): - hash(std::hash{}(std::string())) -{} - -void shader_template::source(const std::string& source) -{ - // Reset template - template_source.clear(); - vertex_directives.clear(); - fragment_directives.clear(); - geometry_directives.clear(); - define_directives.clear(); - - // Iterate through source line-by-line - std::istringstream source_stream(source); - std::string line; - while (std::getline(source_stream, line)) - { - std::string token; - std::istringstream line_stream(line); - - // Detect `#pragma` directives - if (line_stream >> token && token == "#pragma") - { - if (line_stream >> token) - { - // Map line numbers of supported directives - if (token == "define") - { - if (line_stream >> token) - define_directives.insert({token, template_source.size()}); - } - else if (token == "vertex") - vertex_directives.insert(template_source.size()); - else if (token == "fragment") - fragment_directives.insert(template_source.size()); - else if (token == "geometry") - geometry_directives.insert(template_source.size()); - } - } - - // Append line to template source - template_source.push_back(line); - } - - // Calculate hash of source - hash = std::hash{}(source); -} - -std::string shader_template::configure(gl::shader_stage stage, const dictionary_type& definitions) const -{ - replace_stage_directives(stage); - replace_define_directives(definitions); - - // Join vector of source lines into single string - std::ostringstream stream; - std::copy(template_source.begin(), template_source.end(), std::ostream_iterator(stream, "\n")); - return stream.str(); -} - -gl::shader_object* shader_template::compile(gl::shader_stage stage, const dictionary_type& definitions) const -{ - // Generate shader object source - std::string object_source = configure(stage, definitions); - - // Create new shader object - gl::shader_object* object = new gl::shader_object(stage); - - // Set shader object source - object->source(object_source); - - // Compile shader object - object->compile(); - - return object; -} - -gl::shader_program* shader_template::build(const dictionary_type& definitions) const -{ - gl::shader_object* vertex_object = nullptr; - gl::shader_object* fragment_object = nullptr; - gl::shader_object* geometry_object = nullptr; - - // Create shader program - gl::shader_program* program = new gl::shader_program(); - - if (has_vertex_directive()) - { - // Compile vertex shader object and attach to shader program - vertex_object = compile(gl::shader_stage::vertex, definitions); - program->attach(vertex_object); - } - - if (has_fragment_directive()) - { - // Compile fragment shader object and attach to shader program - fragment_object = compile(gl::shader_stage::fragment, definitions); - program->attach(fragment_object); - } - - if (has_geometry_directive()) - { - // Compile fragment shader object and attach to shader program - geometry_object = compile(gl::shader_stage::geometry, definitions); - program->attach(geometry_object); - } - - // Link attached shader objects into shader program - program->link(); - - if (vertex_object) - { - // Detach and delete vertex shader object - program->detach(vertex_object); - delete vertex_object; - } - - if (fragment_object) - { - // Detach and delete fragment shader object - program->detach(fragment_object); - delete fragment_object; - } - - if (geometry_object) - { - // Detach and delete geometry shader object - program->detach(geometry_object); - delete geometry_object; - } - - return program; -} - -void shader_template::replace_stage_directives(gl::shader_stage stage) const -{ - // Determine stage directives according to the shader stage being generated - const std::string vertex_directive = (stage == gl::shader_stage::vertex) ? "#define __VERTEX__" : "/* #undef __VERTEX__ */"; - const std::string fragment_directive = (stage == gl::shader_stage::fragment) ? "#define __FRAGMENT__" : "/* #undef __FRAGMENT__ */"; - const std::string geometry_directive = (stage == gl::shader_stage::geometry) ? "#define __GEOMETRY__" : "/* #undef __GEOMETRY__ */"; - - // Handle `#pragma ` directives - for (std::size_t i: vertex_directives) - template_source[i] = vertex_directive; - for (std::size_t i: fragment_directives) - template_source[i] = fragment_directive; - for (std::size_t i: geometry_directives) - template_source[i] = geometry_directive; -} - -void shader_template::replace_define_directives(const dictionary_type& definitions) const -{ - // For each `#pragma define ` directive - for (const auto& define_directive: define_directives) - { - // Get a reference to the directive line - std::string& line = template_source[define_directive.second]; - - // Check if the corresponding definition was given by the configuration - auto definitions_it = definitions.find(define_directive.first); - if (definitions_it != definitions.end()) - { - // Definition found, replace `#pragma define ` with `#define ` or `#define ` - line = "#define " + define_directive.first; - if (!definitions_it->second.empty()) - line += " " + definitions_it->second; - } - else - { - // Definition not found, replace `#pragma define ` with the comment `/* #undef */`. - line = "/* #undef " + define_directive.first + " */"; - } - } -} - -bool shader_template::has_vertex_directive() const -{ - return !vertex_directives.empty(); -} - -bool shader_template::has_fragment_directive() const -{ - return !fragment_directives.empty(); -} - -bool shader_template::has_geometry_directive() const -{ - return !geometry_directives.empty(); -} - -bool shader_template::has_define_directive(const std::string& key) const -{ - return (define_directives.find(key) != define_directives.end()); -} - -} // namespace render diff --git a/src/render/shader-template.hpp b/src/render/shader-template.hpp deleted file mode 100644 index ded26bc..0000000 --- a/src/render/shader-template.hpp +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_RENDER_SHADER_TEMPLATE_HPP -#define ANTKEEPER_RENDER_SHADER_TEMPLATE_HPP - -#include "gl/shader-object.hpp" -#include "gl/shader-program.hpp" -#include -#include -#include -#include - -namespace render { - -/** - * Template used to for generating one or more shader variants from a single source. - * - * Shader templates support the following preprocessor directives: - * - * * `#pragma vertex`: Replaced with `#define __VERTEX__` when generating vertex shader objects. - * * `#pragma fragment`: Replaced with `#define __FRAGMENT__` when generating fragment shader objects. - * * `#pragma geometry`: Replaced with `#define __GEOMETRY__` when generating geometry shader objects. - * * `#pragma define `: Will be replaced with `#define ` if its definition is passed to the shader template. - * - * @see gl::shader_stage - * @see gl::shader_object - * @see gl::shader_program - */ -class shader_template -{ -public: - /// Container of definitions used to generate `#pragma define ` directives. - typedef std::unordered_map dictionary_type; - - /** - * Constructs a shader template and sets its source code. - * - * @param source_code String containing the shader template source code. - * - * @see shader_template::source(const std::string&) - */ - shader_template(const std::string& source_code); - - /** - * Constructs an empty shader template. - */ - shader_template(); - - /** - * Replaces the source code of the shader template. - * - * @param source_code String containing shader template source code. - */ - void source(const std::string& source_code); - - /** - * Configures shader object source code given a shader stage and template dictionary. - * - * @param stage Shader stage of the shader object to generate. Instances of `#pragma ` in the template source will be replaced with `#define ____`. - * @param definitions Container of definitions used to replace `#pragma define ` directives. - * @return Configured shader object source code. - */ - std::string configure(gl::shader_stage stage, const dictionary_type& definitions = {}) const; - - /** - * Configures and compiles a shader object. - * - * @param stage Shader stage of the shader object to generate. Instances of `#pragma ` in the template source will be replaced with `#define ____`. - * @param definitions Container of definitions used to replace `#pragma define ` directives. - * @return Compiled shader object. - * - * @exception std::runtime_error Any exceptions thrown by gl::shader_object. - */ - gl::shader_object* compile(gl::shader_stage stage, const dictionary_type& definitions = {}) const; - - /** - * Configures and compiles shader objects, then links them into a shader program. Shader object stages are determined according to the presence of `#pragma ` directives. - * - * @param definitions Container of definitions used to replace `#pragma define ` directives. - * @return Linked shader program. - * - * @exception std::runtime_error Any exceptions thrown by gl::shader_object or gl::shader_program. - * - * @see has_vertex_directive() const - * @see has_fragment_directive() const - * @see has_geometry_directive() const - */ - gl::shader_program* build(const dictionary_type& definitions = {}) const; - - /// Returns `true` if the template source contains one or more `#pragma vertex` directive. - bool has_vertex_directive() const; - - /// Returns `true` if the template source contains one or more `#pragma fragment` directive. - bool has_fragment_directive() const; - - /// Returns `true` if the template source contains one or more `#pragma geometry` directive. - bool has_geometry_directive() const; - - /** - * Returns `true` if the template source contains one or more instance of `#pragma define `. - * - * @param key Definition key. - */ - bool has_define_directive(const std::string& key) const; - - /// Returns a hash of the template source. - std::size_t get_hash() const; - -private: - void replace_stage_directives(gl::shader_stage stage) const; - void replace_define_directives(const dictionary_type& definitions) const; - - mutable std::vector template_source; - std::unordered_set vertex_directives; - std::unordered_set fragment_directives; - std::unordered_set geometry_directives; - std::multimap define_directives; - std::size_t hash; -}; - -inline std::size_t shader_template::get_hash() const -{ - return hash; -} - -} // namespace render - -#endif // ANTKEEPER_RENDER_SHADER_TEMPLATE_HPP diff --git a/src/render/stage.cpp b/src/render/stage.cpp deleted file mode 100644 index 74ec45b..0000000 --- a/src/render/stage.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "render/stage.hpp" - -namespace render { - -stage::stage(): - priority(0) -{} - -} // namespace render diff --git a/src/render/stage.hpp b/src/render/stage.hpp deleted file mode 100644 index 4c3248b..0000000 --- a/src/render/stage.hpp +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_RENDER_STAGE_HPP -#define ANTKEEPER_RENDER_STAGE_HPP - -#include "render/context.hpp" - -namespace render { - -/** - * Abstract base class for a single stage in a render pipeline. - */ -class stage -{ -public: - /** - * Constructs a render stage and sets it priority. - * - * @param priority Stage execution order priority. - */ - stage(int priority); - - /// Constructs a render stage. - stage(); - - /// Destructs a render stage. - virtual ~stage() = default; - - /** - * Executes the render stage. - * - * @param ctx Render context. - */ - virtual void execute(render::context& ctx) const = 0; - - /** - * Sets the priority of the stage's execution order in the render pipeline. - * - * @param priority Stage execution order priority. Stages with lower priorities are executed first. - */ - void set_priority(int priority); - - /// Returns the priority of the stage's execution order in the render pipeline. - int get_priority() const noexcept; - -private: - int priority; -}; - -inline int stage::get_priority() const noexcept -{ - return priority; -} - -} // namespace render - -#endif // ANTKEEPER_RENDER_STAGE_HPP diff --git a/src/render/stage/culling-stage.cpp b/src/render/stage/culling-stage.cpp deleted file mode 100644 index 67ae1ae..0000000 --- a/src/render/stage/culling-stage.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "render/stage/culling-stage.hpp" -#include "scene/camera.hpp" -#include "scene/collection.hpp" -#include -#include -#include - -namespace render { - -void culling_stage::execute(render::context& ctx) const -{ - // Get list of all objects in the collection - const std::list& objects = *(ctx.collection->get_objects()); - - // Get camera culling volume - ctx.camera_culling_volume = ctx.camera->get_culling_mask(); - if (!ctx.camera_culling_volume) - ctx.camera_culling_volume = &ctx.camera->get_world_bounds(); - - // Clear set of visible objects - ctx.visible_objects.clear(); - - // Construct mutex to guard set of visible objects - std::mutex mutex; - - // For each object in the scene collection - std::for_each - ( - std::execution::par, - std::begin(objects), - std::end(objects), - [&](scene::object_base* object) - { - // Ignore inactive objects and cameras - if (!object->is_active() || object->get_object_type_id() == scene::camera::object_type_id) - return; - - // Cull object if it doesn't share any common layers with the camera - //if (!(object->get_layer_mask() & camera_layer_mask)) - // return; - - // Get object culling volume - const geom::bounding_volume* object_culling_volume = object->get_culling_mask(); - if (!object_culling_volume) - object_culling_volume = &object->get_world_bounds(); - - // Cull object if it's outside of the camera culling volume - if (!ctx.camera_culling_volume->intersects(*object_culling_volume)) - return; - - // Insert object into set of visible objects - std::lock_guard guard(mutex); - ctx.visible_objects.push_back(object); - } - ); -} - -} // namespace render diff --git a/src/render/stage/culling-stage.hpp b/src/render/stage/culling-stage.hpp deleted file mode 100644 index 3a95ed8..0000000 --- a/src/render/stage/culling-stage.hpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_RENDER_CULLING_STAGE_HPP -#define ANTKEEPER_RENDER_CULLING_STAGE_HPP - -#include "render/stage.hpp" - -namespace render { - -/** - * Builds a set of scene objects visible to the current camera and stores it in the render context. - */ -class culling_stage: public stage -{ -public: - /// Constructs a culling stage. - culling_stage() = default; - - /// Destructs a culling stage. - virtual ~culling_stage() = default; - - /// @copydoc render::stage::execute(render::context&) - virtual void execute(render::context& ctx) const final; -}; - -} // namespace render - -#endif // ANTKEEPER_RENDER_CULLING_STAGE_HPP diff --git a/src/resources/behavior-tree-loader.cpp b/src/resources/behavior-tree-loader.cpp deleted file mode 100644 index c80ebec..0000000 --- a/src/resources/behavior-tree-loader.cpp +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "resource-loader.hpp" -#include "resource-manager.hpp" -#include "entity/ebt.hpp" -#include -#include -#include -#include -#include -#include -#include - -/* - -template -void parse_argument(T& value, const std::string& string) -{ - std::istringstream stream(string); - stream >> value; -} - -template <> -void parse_argument(std::string& value, const std::string& string) -{ - value = string; -} - -template -std::function pack_function(T (*function)(entity::ebt::context&, Args...), std::list argv) -{ - //if (argv.size() != sizeof...(Args)) - - // Parse list of strings into tuple of arguments - std::tuple...> arguments; - if constexpr (sizeof...(Args) > 0) - { - std::apply([&argv](auto& element, auto&... elements) - { - parse_argument(element, argv.front()); - argv.pop_front(); - }, - arguments); - } - - return std::bind( - [function, arguments](entity::ebt::context& context) -> entity::ebt::status - { - return std::apply(function, std::tuple_cat(std::make_tuple(context), arguments)); - }, - std::placeholders::_1); -} - -static entity::ebt::node* load_node(const nlohmann::json::const_iterator& json, resource_manager* resource_manager); -static void load_node_child(entity::ebt::decorator_node* node, const nlohmann::json& json, resource_manager* resource_manager); -static void load_node_children(entity::ebt::composite_node* node, const nlohmann::json& json, resource_manager* resource_manager); - -static entity::ebt::node* load_action_node(const nlohmann::json& json, resource_manager* resource_manager) -{ - // Get function name - auto function_it = json.find("function"); - if (function_it == json.end()) - throw std::runtime_error("load_action_node(): Action node has no function."); - std::string function_name = function_it.value().get(); - - // Get argument vector - std::list arguments; - auto arguments_it = json.find("arguments"); - for (auto it = arguments_it.value().cbegin(); it != arguments_it.value().cend(); ++it) - arguments.push_back(it.value().get()); - - entity::ebt::action* action_node = new entity::ebt::action(); - if (function_name == "print") action_node->function = pack_function(entity::ebt::print, arguments); - else if (function_name == "print_eid") action_node->function = pack_function(entity::ebt::print_eid, arguments); - else if (function_name == "warp_to") action_node->function = pack_function(entity::ebt::warp_to, arguments); - - return action_node; -} - -static entity::ebt::node* load_selector_node(const nlohmann::json& json, resource_manager* resource_manager) -{ - entity::ebt::selector* selector_node = new entity::ebt::selector(); - load_node_children(selector_node, json, resource_manager); - return selector_node; -} - -static entity::ebt::node* load_sequence_node(const nlohmann::json& json, resource_manager* resource_manager) -{ - entity::ebt::sequence* sequence_node = new entity::ebt::sequence(); - load_node_children(sequence_node, json, resource_manager); - return sequence_node; -} - -static entity::ebt::node* load_node(const nlohmann::json::const_iterator& json, resource_manager* resource_manager) -{ - static const std::map> node_loaders = - { - {"action", &load_action_node}, - {"selector", &load_selector_node}, - {"sequence", &load_sequence_node} - }; - - auto node_loader = node_loaders.find(json.key()); - if (node_loader == node_loaders.end()) - { - throw std::runtime_error("load_node(): Unknown behavior tree node type \"" + json.key() + "\""); - } - - return node_loader->second(json.value(), resource_manager); -} - -static void load_node_child(entity::ebt::decorator_node* node, const nlohmann::json& json, resource_manager* resource_manager) -{ - auto it = json.find("child"); - node->child = load_node(it.value().cbegin(), resource_manager); -} - -static void load_node_children(entity::ebt::composite_node* node, const nlohmann::json& json, resource_manager* resource_manager) -{ - auto children_it = json.find("children"); - for (auto it = children_it.value().cbegin(); it != children_it.value().cend(); ++it) - { - entity::ebt::node* child = load_node(it.value().begin(), resource_manager); - node->children.push_back(child); - } -} - -template <> -entity::ebt::node* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) -{ - // Read file into buffer - std::size_t size = static_cast(PHYSFS_fileLength(file)); - std::string buffer; - buffer.resize(size); - PHYSFS_readBytes(file, &buffer[0], size); - - // Parse json from file buffer - nlohmann::json json = nlohmann::json::parse(buffer); - - if (json.size() != 1) - { - throw std::runtime_error("resource_loader::load(): Behavior tree must have exactly one root node."); - } - - return load_node(json.cbegin(), resource_manager); -} -*/ diff --git a/src/resources/control-profile-loader.cpp b/src/resources/control-profile-loader.cpp deleted file mode 100644 index 38cd58d..0000000 --- a/src/resources/control-profile-loader.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "resources/resource-loader.hpp" -#include "resources/serializer.hpp" -#include "resources/deserializer.hpp" -#include "game/control-profile.hpp" - -template <> -game::control_profile* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) -{ - game::control_profile* profile = new game::control_profile(); - - deserialize_context ctx(file); - deserializer().deserialize(*profile, ctx); - - return profile; -} - -template <> -void resource_loader::save(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path, const game::control_profile* profile) -{ - serialize_context ctx(file); - serializer().serialize(*profile, ctx); -} diff --git a/src/resources/deserialize-context.cpp b/src/resources/deserialize-context.cpp deleted file mode 100644 index d22c62f..0000000 --- a/src/resources/deserialize-context.cpp +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "resources/deserialize-context.hpp" -#include "resources/deserialize-error.hpp" -#include - -deserialize_context::deserialize_context(void* handle): - handle(handle), - m_eof(false), - m_error(false) -{} - -std::size_t deserialize_context::read8(std::byte* data, std::size_t count) -{ - const PHYSFS_sint64 status = PHYSFS_readBytes(reinterpret_cast(handle), data, count); - - if (status < 0) - { - m_error = true; - throw deserialize_error(PHYSFS_getLastError()); - //return 0; - } - - if (status != count) - { - m_eof = true; - m_error = true; - throw deserialize_error(PHYSFS_getLastError()); - //return static_cast(count); - } - - return count; -} - -std::size_t deserialize_context::read16_le(std::byte* data, std::size_t count) -{ - PHYSFS_File* file = reinterpret_cast(handle); - PHYSFS_uint16* data16 = reinterpret_cast(data); - - for (std::size_t i = 0; i < count; ++i) - { - if (!PHYSFS_readULE16(file, data16)) - { - m_eof = (PHYSFS_eof(file) != 0); - m_error = true; - throw deserialize_error(PHYSFS_getLastError()); - //return i; - } - - ++data16; - } - - return count; -} - -std::size_t deserialize_context::read16_be(std::byte* data, std::size_t count) -{ - PHYSFS_File* file = reinterpret_cast(handle); - PHYSFS_uint16* data16 = reinterpret_cast(data); - - for (std::size_t i = 0; i < count; ++i) - { - if (!PHYSFS_readUBE16(file, data16)) - { - m_eof = (PHYSFS_eof(file) != 0); - m_error = true; - throw deserialize_error(PHYSFS_getLastError()); - //return i; - } - - ++data16; - } - - return count; -} - -std::size_t deserialize_context::read32_le(std::byte* data, std::size_t count) -{ - PHYSFS_File* file = reinterpret_cast(handle); - PHYSFS_uint32* data32 = reinterpret_cast(data); - - for (std::size_t i = 0; i < count; ++i) - { - if (!PHYSFS_readULE32(file, data32)) - { - m_eof = (PHYSFS_eof(file) != 0); - m_error = true; - throw deserialize_error(PHYSFS_getLastError()); - //return i; - } - - ++data32; - } - - return count; -} - -std::size_t deserialize_context::read32_be(std::byte* data, std::size_t count) -{ - PHYSFS_File* file = reinterpret_cast(handle); - PHYSFS_uint32* data32 = reinterpret_cast(data); - - for (std::size_t i = 0; i < count; ++i) - { - if (!PHYSFS_readUBE32(file, data32)) - { - m_eof = (PHYSFS_eof(file) != 0); - m_error = true; - throw deserialize_error(PHYSFS_getLastError()); - //return i; - } - - ++data32; - } - - return count; -} - -std::size_t deserialize_context::read64_le(std::byte* data, std::size_t count) -{ - PHYSFS_File* file = reinterpret_cast(handle); - PHYSFS_uint64* data64 = reinterpret_cast(data); - - for (std::size_t i = 0; i < count; ++i) - { - if (!PHYSFS_readULE64(file, data64)) - { - m_eof = (PHYSFS_eof(file) != 0); - m_error = true; - throw deserialize_error(PHYSFS_getLastError()); - //return i; - } - - ++data64; - } - - return count; -} - -std::size_t deserialize_context::read64_be(std::byte* data, std::size_t count) -{ - PHYSFS_File* file = reinterpret_cast(handle); - PHYSFS_uint64* data64 = reinterpret_cast(data); - - for (std::size_t i = 0; i < count; ++i) - { - if (!PHYSFS_readUBE64(file, data64)) - { - m_eof = (PHYSFS_eof(file) != 0); - m_error = true; - throw deserialize_error(PHYSFS_getLastError()); - //return i; - } - - ++data64; - } - - return count; -} diff --git a/src/resources/deserializer.cpp b/src/resources/deserializer.cpp deleted file mode 100644 index b56111c..0000000 --- a/src/resources/deserializer.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "resources/deserializer.hpp" -#include - -template <> -void deserializer::deserialize(bool& value, deserialize_context& ctx) -{ - std::uint8_t temp; - ctx.read8(reinterpret_cast(&temp), 1); - value = (temp) ? true : false; -}; - -template <> -void deserializer::deserialize(std::uint8_t& value, deserialize_context& ctx) -{ - ctx.read8(reinterpret_cast(&value), 1); -}; - -template <> -void deserializer::deserialize(std::uint16_t& value, deserialize_context& ctx) -{ - ctx.read16(reinterpret_cast(&value), 1); -}; - -template <> -void deserializer::deserialize(std::uint32_t& value, deserialize_context& ctx) -{ - ctx.read32(reinterpret_cast(&value), 1); -}; - -template <> -void deserializer::deserialize(std::uint64_t& value, deserialize_context& ctx) -{ - ctx.read64(reinterpret_cast(&value), 1); -}; - -template <> -void deserializer::deserialize(std::int8_t& value, deserialize_context& ctx) -{ - ctx.read8(reinterpret_cast(&value), 1); -}; - -template <> -void deserializer::deserialize(std::int16_t& value, deserialize_context& ctx) -{ - ctx.read16(reinterpret_cast(&value), 1); -}; - -template <> -void deserializer::deserialize(std::int32_t& value, deserialize_context& ctx) -{ - ctx.read32(reinterpret_cast(&value), 1); -}; - -template <> -void deserializer::deserialize(std::int64_t& value, deserialize_context& ctx) -{ - ctx.read64(reinterpret_cast(&value), 1); -}; - -template <> -void deserializer::deserialize(float& value, deserialize_context& ctx) -{ - ctx.read32(reinterpret_cast(&value), 1); -}; - -template <> -void deserializer::deserialize(double& value, deserialize_context& ctx) -{ - ctx.read64(reinterpret_cast(&value), 1); -}; - -template <> -void deserializer::deserialize(std::string& value, deserialize_context& ctx) -{ - std::uint64_t length = 0; - ctx.read64(reinterpret_cast(&length), 1); - value.resize(static_cast(length)); - ctx.read8(reinterpret_cast(value.data()), static_cast(length)); -}; - -template <> -void deserializer::deserialize(std::u8string& value, deserialize_context& ctx) -{ - std::uint64_t length = 0; - ctx.read64(reinterpret_cast(&length), 1); - value.resize(static_cast(length)); - ctx.read8(reinterpret_cast(value.data()), static_cast(length)); -}; - -template <> -void deserializer::deserialize(std::u16string& value, deserialize_context& ctx) -{ - std::uint64_t length = 0; - ctx.read64(reinterpret_cast(&length), 1); - value.resize(static_cast(length)); - ctx.read16(reinterpret_cast(value.data()), static_cast(length)); -}; - -template <> -void deserializer::deserialize(std::u32string& value, deserialize_context& ctx) -{ - std::uint64_t length = 0; - ctx.read64(reinterpret_cast(&length), 1); - value.resize(static_cast(length)); - ctx.read32(reinterpret_cast(value.data()), static_cast(length)); -}; diff --git a/src/resources/deserializer.hpp b/src/resources/deserializer.hpp deleted file mode 100644 index 6d013f9..0000000 --- a/src/resources/deserializer.hpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_RESOURCES_DESERIALIZER_HPP -#define ANTKEEPER_RESOURCES_DESERIALIZER_HPP - -#include "resources/deserialize-context.hpp" - -/** - * Specializations of deserializer define the deserialization process for a given type. - * - * @tparam T Deserializable type. - */ -template -struct deserializer -{ - /** - * Deserializes a value. - * - * @param value Value to deserialize. - * @param ctx Deserialize context. - */ - void deserialize(T& value, deserialize_context& ctx); -}; - -#endif // ANTKEEPER_RESOURCES_DESERIALIZER_HPP diff --git a/src/resources/dict-loader.cpp b/src/resources/dict-loader.cpp deleted file mode 100644 index ac3f614..0000000 --- a/src/resources/dict-loader.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "resources/resource-loader.hpp" -#include "resources/serializer.hpp" -#include "resources/deserializer.hpp" -#include "utility/dict.hpp" -#include -#include - -template <> -dict* resource_loader>::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) -{ - dict* dict = new ::dict(); - - deserialize_context ctx(file); - deserializer<::dict>().deserialize(*dict, ctx); - - return dict; -} - -template <> -void resource_loader>::save(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path, const dict* dict) -{ - serialize_context ctx(file); - serializer<::dict>().serialize(*dict, ctx); -} diff --git a/src/resources/entity-archetype-loader.cpp b/src/resources/entity-archetype-loader.cpp deleted file mode 100644 index 031985a..0000000 --- a/src/resources/entity-archetype-loader.cpp +++ /dev/null @@ -1,358 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "resource-loader.hpp" -#include "resource-manager.hpp" -#include "render/model.hpp" -#include "game/component/atmosphere.hpp" -#include "game/component/behavior.hpp" -#include "game/component/collision.hpp" -#include "game/component/diffuse-reflector.hpp" -#include "game/component/terrain.hpp" -#include "game/component/transform.hpp" -#include "game/component/model.hpp" -#include "game/component/orbit.hpp" -#include "game/component/blackbody.hpp" -#include "game/component/celestial-body.hpp" -#include "entity/archetype.hpp" -#include "entity/ebt.hpp" -#include "physics/orbit/elements.hpp" -#include "resources/json.hpp" -#include - -static bool load_component_atmosphere(entity::archetype& archetype, const json& element) -{ - game::component::atmosphere component; - - if (element.contains("upper_limit")) - component.upper_limit = element["upper_limit"].get(); - if (element.contains("index_of_refraction")) - component.index_of_refraction = element["index_of_refraction"].get(); - - if (element.contains("rayleigh_concentration")) - component.rayleigh_concentration = element["rayleigh_concentration"].get(); - if (element.contains("rayleigh_scale_height")) - component.rayleigh_scale_height = element["rayleigh_scale_height"].get(); - - if (element.contains("mie_concentration")) - component.mie_concentration = element["mie_concentration"].get(); - if (element.contains("mie_scale_height")) - component.mie_scale_height = element["mie_scale_height"].get(); - if (element.contains("mie_anisotropy")) - component.mie_anisotropy = element["mie_anisotropy"].get(); - if (element.contains("mie_albedo")) - component.mie_albedo = element["mie_albedo"].get(); - - if (element.contains("ozone_concentration")) - component.ozone_concentration = element["ozone_concentration"].get(); - if (element.contains("ozone_lower_limit")) - component.ozone_lower_limit = element["ozone_lower_limit"].get(); - if (element.contains("ozone_upper_limit")) - component.ozone_upper_limit = element["ozone_upper_limit"].get(); - if (element.contains("ozone_mode")) - component.ozone_mode = element["ozone_mode"].get(); - - if (element.contains("airglow_illuminance")) - { - const auto& airglow_illuminance = element["airglow_illuminance"]; - component.airglow_illuminance.x() = airglow_illuminance[0].get(); - component.airglow_illuminance.y() = airglow_illuminance[1].get(); - component.airglow_illuminance.z() = airglow_illuminance[2].get(); - } - - archetype.stamps.push_back - ( - [component](entt::handle& handle) - { - handle.emplace_or_replace(component); - } - ); - - return true; -} - -/* -static bool load_component_behavior(entity::archetype& archetype, resource_manager& resource_manager, const json& element) -{ - game::component::behavior component; - component.behavior_tree = nullptr; - - if (element.contains("file")) - { - component.behavior_tree = resource_manager.load(element["file"].get()); - } - - archetype.stamps.push_back - ( - [component](entt::handle& handle) - { - handle.emplace_or_replace(component); - } - ); - - return (component.behavior_tree != nullptr); -} -*/ - -static bool load_component_blackbody(entity::archetype& archetype, const json& element) -{ - game::component::blackbody component; - component.temperature = 0.0; - - if (element.contains("temperature")) - component.temperature = element["temperature"].get(); - - archetype.stamps.push_back - ( - [component](entt::handle& handle) - { - handle.emplace_or_replace(component); - } - ); - - return true; -} - -static bool load_component_celestial_body(entity::archetype& archetype, const json& element) -{ - game::component::celestial_body component; - - if (element.contains("radius")) - component.radius = element["radius"].get(); - if (element.contains("mass")) - component.mass = element["mass"].get(); - if (element.contains("pole_ra")) - { - component.pole_ra.clear(); - auto& pole_ra_element = element["pole_ra"]; - for (auto it = pole_ra_element.rbegin(); it != pole_ra_element.rend(); ++it) - component.pole_ra.push_back(math::radians(it->get())); - } - if (element.contains("pole_dec")) - { - component.pole_dec.clear(); - auto& pole_dec_element = element["pole_dec"]; - for (auto it = pole_dec_element.rbegin(); it != pole_dec_element.rend(); ++it) - component.pole_dec.push_back(math::radians(it->get())); - } - if (element.contains("prime_meridian")) - { - component.prime_meridian.clear(); - auto& prime_meridian_element = element["prime_meridian"]; - for (auto it = prime_meridian_element.rbegin(); it != prime_meridian_element.rend(); ++it) - component.prime_meridian.push_back(math::radians(it->get())); - } - if (element.contains("albedo")) - component.albedo = element["albedo"].get(); - - archetype.stamps.push_back - ( - [component](entt::handle& handle) - { - handle.emplace_or_replace(component); - } - ); - - return true; -} - -static bool load_component_collision(entity::archetype& archetype, resource_manager& resource_manager, const json& element) -{ - game::component::collision component; - component.mesh = nullptr; - - if (element.contains("file")) - { - component.mesh = resource_manager.load(element["file"].get()); - } - - archetype.stamps.push_back - ( - [component](entt::handle& handle) - { - handle.emplace_or_replace(component); - } - ); - - return (component.mesh != nullptr); -} - -static bool load_component_diffuse_reflector(entity::archetype& archetype, const json& element) -{ - game::component::diffuse_reflector component; - component.albedo = 0.0; - - if (element.contains("albedo")) - component.albedo = element["albedo"].get(); - - archetype.stamps.push_back - ( - [component](entt::handle& handle) - { - handle.emplace_or_replace(component); - } - ); - - return true; -} - -static bool load_component_model(entity::archetype& archetype, resource_manager& resource_manager, const json& element) -{ - game::component::model component; - component.instance_count = 0; - //component.layers = ~0; - component.layers = 1; - - if (element.contains("file")) - { - component.render_model = resource_manager.load(element["file"].get()); - } - - archetype.stamps.push_back - ( - [component](entt::handle& handle) - { - handle.emplace_or_replace(component); - } - ); - - return true; -} - -static bool load_component_orbit(entity::archetype& archetype, const json& element) -{ - game::component::orbit component; - - component.parent = entt::null; - component.ephemeris_index = -1; - component.scale = 1.0; - component.position = {0, 0, 0}; - - if (element.contains("ephemeris_index")) - component.ephemeris_index = element["ephemeris_index"].get(); - if (element.contains("scale")) - component.scale = element["scale"].get(); - - archetype.stamps.push_back - ( - [component](entt::handle& handle) - { - handle.emplace_or_replace(component); - } - ); - - return true; -} - -static bool load_component_transform(entity::archetype& archetype, const json& element) -{ - game::component::transform component; - component.local = math::transform::identity; - component.warp = true; - - if (element.contains("translation")) - { - auto translation = element["translation"]; - component.local.translation.x() = translation[0].get(); - component.local.translation.y() = translation[1].get(); - component.local.translation.z() = translation[2].get(); - } - - if (element.contains("rotation")) - { - auto translation = element["rotation"]; - component.local.rotation.w() = translation[0].get(); - component.local.rotation.x() = translation[1].get(); - component.local.rotation.y() = translation[2].get(); - component.local.rotation.z() = translation[3].get(); - } - - if (element.contains("scale")) - { - auto translation = element["scale"]; - component.local.scale.x() = translation[0].get(); - component.local.scale.y() = translation[1].get(); - component.local.scale.z() = translation[2].get(); - } - - component.world = component.local; - - archetype.stamps.push_back - ( - [component](entt::handle& handle) - { - handle.emplace_or_replace(component); - } - ); - - return true; -} - -static bool load_component(entity::archetype& archetype, resource_manager& resource_manager, json::const_iterator element) -{ - if (element.key() == "atmosphere") - return load_component_atmosphere(archetype, element.value()); - // if (element.key() == "behavior") - // return load_component_behavior(archetype, resource_manager, element.value()); - if (element.key() == "blackbody") - return load_component_blackbody(archetype, element.value()); - if (element.key() == "celestial_body") - return load_component_celestial_body(archetype, element.value()); - if (element.key() == "collision") - return load_component_collision(archetype, resource_manager, element.value()); - if (element.key() == "diffuse_reflector") - return load_component_diffuse_reflector(archetype, element.value()); - if (element.key() == "model") - return load_component_model(archetype, resource_manager, element.value()); - if (element.key() == "orbit") - return load_component_orbit(archetype, element.value()); - if (element.key() == "transform") - return load_component_transform(archetype, element.value()); - - //throw std::runtime_error("Unknown component type \"" + element.key() + "\""); - - return false; -} - -template <> -entity::archetype* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) -{ - // Allocate archetype - entity::archetype* archetype = new entity::archetype(); - - // Read file into buffer - std::size_t size = static_cast(PHYSFS_fileLength(file)); - std::string buffer; - buffer.resize(size); - PHYSFS_readBytes(file, &buffer[0], size); - - // Parse JSON data from file buffer - json data = nlohmann::json::parse(buffer, nullptr, true, true); - - // Load components from table rows - for (json::const_iterator element = data.cbegin(); element != data.cend(); ++element) - { - if (!load_component(*archetype, *resource_manager, element)) - { - throw std::runtime_error("Failed to load component \"" + element.key() + "\""); - } - } - - return archetype; -} diff --git a/src/resources/ephemeris-loader.cpp b/src/resources/ephemeris-loader.cpp deleted file mode 100644 index 2d4ddc7..0000000 --- a/src/resources/ephemeris-loader.cpp +++ /dev/null @@ -1,244 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "resource-loader.hpp" -#include "physics/orbit/ephemeris.hpp" -#include "utility/bit-math.hpp" -#include -#include - -/// Offset to time data in the JPL DE header, in bytes. -static constexpr std::size_t jpl_de_offset_time = 0xA5C; - -/// Offset to the first coefficient table in the JPL DE header, in bytes. -static constexpr std::size_t jpl_de_offset_table1 = 0xA88; - -/// Offset to the DE version number in the JPL DE header, in bytes. -static constexpr std::size_t jpl_de_offset_denum = 0xB18; - -/// Offset to the second coefficient table in the JPL DE header, in bytes. -static constexpr std::size_t jpl_de_offset_table2 = 0xB1C; - -/// Offset to the third coefficient table in the JPL DE header, in bytes, if the constant limit has not been exceeded. -static constexpr std::size_t jpl_de_offset_table3 = 0xB28; - -/// Mask to detect bytes in the most significant word of the JPL DE version number. -static constexpr std::int32_t jpl_de_denum_endian_mask = 0xFFFF0000; - -/// Number of items in the first coefficient table. -static constexpr std::size_t jpl_de_table1_count = 12; - -/// Number of items in the second coefficient table. -static constexpr std::size_t jpl_de_table2_count = 1; - -/// Number of items in the third coefficient table. -static constexpr std::size_t jpl_de_table3_count = 2; - -/// Maximum number of items in a JPL DE file. -static constexpr std::size_t jpl_de_max_item_count = jpl_de_table1_count + jpl_de_table2_count + jpl_de_table3_count; - -/// Maximum number of constants in the first set of constant names. -static constexpr std::size_t jpl_de_constant_limit = 400; - -/// Length of a constant name, in bytes. -static constexpr std::size_t jpl_de_constant_length = 6; - -/// Enumerated IDs of the JPL DE items. -enum -{ - /// Mercury - jpl_de_id_mercury, - - /// Venus - jpl_de_id_venus, - - /// Earth-Moon barycenter - jpl_de_id_embary, - - /// Mars - jpl_de_id_mars, - - /// Jupiter - jpl_de_id_jupiter, - - /// Saturn - jpl_de_id_saturn, - - /// Uranus - jpl_de_id_uranus, - - /// Neptune - jpl_de_id_neptune, - - /// Pluto - jpl_de_id_pluto, - - /// Moon - jpl_de_id_moon, - - /// Sun - jpl_de_id_sun, - - /// Earth nutation - jpl_de_id_earth_nutation, - - /// Lunar mantle libration - jpl_de_id_luma_libration, - - /// Lunar mantle angular velocity - jpl_de_id_luma_angular_velocity, - - /// TT-TDB - jpl_de_id_tt_tdb -}; - -/// Number of components for each JPL DE item. -static constexpr std::size_t jpl_de_component_count[jpl_de_max_item_count] = -{ - 3, // Mercury: x,y,z (km) - 3, // Venus: x,y,z (km) - 3, // Earth-Moon barycenter: x,y,z (km) - 3, // Mars: x,y,z (km) - 3, // Jupiter: x,y,z (km) - 3, // Saturn: x,y,z (km) - 3, // Uranus: x,y,z (km) - 3, // Neptune: x,y,z (km) - 3, // Pluto: x,y,z (km) - 3, // Moon: x,y,z (km) - 3, // Sun: x,y,z (km) - 2, // Earth nutation: d_psi,d_epsilon (radians) - 3, // Lunar mantle libration: phi,theta,ps (radians) - 3, // Lunar mantle angular velocity: omega_x,omega_y,omega_z (radians/day) - 1 // TT-TDB: t (seconds) -}; - -/// Reads and swaps the byte order of 32-bit numbers. -static PHYSFS_sint64 read_swap32(PHYSFS_File* handle, void* buffer, PHYSFS_uint64 len) -{ - PHYSFS_sint64 status = PHYSFS_readBytes(handle, buffer, len); - for (std::uint32_t* ptr32 = (uint32_t*)buffer; len >= 8; len -= 8, ++ptr32) - *ptr32 = bit::swap32(*ptr32); - return status; -} - -/// Reads and swaps the byte order of 64-bit numbers. -static PHYSFS_sint64 read_swap64(PHYSFS_File* handle, void* buffer, PHYSFS_uint64 len) -{ - PHYSFS_sint64 status = PHYSFS_readBytes(handle, buffer, len); - for (std::uint64_t* ptr64 = (uint64_t*)buffer; len >= 8; len -= 8, ++ptr64) - *ptr64 = bit::swap64(*ptr64); - return status; -} - -template <> -physics::orbit::ephemeris* resource_loader>::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) -{ - // Init file reading function pointers - PHYSFS_sint64 (*read32)(PHYSFS_File*, void*, PHYSFS_uint64) = &PHYSFS_readBytes; - PHYSFS_sint64 (*read64)(PHYSFS_File*, void*, PHYSFS_uint64) = &PHYSFS_readBytes; - - // Read DE version number - std::int32_t denum; - PHYSFS_seek(file, jpl_de_offset_denum); - PHYSFS_readBytes(file, &denum, sizeof(std::int32_t)); - - // If file endianness does not match host - if (denum & jpl_de_denum_endian_mask) - { - // Use endian-swapping read functions - read32 = &read_swap32; - read64 = &read_swap64; - } - - // Read ephemeris time - double ephemeris_time[3]; - PHYSFS_seek(file, jpl_de_offset_time); - read64(file, ephemeris_time, sizeof(double) * 3); - - // Make time relative to J2000 epoch - const double epoch = 2451545.0; - ephemeris_time[0] -= epoch; - ephemeris_time[1] -= epoch; - - // Read number of constants - std::int32_t constant_count; - read32(file, &constant_count, sizeof(std::int32_t)); - - // Read first coefficient table - std::int32_t coeff_table[jpl_de_max_item_count][3]; - PHYSFS_seek(file, jpl_de_offset_table1); - read32(file, coeff_table, sizeof(std::int32_t) * jpl_de_table1_count * 3); - - // Read second coefficient table - PHYSFS_seek(file, jpl_de_offset_table2); - read32(file, &coeff_table[jpl_de_table1_count][0], sizeof(std::int32_t) * jpl_de_table2_count * 3); - - // Seek past extra constant names - if (constant_count > jpl_de_constant_limit) - PHYSFS_seek(file, jpl_de_offset_table3 + (constant_count - jpl_de_constant_limit) * jpl_de_constant_length); - - // Read third coefficient table - read32(file, &coeff_table[jpl_de_table1_count + jpl_de_table2_count][0], sizeof(std::int32_t) * jpl_de_table3_count * 3); - - // Calculate number of coefficients per record - std::int32_t record_coeff_count = 0; - for (int i = 0; i < jpl_de_max_item_count; ++i) - { - std::int32_t coeff_count = coeff_table[i][0] + coeff_table[i][1] * coeff_table[i][2] * jpl_de_component_count[i] - 1; - record_coeff_count = std::max(record_coeff_count, coeff_count); - } - - // Calculate record size and record count - std::size_t record_size = record_coeff_count * sizeof(double); - std::size_t record_count = static_cast((ephemeris_time[1] - ephemeris_time[0]) / ephemeris_time[2]); - - // Calculate coefficient strides - std::size_t strides[11]; - for (int i = 0; i < 11; ++i) - strides[i] = coeff_table[i][2] * coeff_table[i][1] * 3; - - // Allocate and resize ephemeris to accommodate items 0-10 - physics::orbit::ephemeris* ephemeris = new physics::orbit::ephemeris(); - ephemeris->resize(11); - - // Init trajectories - for (int i = 0; i < 11; ++i) - { - auto& trajectory = (*ephemeris)[i]; - trajectory.t0 = ephemeris_time[0]; - trajectory.t1 = ephemeris_time[1]; - trajectory.dt = ephemeris_time[2] / static_cast(coeff_table[i][2]); - trajectory.n = coeff_table[i][1]; - trajectory.a.resize(record_count * strides[i]); - } - - // Read coefficients - for (std::size_t i = 0; i < record_count; ++i) - { - // Seek to coefficient record - PHYSFS_seek(file, (i + 2) * record_size + 2 * sizeof(double)); - - for (int j = 0; j < 11; ++j) - { - read64(file, &(*ephemeris)[j].a[i * strides[j]], sizeof(double) * strides[j]); - } - } - - return ephemeris; -} diff --git a/src/resources/file-buffer-loader.cpp b/src/resources/file-buffer-loader.cpp deleted file mode 100644 index 3852657..0000000 --- a/src/resources/file-buffer-loader.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "resources/resource-loader.hpp" -#include "resources/file-buffer.hpp" -#include - -template <> -file_buffer* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) -{ - file_buffer* buffer = new file_buffer(); - - // Read file into buffer - std::size_t size = static_cast(PHYSFS_fileLength(file)); - buffer->resize(size); - PHYSFS_readBytes(file, buffer->data(), size); - - return buffer; -} diff --git a/src/resources/image-loader.cpp b/src/resources/image-loader.cpp deleted file mode 100644 index 51df910..0000000 --- a/src/resources/image-loader.cpp +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "resource-loader.hpp" -#include "stb/stb_image.h" -#include "resources/image.hpp" -#include -#include -#include -#include - -template <> -image* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) -{ - unsigned char* buffer; - int size; - ::image* image = nullptr; - - // Read input stream into buffer - size = static_cast(PHYSFS_fileLength(file)); - buffer = new unsigned char[size]; - PHYSFS_readBytes(file, buffer, size); - - // Select loader according to file extension - if (path.extension() == ".exr") - { - // Load OpenEXR with TinyEXR - int status = TINYEXR_SUCCESS; - const char* error = nullptr; - - // Read EXR version - EXRVersion exr_version; - status = ParseEXRVersionFromMemory(&exr_version, buffer, size); - if (status != TINYEXR_SUCCESS) - { - delete[] buffer; - throw std::runtime_error("TinyEXR parse version error (" + std::to_string(status) + "): invalid EXR file"); - } - - // Check if image is multipart - if (exr_version.multipart) - { - throw std::runtime_error("OpenEXR multipart images not supported"); - } - - // Read EXR header - EXRHeader exr_header; - InitEXRHeader(&exr_header); - status = ParseEXRHeaderFromMemory(&exr_header, &exr_version, buffer, size, &error); - if (status != TINYEXR_SUCCESS) - { - std::string error_string(error); - FreeEXRErrorMessage(error); - delete[] buffer; - throw std::runtime_error("TinyEXR parse header error (" + std::to_string(status) + "): " + error_string); - } - - // Check if image is tiled - if (exr_header.tiled) - { - FreeEXRHeader(&exr_header); - delete[] buffer; - throw std::runtime_error("OpenEXR tiled images not supported"); - } - - // Read half channels as float - for (int i = 0; i < exr_header.num_channels; ++i) - { - if (exr_header.pixel_types[i] == TINYEXR_PIXELTYPE_HALF) - { - exr_header.requested_pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT; - } - } - - // Read EXR data - EXRImage exr_image; - InitEXRImage(&exr_image); - status = LoadEXRImageFromMemory(&exr_image, &exr_header, buffer, size, &error); - if (status != TINYEXR_SUCCESS) - { - std::string error_string(error); - FreeEXRErrorMessage(error); - FreeEXRHeader(&exr_header); - delete[] buffer; - throw std::runtime_error("TinyEXR load error (" + std::to_string(status) + "): " + error_string); - } - - // Free file buffer - delete[] buffer; - - // Create image - image = new ::image(); - image->format(sizeof(float), exr_image.num_channels); - image->resize(static_cast(exr_image.width), static_cast(exr_image.height)); - - // Fill image pixels - float* component = static_cast(image->data()); - for (int y = exr_image.height - 1; y >= 0; --y) - { - int row_offset = y * exr_image.width; - - for (int x = 0; x < exr_image.width; ++x) - { - int pixel_index = row_offset + x; - - for (int c = exr_image.num_channels - 1; c >= 0; --c) - { - *(component++) = reinterpret_cast(exr_image.images)[c][pixel_index]; - } - } - } - - // Free EXR data - FreeEXRImage(&exr_image); - FreeEXRHeader(&exr_header); - } - else - { - // Load all other formats with stb_image - - // Set vertical flip on load in order to upload pixels correctly to OpenGL - stbi_set_flip_vertically_on_load(true); - - // Load image data - void* pixels = nullptr; - int width = 0; - int height = 0; - int channels = 0; - pixels = stbi_load_from_memory(buffer, size, &width, &height, &channels, 0); - - // Free file buffer - delete[] buffer; - - // Check if image was loaded - if (!pixels) - { - throw std::runtime_error("STBI failed to load image from memory."); - } - - // Create image - std::size_t component_size = sizeof(unsigned char); - image = new ::image(); - image->format(component_size, channels); - image->resize(static_cast(width), static_cast(height)); - std::memcpy(image->data(), pixels, image->get_size()); - - // Free loaded image data - stbi_image_free(pixels); - } - - return image; -} - diff --git a/src/resources/image.cpp b/src/resources/image.cpp deleted file mode 100644 index b93fcc7..0000000 --- a/src/resources/image.cpp +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "image.hpp" -#include -#include - -image::image(const image& source) -{ - *this = source; -} - -image::image(): - width(0), - height(0), - component_size(0), - channel_count(0), - pixel_size(0), - pixels(nullptr), - size(0) -{} - -image::~image() -{ - free_pixels(); -} - -image& image::operator=(const image& source) -{ - format(source.component_size, source.channel_count); - resize(source.width, source.height); - std::memcpy(pixels, source.pixels, size); - - return *this; -} - -bool image::compatible(const image& other) const -{ - return (other.component_size == component_size && other.channel_count == channel_count); -} - -void image::copy(const image& source, unsigned int w, unsigned int h, unsigned int from_x, int unsigned from_y, unsigned int to_x, unsigned int to_y) -{ - if (!compatible(source)) - { - throw std::runtime_error("Cannot copy image with mismatched format"); - } - - const unsigned char* from_pixels = static_cast(source.pixels); - unsigned char* to_pixels = static_cast(pixels); - - for (unsigned int i = 0; i < h; ++i) - { - // Calculate vertical pixel offset - unsigned int from_i = from_y + i; - unsigned int to_i = to_y + i; - - // Bounds check - if (from_i >= source.height || to_i >= height) - break; - - for (unsigned int j = 0; j < w; ++j) - { - // Calculate horizontal pixel offsets - unsigned int from_j = from_x + j; - unsigned int to_j = to_x + j; - - // Bounds check - if (from_j >= source.width || to_j >= width) - continue; - - // Calculate pixel data offset (in bytes) - std::size_t from_offset = (from_i * source.width + from_j) * pixel_size; - std::size_t to_offset = (to_i * width + to_j) * pixel_size; - - // Copy single pixel - std::memcpy(to_pixels + to_offset, from_pixels + from_offset, pixel_size); - } - } -} - -void image::format(std::size_t component_size, std::size_t channel_count) -{ - if (this->component_size == component_size && this->channel_count == channel_count) - return; - - free_pixels(); - this->component_size = component_size; - this->channel_count = channel_count; - pixel_size = component_size * channel_count; - size = width * height * pixel_size; - allocate_pixels(); -} - -void image::resize(unsigned int width, unsigned int height) -{ - if (this->width == width && this->height == height) - { - return; - } - - free_pixels(); - this->width = width; - this->height = height; - size = width * height * pixel_size; - allocate_pixels(); -} - -void image::allocate_pixels() -{ - if (size != 0) - { - pixels = new unsigned char[size]; - } -} - -void image::free_pixels() -{ - if (pixels != nullptr) - { - delete[] reinterpret_cast(pixels); - pixels = nullptr; - size = 0; - } -} diff --git a/src/resources/image.hpp b/src/resources/image.hpp deleted file mode 100644 index 139e267..0000000 --- a/src/resources/image.hpp +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_IMAGE_HPP -#define ANTKEEPER_IMAGE_HPP - -#include "math/vector.hpp" -#include -#include - -/** - * Stores basic image data. - */ -class image -{ -public: - /** - * Creates a copy of another image. - * - * @param source Image from which to copy. - */ - image(const image& source); - - /// Creates an image. - image(); - - /// Destroys an image. - ~image(); - - /** - * Makes this image a copy of another image. - * - * @param source Image from which to copy. - */ - image& operator=(const image& source); - - /** - * Returns an iterator to the first pixel. - * - * @tparam T Pixel data type. - */ - /// @{ - template - inline constexpr T* begin() noexcept - { - static_assert(std::is_standard_layout::value, "Pixel iterator type is not standard-layout."); - static_assert(std::is_trivial::value, "Pixel iterator type is not trivial."); - return static_cast(pixels); - } - template - inline constexpr const T* begin() const noexcept - { - static_assert(std::is_standard_layout::value, "Pixel iterator type is not standard-layout."); - static_assert(std::is_trivial::value, "Pixel iterator type is not trivial."); - return static_cast(pixels); - } - template - inline constexpr const T* cbegin() const noexcept - { - static_assert(std::is_standard_layout::value, "Pixel iterator type is not standard-layout."); - static_assert(std::is_trivial::value, "Pixel iterator type is not trivial."); - return static_cast(pixels); - } - /// @} - - /** - * Returns an iterator to the pixel following the last pixel. - * - * @tparam T Pixel data type. - */ - /// @{ - template - inline constexpr T* end() noexcept - { - static_assert(std::is_standard_layout::value, "Pixel iterator type is not standard-layout."); - static_assert(std::is_trivial::value, "Pixel iterator type is not trivial."); - return reinterpret_cast(static_cast(pixels) + size); - } - template - inline constexpr const T* end() const noexcept - { - static_assert(std::is_standard_layout::value, "Pixel iterator type is not standard-layout."); - static_assert(std::is_trivial::value, "Pixel iterator type is not trivial."); - return reinterpret_cast(static_cast(pixels) + size); - } - template - inline constexpr const T* cend() const noexcept - { - static_assert(std::is_standard_layout::value, "Pixel iterator type is not standard-layout."); - static_assert(std::is_trivial::value, "Pixel iterator type is not trivial."); - return reinterpret_cast(static_cast(pixels) + size); - } - /// @} - - /** - * Checks whether another image has the same number of channels and pixel size as this image. - * - * @param other Image for with which to compare compatibility. - * @return `true` if the image formats are compatible, `false` otherwise. - */ - bool compatible(const image& other) const; - - /** - * Copies pixel data from another image with a compatible format into this image. - * - * @param source Source image from which to copy pixel data. - * @param w Width of the subimage to copy. - * @param h Height of the subimage to copy. - * @param from_x X-coordinate of the first pixel to copy from the source subimage. - * @param from_y Y-coordinate of the first pixel to copy from the source subimage. - * @param to_x X-coordinate of the first pixel in the destination subimage. - * @param to_y Y-coordinate of the first pixel in the destination subimage. - * - * @except std::runtime_error Cannot copy image with mismatched format. - * - * @see image::compatible(const image&) const - */ - void copy(const image& source, unsigned int w, unsigned int h, unsigned int from_x = 0, int unsigned from_y = 0, unsigned int to_x = 0, unsigned int to_y = 0); - - /** - * Changes the format of the image. Existing pixel data will be erased if the format has changed. - * - * @param component_size Size of channel components, in bytes. - * @param channel_count Number of channels in the image. - */ - void format(std::size_t component_size, std::size_t channel_count); - - /** - * Resizes the image. Existing pixel data will be erased if the size has changed. - * - * @param width New width of the image, in pixels. - * @param height New height of the image, in pixels. - */ - void resize(unsigned int width, unsigned int height); - - /// Returns the width of the image, in pixels. - unsigned int get_width() const; - - /// Returns the height of the image, in pixels. - unsigned int get_height() const; - - /// Returns the size of channel components, in bytes. - std::size_t get_component_size() const; - - /// Returns the number of color channels in the image. - std::size_t get_channel_count() const; - - /// Returns a pointer to the pixel data. - /// @{ - void* data() noexcept; - const void* data() const noexcept; - /// @} - - /// Returns the size of a single pixel, in bytes. - std::size_t get_pixel_size() const; - - /// Returns the size of the image, in bytes. - std::size_t get_size() const; - -private: - void allocate_pixels(); - void free_pixels(); - - unsigned int width; - unsigned int height; - std::size_t component_size; - std::size_t channel_count; - void* pixels; - std::size_t pixel_size; - std::size_t size; -}; - -inline unsigned int image::get_width() const -{ - return width; -} - -inline unsigned int image::get_height() const -{ - return height; -} - -inline std::size_t image::get_component_size() const -{ - return component_size; -} - -inline std::size_t image::get_channel_count() const -{ - return channel_count; -} - -inline void* image::data() noexcept -{ - return pixels; -} - -inline const void* image::data() const noexcept -{ - return pixels; -} - -inline std::size_t image::get_pixel_size() const -{ - return pixel_size; -} - -inline std::size_t image::get_size() const -{ - return size; -} - -#endif // ANTKEEPER_IMAGE_HPP diff --git a/src/resources/json-loader.cpp b/src/resources/json-loader.cpp deleted file mode 100644 index d72dd5f..0000000 --- a/src/resources/json-loader.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "resources/resource-loader.hpp" -#include "resources/resource-manager.hpp" -#include "resources/json.hpp" -#include - -template <> -json* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) -{ - // Read file into buffer - std::size_t size = static_cast(PHYSFS_fileLength(file)); - std::string buffer; - buffer.resize(size); - PHYSFS_readBytes(file, &buffer[0], size); - - // Parse json from file buffer - json* data = new json(json::parse(buffer, nullptr, true, true)); - - return data; -} diff --git a/src/resources/material-loader.cpp b/src/resources/material-loader.cpp deleted file mode 100644 index 882f65c..0000000 --- a/src/resources/material-loader.cpp +++ /dev/null @@ -1,457 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "resource-loader.hpp" -#include "resource-manager.hpp" -#include "render/material.hpp" -#include "render/material-flags.hpp" -#include -#include -#include -#include -#include - -template -static bool read_value(T* value, const nlohmann::json& json, const std::string& name) -{ - if (auto element = json.find(name); element != json.end()) - { - *value = element.value().get(); - return true; - } - - return false; -} - -static bool load_texture_1d_property(resource_manager* resource_manager, render::material* material, const std::string& name, const nlohmann::json& json) -{ - // If JSON element is an array - if (json.is_array()) - { - // Determine size of the array - std::size_t array_size = json.size(); - - // Create property - render::material_property* property = material->add_property(name, array_size); - - // Load textures - std::size_t i = 0; - for (const auto& element: json) - { - std::string filename = element.get(); - const gl::texture_1d* texture = resource_manager->load(filename); - property->set_value(i++, texture); - } - } - else - { - // Create property - render::material_property* property = material->add_property(name); - - // Load texture - std::string filename = json.get(); - const gl::texture_1d* texture = resource_manager->load(filename); - property->set_value(texture); - } - - return true; -} - -static bool load_texture_2d_property(resource_manager* resource_manager, render::material* material, const std::string& name, const nlohmann::json& json) -{ - // If JSON element is an array - if (json.is_array()) - { - // Determine size of the array - std::size_t array_size = json.size(); - - // Create property - render::material_property* property = material->add_property(name, array_size); - - // Load textures - std::size_t i = 0; - for (const auto& element: json) - { - std::string filename = element.get(); - const gl::texture_2d* texture = resource_manager->load(filename); - property->set_value(i++, texture); - } - } - else - { - // Create property - render::material_property* property = material->add_property(name); - - // Load texture - std::string filename = json.get(); - const gl::texture_2d* texture = resource_manager->load(filename); - property->set_value(texture); - } - - return true; -} - -static bool load_texture_cube_property(resource_manager* resource_manager, render::material* material, const std::string& name, const nlohmann::json& json) -{ - return false; -} - -template -static bool load_scalar_property(render::material* material, const std::string& name, const nlohmann::json& json) -{ - // If JSON element is an array - if (json.is_array()) - { - // Determine size of the array - std::size_t array_size = json.size(); - - // Create property - render::material_property* property = material->add_property(name, array_size); - - // Set property values - std::size_t i = 0; - for (const auto& element: json) - property->set_value(i++, element.get()); - } - else - { - // Create property - render::material_property* property = material->add_property(name); - - // Set property value - property->set_value(json.get()); - } - - return true; -} - -template -static bool load_vector_property(render::material* material, const std::string& name, std::size_t vector_size, const nlohmann::json& json) -{ - // If JSON element is an array of arrays - if (json.is_array() && json.begin().value().is_array()) - { - // Determine size of the array - std::size_t array_size = json.size(); - - // Create property - render::material_property* property = material->add_property(name, array_size); - - // For each vector in the array - std::size_t i = 0; - for (const auto& vector_element: json) - { - // Read vector elements - T value; - std::size_t j = 0; - for (const auto& value_element: vector_element) - value[j++] = value_element.get(); - - // Set property values - property->set_value(i++, value); - } - } - else - { - // Create property - render::material_property* property = material->add_property(name); - - // Read vector elements - T value; - std::size_t i = 0; - for (const auto& value_element: json) - value[i++] = value_element.get(); - - // Set property values - property->set_value(value); - } - - return true; -} - -template -static bool load_matrix_property(render::material* material, const std::string& name, std::size_t column_count, std::size_t row_count, const nlohmann::json& json) -{ - // If JSON element is an array of arrays of arrays - if (json.is_array() && json.begin().value().is_array()) - { - if (json.begin().value().begin().value().is_array()) - { - // Determine size of the array - std::size_t array_size = json.size(); - - // Create property - render::material_property* property = material->add_property(name, array_size); - - // For each matrix in the array - std::size_t i = 0; - for (const auto& matrix_element: json) - { - // Read vector elements - T value; - std::size_t j = 0; - for (const auto& column_element: matrix_element) - { - std::size_t k = 0; - for (const auto& row_element: column_element) - { - value[j][k] = row_element.get(); - ++k; - } - - ++j; - } - - // Set property values - property->set_value(i, value); - - ++i; - } - - return true; - } - else - { - // Create property - render::material_property* property = material->add_property(name); - - // Read matrix elements - T value; - std::size_t i = 0; - for (const auto& column_element: json) - { - std::size_t j = 0; - for (const auto& row_element: column_element) - { - value[i][j] = row_element.get(); - ++j; - } - - ++i; - } - - // Set property values - property->set_value(value); - - return true; - } - } - - return false; -} - -template <> -render::material* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) -{ - // Read file into buffer - std::size_t size = static_cast(PHYSFS_fileLength(file)); - std::string buffer; - buffer.resize(size); - PHYSFS_readBytes(file, &buffer[0], size); - - // Parse json from file buffer - nlohmann::json json = nlohmann::json::parse(buffer, nullptr, true, true); - - // Allocate material - render::material* material = new render::material(); - - // Read shader filename - std::string shader_filename; - if (read_value(&shader_filename, json, "shader")) - { - // Load shader program - gl::shader_program* program = resource_manager->load(shader_filename); - material->set_shader_program(program); - } - - // Init material flags - std::uint32_t flags = 0; - - // Read blend mode - std::string blend_mode; - read_value(&blend_mode, json, "blend_mode"); - if (blend_mode == "opaque") - { - material->set_blend_mode(render::blend_mode::opaque); - } - else if (blend_mode == "masked") - { - material->set_blend_mode(render::blend_mode::masked); - } - else if (blend_mode == "translucent") - { - material->set_blend_mode(render::blend_mode::translucent); - } - - // Read opacity threshold - float opacity_threshold = 0.5f; - if (read_value(&opacity_threshold, json, "opacity_threshold")) - { - material->set_opacity_threshold(opacity_threshold); - } - - // Read two sided - bool two_sided = false; - read_value(&two_sided, json, "two_sided"); - material->set_two_sided(two_sided); - - // Read shadow mode - std::string shadow_mode; - read_value(&shadow_mode, json, "shadow_mode"); - if (shadow_mode == "opaque") - { - material->set_shadow_mode(render::shadow_mode::opaque); - } - else if (shadow_mode == "none") - { - material->set_shadow_mode(render::shadow_mode::none); - } - - // Read depth mode - std::string depth_mode; - read_value(&depth_mode, json, "depth_mode"); - if (depth_mode == "in_front") - flags |= MATERIAL_FLAG_X_RAY; - - // Read decal mode - std::string decal_mode; - read_value(&decal_mode, json, "decal_mode"); - if (decal_mode == "decal") - flags |= MATERIAL_FLAG_DECAL; - else if (decal_mode == "surface") - flags |= MATERIAL_FLAG_DECAL_SURFACE; - - // Set material flags - material->set_flags(flags); - - // Read material properties - if (auto properties_element = json.find("properties"); properties_element != json.end()) - { - for (const auto& property_element: properties_element.value()) - { - // Read property name - std::string name; - if (!read_value(&name, property_element, "name")) - // Ignore nameless properties - continue; - - // Read property type - std::string type; - if (!read_value(&type, property_element, "type")) - // Ignore typeless properties - continue; - - // Find value element - auto value_element = property_element.find("value"); - if (value_element == property_element.end()) - // Ignore valueless properties - continue; - - if (type == "texture_1d") - { - load_texture_1d_property(resource_manager, material, name, value_element.value()); - } - else if (type == "texture_2d") - { - load_texture_2d_property(resource_manager, material, name, value_element.value()); - } - else if (type == "texture_cube") - { - load_texture_cube_property(resource_manager, material, name, value_element.value()); - } - // If property type is a matrix - else if (type[type.size() - 2] == 'x' && - std::isdigit(type[type.size() - 3]) && - std::isdigit(type.back())) - { - std::size_t columns = std::stoul(type.substr(type.size() - 3, 1)); - std::size_t rows = std::stoul(type.substr(type.size() - 1, 1)); - - if (type.find("float") != std::string::npos) - { - if (size == 2) - load_matrix_property(material, name, columns, rows, value_element.value()); - else if (size == 3) - load_matrix_property(material, name, columns, rows, value_element.value()); - else if (size == 4) - load_matrix_property(material, name, columns, rows, value_element.value()); - } - } - // If property type is a vector - else if (std::isdigit(type.back())) - { - std::size_t size = std::stoul(type.substr(type.size() - 1, 1)); - - if (type.find("float") != std::string::npos) - { - if (size == 2) - load_vector_property(material, name, size, value_element.value()); - else if (size == 3) - load_vector_property(material, name, size, value_element.value()); - else if (size == 4) - load_vector_property(material, name, size, value_element.value()); - } - else if (type.find("uint") != std::string::npos) - { - if (size == 2) - load_vector_property(material, name, size, value_element.value()); - else if (size == 3) - load_vector_property(material, name, size, value_element.value()); - else if (size == 4) - load_vector_property(material, name, size, value_element.value()); - } - else if (type.find("int") != std::string::npos) - { - if (size == 2) - load_vector_property(material, name, size, value_element.value()); - else if (size == 3) - load_vector_property(material, name, size, value_element.value()); - else if (size == 4) - load_vector_property(material, name, size, value_element.value()); - } - else if (type.find("bool") != std::string::npos) - { - if (size == 2) - load_vector_property(material, name, size, value_element.value()); - else if (size == 3) - load_vector_property(material, name, size, value_element.value()); - else if (size == 4) - load_vector_property(material, name, size, value_element.value()); - } - } - // If property type is a scalar - else - { - if (type.find("float") != std::string::npos) - load_scalar_property(material, name, value_element.value()); - else if (type.find("uint") != std::string::npos) - load_scalar_property(material, name, value_element.value()); - else if (type.find("int") != std::string::npos) - load_scalar_property(material, name, value_element.value()); - else if (type.find("bool") != std::string::npos) - load_scalar_property(material, name, value_element.value()); - } - } - } - - // Update material tweens - material->update_tweens(); - - return material; -} diff --git a/src/resources/mesh-loader.cpp b/src/resources/mesh-loader.cpp deleted file mode 100644 index cf6539e..0000000 --- a/src/resources/mesh-loader.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "resources/resource-loader.hpp" -#include "geom/mesh.hpp" -#include "geom/mesh-functions.hpp" -#include "utility/fundamental-types.hpp" -#include -#include -#include - -template <> -geom::mesh* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) -{ - std::string line; - std::vector vertices; - std::vector> triangles; - - while (!PHYSFS_eof(file)) - { - // Read line - physfs_getline(file, line); - - // Tokenize line - std::vector tokens; - std::string token; - std::istringstream linestream(line); - while (linestream >> token) - tokens.push_back(token); - - // Skip empty lines and comments - if (tokens.empty() || tokens[0][0] == '#') - continue; - - if (tokens[0] == "v") - { - if (tokens.size() != 4) - { - std::stringstream stream; - stream << "resource_loader::load(): Invalid line \"" << line << "\"" << std::endl; - throw std::runtime_error(stream.str()); - } - - float3 vertex; - - std::stringstream(tokens[1]) >> vertex[0]; - std::stringstream(tokens[2]) >> vertex[1]; - std::stringstream(tokens[3]) >> vertex[2]; - - vertices.push_back(vertex); - } - else if (tokens[0] == "f") - { - if (tokens.size() != 4) - { - std::stringstream stream; - stream << "resource_loader::load(): Invalid line \"" << line << "\"" << std::endl; - throw std::runtime_error(stream.str()); - - } - - std::uint_fast32_t a, b, c; - std::stringstream(tokens[1]) >> a; - std::stringstream(tokens[2]) >> b; - std::stringstream(tokens[3]) >> c; - triangles.push_back({a - 1, b - 1, c - 1}); - } - } - - geom::mesh* mesh = new geom::mesh(); - geom::create_triangle_mesh(*mesh, vertices, triangles); - - return mesh; -} - diff --git a/src/resources/model-loader.cpp b/src/resources/model-loader.cpp deleted file mode 100644 index fed3f98..0000000 --- a/src/resources/model-loader.cpp +++ /dev/null @@ -1,334 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "resources/resource-loader.hpp" -#include "resources/resource-manager.hpp" -#include "resources/deserializer.hpp" -#include "render/model.hpp" -#include "render/vertex-attribute.hpp" -#include "gl/vertex-attribute.hpp" -#include "gl/drawing-mode.hpp" -#include "math/numbers.hpp" -#include - -inline constexpr std::uint16_t vertex_attribute_position = 0b0000000000000001; -inline constexpr std::uint16_t vertex_attribute_uv = 0b0000000000000010; -inline constexpr std::uint16_t vertex_attribute_normal = 0b0000000000000100; -inline constexpr std::uint16_t vertex_attribute_tangent = 0b0000000000001000; -inline constexpr std::uint16_t vertex_attribute_color = 0b0000000000010000; -inline constexpr std::uint16_t vertex_attribute_bone = 0b0000000000100000; -inline constexpr std::uint16_t vertex_attribute_barycentric = 0b0000000001000000; -inline constexpr std::uint16_t vertex_attribute_morph_target = 0b0000000010000000; -inline constexpr std::uint16_t vertex_attribute_index = 0b0000000100000000; - -template <> -render::model* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) -{ - deserialize_context ctx(file); - - // Read vertex format - std::uint16_t vertex_format_flags = 0; - ctx.read16(reinterpret_cast(&vertex_format_flags), 1); - - // Read bone per vertex (if any) - std::uint8_t bones_per_vertex = 0; - if (vertex_format_flags & vertex_attribute_bone) - { - ctx.read8(reinterpret_cast(&bones_per_vertex), 1); - } - - // Read vertex count - std::uint32_t vertex_count = 0; - ctx.read32(reinterpret_cast(&vertex_count), 1); - - // Determine vertex size - std::size_t vertex_size = 0; - if (vertex_format_flags & vertex_attribute_position) - { - vertex_size += sizeof(float) * 3; - } - if (vertex_format_flags & vertex_attribute_uv) - { - vertex_size += sizeof(float) * 2; - } - if (vertex_format_flags & vertex_attribute_normal) - { - vertex_size += sizeof(float) * 3; - } - if (vertex_format_flags & vertex_attribute_tangent) - { - vertex_size += sizeof(float) * 4; - } - if (vertex_format_flags & vertex_attribute_color) - { - vertex_size += sizeof(float) * 4; - } - if (vertex_format_flags & vertex_attribute_bone) - { - vertex_size += sizeof(std::uint32_t) * bones_per_vertex; - vertex_size += sizeof(float) * bones_per_vertex; - } - if (vertex_format_flags & vertex_attribute_barycentric) - { - vertex_size += sizeof(float) * 3; - } - if (vertex_format_flags & vertex_attribute_morph_target) - { - vertex_size += sizeof(float) * 3; - } - - // Allocate vertex data - std::byte* vertex_data = new std::byte[vertex_count * vertex_size]; - - // Read vertices - if constexpr (std::endian::native == std::endian::little) - { - ctx.read8(vertex_data, vertex_count * vertex_size); - } - else - { - std::byte* vertex_data_offset = vertex_data; - for (std::size_t i = 0; i < vertex_count; ++i) - { - if (vertex_format_flags & vertex_attribute_position) - { - ctx.read32(vertex_data_offset, 3); - vertex_data_offset += sizeof(float) * 3; - } - if (vertex_format_flags & vertex_attribute_uv) - { - ctx.read32(vertex_data_offset, 2); - vertex_data_offset += sizeof(float) * 2; - } - if (vertex_format_flags & vertex_attribute_normal) - { - ctx.read32(vertex_data_offset, 3); - vertex_data_offset += sizeof(float) * 3; - } - if (vertex_format_flags & vertex_attribute_tangent) - { - ctx.read32(vertex_data_offset, 4); - vertex_data_offset += sizeof(float) * 4; - } - if (vertex_format_flags & vertex_attribute_color) - { - ctx.read32(vertex_data_offset, 4); - vertex_data_offset += sizeof(float) * 4; - } - if (vertex_format_flags & vertex_attribute_bone) - { - ctx.read32(vertex_data_offset, bones_per_vertex); - ctx.read32(vertex_data_offset, bones_per_vertex); - - vertex_data_offset += sizeof(std::uint32_t) * bones_per_vertex; - vertex_data_offset += sizeof(float) * bones_per_vertex; - } - if (vertex_format_flags & vertex_attribute_barycentric) - { - ctx.read32(vertex_data_offset, 3); - vertex_data_offset += sizeof(float) * 3; - } - if (vertex_format_flags & vertex_attribute_morph_target) - { - ctx.read32(vertex_data_offset, 3); - vertex_data_offset += sizeof(float) * 3; - } - } - } - - // Read geometry bounds - geom::aabb bounds; - ctx.read32(reinterpret_cast(bounds.min_point.data()), 3); - ctx.read32(reinterpret_cast(bounds.max_point.data()), 3); - - // Allocate a model - render::model* model = new render::model(); - - // Set the model bounds - model->set_bounds(bounds); - - // Resize model VBO and upload vertex data - gl::vertex_buffer* vbo = model->get_vertex_buffer(); - vbo->resize(vertex_count * vertex_size, vertex_data); - - // Free vertex data - delete[] vertex_data; - - // Bind vertex attributes to VAO - gl::vertex_array* vao = model->get_vertex_array(); - gl::vertex_attribute attribute; - attribute.buffer = vbo; - attribute.offset = 0; - attribute.stride = vertex_size; - if (vertex_format_flags & vertex_attribute_position) - { - attribute.type = gl::vertex_attribute_type::float_32; - attribute.components = 3; - vao->bind(render::vertex_attribute::position, attribute); - attribute.offset += sizeof(float) * attribute.components; - } - if (vertex_format_flags & vertex_attribute_uv) - { - attribute.type = gl::vertex_attribute_type::float_32; - attribute.components = 2; - vao->bind(render::vertex_attribute::uv, attribute); - attribute.offset += sizeof(float) * attribute.components; - } - if (vertex_format_flags & vertex_attribute_normal) - { - attribute.type = gl::vertex_attribute_type::float_32; - attribute.components = 3; - vao->bind(render::vertex_attribute::normal, attribute); - attribute.offset += sizeof(float) * attribute.components; - } - if (vertex_format_flags & vertex_attribute_tangent) - { - attribute.type = gl::vertex_attribute_type::float_32; - attribute.components = 4; - vao->bind(render::vertex_attribute::tangent, attribute); - attribute.offset += sizeof(float) * attribute.components; - } - if (vertex_format_flags & vertex_attribute_color) - { - attribute.type = gl::vertex_attribute_type::float_32; - attribute.components = 4; - vao->bind(render::vertex_attribute::color, attribute); - attribute.offset += sizeof(float) * attribute.components; - } - if (vertex_format_flags & vertex_attribute_bone) - { - attribute.type = gl::vertex_attribute_type::uint_16; - attribute.components = bones_per_vertex; - vao->bind(render::vertex_attribute::bone_index, attribute); - attribute.offset += sizeof(std::uint32_t) * attribute.components; - - attribute.type = gl::vertex_attribute_type::float_32; - attribute.components = bones_per_vertex; - vao->bind(render::vertex_attribute::bone_weight, attribute); - attribute.offset += sizeof(float) * attribute.components; - } - if (vertex_format_flags & vertex_attribute_barycentric) - { - attribute.type = gl::vertex_attribute_type::float_32; - attribute.components = 3; - vao->bind(render::vertex_attribute::barycentric, attribute); - attribute.offset += sizeof(float) * attribute.components; - } - if (vertex_format_flags & vertex_attribute_morph_target) - { - attribute.type = gl::vertex_attribute_type::float_32; - attribute.components = 3; - vao->bind(render::vertex_attribute::target, attribute); - attribute.offset += sizeof(float) * attribute.components; - } - - // Read material count - std::uint16_t material_count = 0; - ctx.read16(reinterpret_cast(&material_count), 1); - - // Read materials - for (std::uint16_t i = 0; i < material_count; ++i) - { - // Read material name length - std::uint8_t material_name_length = 0; - ctx.read8(reinterpret_cast(&material_name_length), 1); - - // Read material name - std::string material_name(static_cast(material_name_length), '\0'); - ctx.read8(reinterpret_cast(material_name.data()), material_name_length); - - // Read offset to index of first vertex - std::uint32_t material_vertex_offset = 0; - ctx.read32(reinterpret_cast(&material_vertex_offset), 1); - - // Read vertex count - std::uint32_t material_vertex_count = 0; - ctx.read32(reinterpret_cast(&material_vertex_count), 1); - - // Slugify material filename - std::string material_filename = material_name + ".mtl"; - std::replace(material_filename.begin(), material_filename.end(), '_', '-'); - - // Load material from file - render::material* material = resource_manager->load(material_filename); - - // Create model material group - render::model_group* material_group = model->add_group(material_name); - material_group->set_drawing_mode(gl::drawing_mode::triangles); - material_group->set_start_index(material_vertex_offset); - material_group->set_index_count(material_vertex_count); - material_group->set_material(material); - } - - // Read skeleton - if (vertex_format_flags & vertex_attribute_bone) - { - ::skeleton& skeleton = model->get_skeleton(); - pose& bind_pose = skeleton.bind_pose; - - // Read bone count - std::uint16_t bone_count = 0; - ctx.read16(reinterpret_cast(&bone_count), 1); - - // Read bones - for (std::uint16_t i = 0; i < bone_count; ++i) - { - // Read bone name length - std::uint8_t bone_name_length = 0; - ctx.read8(reinterpret_cast(&bone_name_length), 1); - - // Read bone name - std::string bone_name(static_cast(bone_name_length), '\0'); - ctx.read8(reinterpret_cast(bone_name.data()), bone_name_length); - - // Read parent bone index - std::uint16_t parent_bone_index = i; - ctx.read16(reinterpret_cast(&parent_bone_index), 1); - - // Construct bone identifier - ::bone bone = make_bone(i, parent_bone_index); - - // Add bone to bone map - skeleton.bone_map[bone_name] = bone; - - // Get reference to the bone's bind pose transform - auto& bone_transform = bind_pose[bone]; - - // Read bone translation - ctx.read32(reinterpret_cast(bone_transform.translation.data()), 3); - - // Read bone rotation - ctx.read32(reinterpret_cast(&bone_transform.rotation.r), 1); - ctx.read32(reinterpret_cast(bone_transform.rotation.i.data()), 3); - - // Set bone scale - bone_transform.scale = {1, 1, 1}; - - // Read bone length - float bone_length = 0.0f; - ctx.read32(reinterpret_cast(&bone_length), 1); - } - - // Calculate inverse skeleton-space bind pose - ::concatenate(skeleton.bind_pose, skeleton.inverse_bind_pose); - ::inverse(skeleton.inverse_bind_pose, skeleton.inverse_bind_pose); - } - - return model; -} diff --git a/src/resources/resource-handle.cpp b/src/resources/resource-handle.cpp deleted file mode 100644 index cac53a4..0000000 --- a/src/resources/resource-handle.cpp +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "resources/resource-handle.hpp" - -resource_handle_base::resource_handle_base(): - reference_count(0) -{} - diff --git a/src/resources/resource-loader.cpp b/src/resources/resource-loader.cpp deleted file mode 100644 index 1c8fa75..0000000 --- a/src/resources/resource-loader.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "resources/resource-loader.hpp" -#include "resources/deserialize-error.hpp" -#include - -void physfs_getline(PHYSFS_File* file, std::string& line) -{ - line.clear(); - - for (;;) - { - char c; - const PHYSFS_sint64 status = PHYSFS_readBytes(file, &c, 1); - - if (status == 1) - { - if (c == '\r') - { - continue; - } - else if (c == '\n') - { - break; - } - else - { - line.append(1, c); - } - - } - else - { - if (PHYSFS_eof(file)) - { - break; - } - else - { - throw deserialize_error(PHYSFS_getLastError()); - } - } - } -} diff --git a/src/resources/resource-manager.cpp b/src/resources/resource-manager.cpp deleted file mode 100644 index c6f1938..0000000 --- a/src/resources/resource-manager.cpp +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "resources/resource-manager.hpp" -#include "debug/log.hpp" -#include - -resource_manager::resource_manager() -{ - // Log PhysicsFS info - // PHYSFS_Version physfs_compiled_version; - // PHYSFS_Version physfs_linked_version; - // PHYSFS_VERSION(&physfs_compiled_version); - // PHYSFS_getLinkedVersion(&physfs_linked_version); - // debug::log::info - // ( - // "PhysicsFS compiled version: {}.{}.{}; linked version: {}.{}.{}", - // physfs_compiled_version.major, - // physfs_compiled_version.minor, - // physfs_compiled_version.patch, - // physfs_linked_version.major, - // physfs_linked_version.minor, - // physfs_linked_version.patch - // ); - - // Init PhysicsFS - debug::log::trace("Initializing PhysicsFS..."); - if (!PHYSFS_init(nullptr)) - { - debug::log::error("Failed to initialize PhysicsFS: {}", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); - throw std::runtime_error("Failed to initialize PhysicsFS"); - } - else - { - debug::log::trace("Initialized PhysicsFS"); - } -} - -resource_manager::~resource_manager() -{ - debug::log::trace("Deleting cached resources..."); - - // Delete cached resources - for (auto it = resource_cache.begin(); it != resource_cache.end(); ++it) - { - delete it->second; - } - - debug::log::trace("Deleted cached resources"); - - // Deinit PhysicsFS - debug::log::trace("Deinitializing PhysicsFS..."); - if (!PHYSFS_deinit()) - { - debug::log::error("Failed to deinitialize PhysicsFS: {}", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); - } - else - { - debug::log::trace("Deinitialized PhysicsFS"); - } -} - -bool resource_manager::mount(const std::filesystem::path& path) -{ - debug::log::trace("Mounting path \"{}\"...", path.string()); - if (!PHYSFS_mount(path.string().c_str(), nullptr, 1)) - { - debug::log::error("Failed to mount path \"{}\": {}", path.string(), PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); - return false; - } - else - { - debug::log::trace("Mounted path \"{}\"", path.string()); - return true; - } -} - -void resource_manager::set_write_dir(const std::filesystem::path& path) -{ - if (!PHYSFS_setWriteDir(path.string().c_str())) - { - debug::log::error("Failed set write directory to \"{}\": {}", path.string(), PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); - } - else - { - debug::log::trace("Set write directory to \"{}\"", path.string()); - } -} - -void resource_manager::unload(const std::filesystem::path& path) -{ - // Check if resource is in the cache - auto it = resource_cache.find(path); - if (it != resource_cache.end()) - { - // Decrement the resource handle reference count - --it->second->reference_count; - - // Free the resource if the resource handle is unreferenced - if (it->second->reference_count <= 0) - { - debug::log::trace("Unloading resource \"{}\"...", path.string()); - - delete it->second; - - debug::log::trace("Unloaded resource \"{}\"", path.string()); - } - - // Remove resource from the cache - resource_cache.erase(it); - } -} - -void resource_manager::include(const std::filesystem::path& search_path) -{ - search_paths.push_back(search_path); -} diff --git a/src/resources/resource-manager.hpp b/src/resources/resource-manager.hpp deleted file mode 100644 index 441aafb..0000000 --- a/src/resources/resource-manager.hpp +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_RESOURCES_RESOURCE_MANAGER_HPP -#define ANTKEEPER_RESOURCES_RESOURCE_MANAGER_HPP - -#include "resources/resource-handle.hpp" -#include "resources/resource-loader.hpp" -#include "debug/log.hpp" -#include -#include -#include -#include -#include -#include - -/** - * Loads resources. - */ -class resource_manager -{ -public: - /** - * Creates a resource manager. - */ - resource_manager(); - - /** - * Destroys a resource manager and frees all of its resources. - */ - ~resource_manager(); - - bool mount(const std::filesystem::path& path); - - void set_write_dir(const std::filesystem::path& path); - - /** - * Adds a path to be searched when a resource is requested. - * - * @param path Search path. - */ - void include(const std::filesystem::path& path); - - /** - * Loads the requested resource. If the resource has already been loaded it will be retrieved from the resource cache and its reference count incremented. - * - * @tparam T Resource type. - * @param path Path to the resource, relative to the search paths. - * @return Pointer to the requested resource, or nullptr if the resource could not be found nor loaded. - */ - template - T* load(const std::filesystem::path& path); - - /** - * Decrements a resource's reference count and unloads the resource if it's unreferenced. - * - * @param path Path to the resource, relative to the search paths. - */ - void unload(const std::filesystem::path& path); - - /** - * Saves the specified resource. - * - * @tparam T Resource type. - * @param resource Pointer to the resource. - * @param path Path to the resource. - */ - template - void save(const T* resource, const std::filesystem::path& path); - -private: - std::map resource_cache; - std::list search_paths; -}; - -template -T* resource_manager::load(const std::filesystem::path& path) -{ - // Check if resource is in the cache - auto it = resource_cache.find(path); - if (it != resource_cache.end()) - { - //debug::log::trace("Fetched cached resource \"{}\"". path.string()); - - // Resource found - resource_handle* resource = static_cast*>(it->second); - - // Increment resource handle reference count - ++resource->reference_count; - - // Return resource data - return resource->data; - } - - debug::log::trace("Loading resource \"{}\"...", path.string()); - - // Resource not cached, look for file in search paths - T* data = nullptr; - bool found = false; - for (const std::filesystem::path& search_path: search_paths) - { - std::filesystem::path full_path = search_path / path; - - // Check if file exists - if (!PHYSFS_exists(full_path.string().c_str())) - { - continue; - } - - // File found - found = true; - - // Open file for reading - PHYSFS_File* file = PHYSFS_openRead(full_path.string().c_str()); - if (!file) - { - debug::log::error("Failed to load resource \"{}\": {}", path.string(), PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); - break; - } - - // Load opened file - try - { - data = resource_loader::load(this, file, full_path); - } - catch (const std::exception& e) - { - debug::log::error("Failed to load resource \"{}\": {}", path.string(), e.what()); - } - - // Close opened file - if (!PHYSFS_close(file)) - { - debug::log::error("Failed to close resource file \"{}\": {}", path.string(), PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); - } - - break; - } - - if (!data) - { - if (!found) - { - debug::log::error("Failed to load resource \"{}\": file not found", path.string()); - } - - return nullptr; - } - - // Create a resource handle for the resource data - resource_handle* resource = new resource_handle(); - resource->data = data; - resource->reference_count = 1; - - // Add resource to the cache - resource_cache[path] = resource; - - debug::log::trace("Loaded resource \"{}\"", path.string()); - - return resource->data; -} - -template -void resource_manager::save(const T* resource, const std::filesystem::path& path) -{ - debug::log::trace("Saving resource to \"{}\"", path.string()); - - // Open file for writing - PHYSFS_File* file = PHYSFS_openWrite(path.string().c_str()); - if (!file) - { - debug::log::error("Failed to save resource to \"{}\": {}", path.string(), PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); - return; - } - - // Save to opened file - try - { - resource_loader::save(this, file, path, resource); - debug::log::trace("Saved resource to \"{}\"", path.string()); - } - catch (const std::exception& e) - { - debug::log::error("Failed to save resource to \"{}\": {}", e.what()); - } - - // Close opened file - if (!PHYSFS_close(file)) - { - debug::log::error("Failed to close file \"{}\": {}", path.string(), PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); - } -} - -#endif // ANTKEEPER_RESOURCES_RESOURCE_MANAGER_HPP diff --git a/src/resources/serialize-context.cpp b/src/resources/serialize-context.cpp deleted file mode 100644 index b80a65b..0000000 --- a/src/resources/serialize-context.cpp +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "resources/serialize-context.hpp" -#include "resources/serialize-error.hpp" -#include - -serialize_context::serialize_context(void* handle): - handle(handle), - m_error(false) -{} - -std::size_t serialize_context::write8(const std::byte* data, std::size_t count) -{ - const PHYSFS_sint64 status = PHYSFS_writeBytes(reinterpret_cast(handle), data, count); - - if (status < 0) - { - m_error = true; - throw serialize_error(PHYSFS_getLastError()); - //return 0; - } - - if (status != count) - { - m_error = true; - throw serialize_error(PHYSFS_getLastError()); - //return static_cast(count); - } - - return count; -} - -std::size_t serialize_context::write16_le(const std::byte* data, std::size_t count) -{ - PHYSFS_File* file = reinterpret_cast(handle); - const PHYSFS_uint16* data16 = reinterpret_cast(data); - - for (std::size_t i = 0; i < count; ++i) - { - if (!PHYSFS_writeULE16(file, *data16)) - { - m_error = true; - throw serialize_error(PHYSFS_getLastError()); - //return i; - } - - ++data16; - } - - return count; -} - -std::size_t serialize_context::write16_be(const std::byte* data, std::size_t count) -{ - PHYSFS_File* file = reinterpret_cast(handle); - const PHYSFS_uint16* data16 = reinterpret_cast(data); - - for (std::size_t i = 0; i < count; ++i) - { - if (!PHYSFS_writeUBE16(file, *data16)) - { - m_error = true; - throw serialize_error(PHYSFS_getLastError()); - //return i; - } - - ++data16; - } - - return count; -} - -std::size_t serialize_context::write32_le(const std::byte* data, std::size_t count) -{ - PHYSFS_File* file = reinterpret_cast(handle); - const PHYSFS_uint32* data32 = reinterpret_cast(data); - - for (std::size_t i = 0; i < count; ++i) - { - if (!PHYSFS_writeULE32(file, *data32)) - { - m_error = true; - throw serialize_error(PHYSFS_getLastError()); - //return i; - } - - ++data32; - } - - return count; -} - -std::size_t serialize_context::write32_be(const std::byte* data, std::size_t count) -{ - PHYSFS_File* file = reinterpret_cast(handle); - const PHYSFS_uint32* data32 = reinterpret_cast(data); - - for (std::size_t i = 0; i < count; ++i) - { - if (!PHYSFS_writeUBE32(file, *data32)) - { - m_error = true; - throw serialize_error(PHYSFS_getLastError()); - //return i; - } - - ++data32; - } - - return count; -} - -std::size_t serialize_context::write64_le(const std::byte* data, std::size_t count) -{ - PHYSFS_File* file = reinterpret_cast(handle); - const PHYSFS_uint64* data64 = reinterpret_cast(data); - - for (std::size_t i = 0; i < count; ++i) - { - if (!PHYSFS_writeULE64(file, *data64)) - { - m_error = true; - throw serialize_error(PHYSFS_getLastError()); - //return i; - } - - ++data64; - } - - return count; -} - -std::size_t serialize_context::write64_be(const std::byte* data, std::size_t count) -{ - PHYSFS_File* file = reinterpret_cast(handle); - const PHYSFS_uint64* data64 = reinterpret_cast(data); - - for (std::size_t i = 0; i < count; ++i) - { - if (!PHYSFS_writeUBE64(file, *data64)) - { - m_error = true; - throw serialize_error(PHYSFS_getLastError()); - //return i; - } - - ++data64; - } - - return count; -} diff --git a/src/resources/serializer.cpp b/src/resources/serializer.cpp deleted file mode 100644 index 3a66cc9..0000000 --- a/src/resources/serializer.cpp +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "resources/serializer.hpp" -#include - -template <> -void serializer::serialize(const bool& value, serialize_context& ctx) -{ - const std::uint8_t temp = (value) ? 1 : 0; - ctx.write8(reinterpret_cast(&temp), 1); -}; - -template <> -void serializer::serialize(const std::uint8_t& value, serialize_context& ctx) -{ - ctx.write8(reinterpret_cast(&value), 1); -}; - -template <> -void serializer::serialize(const std::uint16_t& value, serialize_context& ctx) -{ - ctx.write16(reinterpret_cast(&value), 1); -}; - -template <> -void serializer::serialize(const std::uint32_t& value, serialize_context& ctx) -{ - ctx.write32(reinterpret_cast(&value), 1); -}; - -template <> -void serializer::serialize(const std::uint64_t& value, serialize_context& ctx) -{ - ctx.write64(reinterpret_cast(&value), 1); -}; - -template <> -void serializer::serialize(const std::int8_t& value, serialize_context& ctx) -{ - ctx.write8(reinterpret_cast(&value), 1); -}; - -template <> -void serializer::serialize(const std::int16_t& value, serialize_context& ctx) -{ - ctx.write16(reinterpret_cast(&value), 1); -}; - -template <> -void serializer::serialize(const std::int32_t& value, serialize_context& ctx) -{ - ctx.write32(reinterpret_cast(&value), 1); -}; - -template <> -void serializer::serialize(const std::int64_t& value, serialize_context& ctx) -{ - ctx.write64(reinterpret_cast(&value), 1); -}; - -template <> -void serializer::serialize(const float& value, serialize_context& ctx) -{ - ctx.write32(reinterpret_cast(&value), 1); -}; - -template <> -void serializer::serialize(const double& value, serialize_context& ctx) -{ - ctx.write64(reinterpret_cast(&value), 1); -}; - -template <> -void serializer::serialize(const std::string& value, serialize_context& ctx) -{ - const std::uint64_t length = static_cast(value.length()); - ctx.write64(reinterpret_cast(&length), 1); - ctx.write8(reinterpret_cast(value.data()), static_cast(length)); -}; - -template <> -void serializer::serialize(const std::u8string& value, serialize_context& ctx) -{ - const std::uint64_t length = static_cast(value.length()); - ctx.write64(reinterpret_cast(&length), 1); - ctx.write8(reinterpret_cast(value.data()), static_cast(length)); -}; - -template <> -void serializer::serialize(const std::u16string& value, serialize_context& ctx) -{ - const std::uint64_t length = static_cast(value.length()); - ctx.write64(reinterpret_cast(&length), 1); - ctx.write16(reinterpret_cast(value.data()), static_cast(length)); -}; - -template <> -void serializer::serialize(const std::u32string& value, serialize_context& ctx) -{ - const std::uint64_t length = static_cast(value.length()); - ctx.write64(reinterpret_cast(&length), 1); - ctx.write32(reinterpret_cast(value.data()), static_cast(length)); -}; diff --git a/src/resources/serializer.hpp b/src/resources/serializer.hpp deleted file mode 100644 index 2482c70..0000000 --- a/src/resources/serializer.hpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_RESOURCES_SERIALIZER_HPP -#define ANTKEEPER_RESOURCES_SERIALIZER_HPP - -#include "resources/serialize-context.hpp" - -/** - * Specializations of serializer define the serialization process for a given type. - * - * @tparam T Serializable type. - */ -template -struct serializer -{ - /** - * Serializes a value. - * - * @param value Value to serialize. - * @param ctx Serialize context. - */ - void serialize(const T& value, serialize_context& ctx); -}; - -#endif // ANTKEEPER_RESOURCES_SERIALIZER_HPP diff --git a/src/resources/shader-loader.cpp b/src/resources/shader-loader.cpp deleted file mode 100644 index 9e07e6c..0000000 --- a/src/resources/shader-loader.cpp +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "resources/resource-loader.hpp" -#include "resources/resource-manager.hpp" -#include "resources/text-file.hpp" -#include "render/shader-template.hpp" -#include "gl/shader-object.hpp" -#include "gl/shader-program.hpp" -#include -#include - -/** - * Scans a text file for the presence of a `#pragma once` directive. - * - * @param source Text file to scan. - * - * @return `true` if the file contains a `#pragma once` directive, `false` otherwise. - */ -static bool has_pragma_once(const text_file& source) -{ - for (const auto& line: source) - { - std::istringstream line_stream(line); - std::string token; - - // If line contains a `#pragma once` directive - if (line_stream >> token && token == "#pragma" && - line_stream >> token && token == "once") - { - return true; - } - } - - return false; -} - -/** - * Handles `#pragma include` directives by loading the specified text files and inserting them in place. - */ -static void handle_includes(text_file& source, std::unordered_set& include_once, resource_manager* resource_manager) -{ - // For each line in the source - for (std::size_t i = 0; i < source.size(); ++i) - { - std::string token; - std::istringstream line_stream(source[i]); - - // If line contains a `#pragma include` directive - if (line_stream >> token && token == "#pragma" && - line_stream >> token && token == "include") - { - // If third token is enclosed in quotes or angled brackets - if (line_stream >> token && token.size() > 2 && - ((token.front() == '\"' && token.back() == '\"') || - (token.front() == '<' && token.back() == '>'))) - { - // Extract include path - std::string path = token.substr(1, token.length() - 2); - - // Load include file - text_file* include_file = resource_manager->load(path); - if (!include_file) - { - source[i] = "#error file not found (" + path + ")"; - } - else - { - // If file has not been included or has no `#pragma once` directive - if (!include_once.contains(include_file)) - { - // If file has `#pragma once` directive - if (has_pragma_once(*include_file)) - { - // Add file to set of files to include once - include_once.insert(include_file); - } - - // Create a copy of the include file - text_file include_file_copy = *include_file; - - // Handle `#pragma include` directives inside include file - handle_includes(include_file_copy, include_once, resource_manager); - - // Replace #pragma include directive with include file contents - source.erase(source.begin() + i); - source.insert(source.begin() + i, include_file_copy.begin(), include_file_copy.end()); - i += include_file_copy.size() - 1; - } - } - } - else - { - source[i] = "#error malformed include directive (" + source[i] + ")"; - } - } - } -} - -template <> -gl::shader_program* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) -{ - // Load shader source file - text_file* source_file = resource_loader::load(resource_manager, file, path); - - // Make a copy of the shader source file - text_file source_file_copy = *source_file; - - // Handle `#pragma include` directives - std::unordered_set include_once; - include_once.insert(source_file); - handle_includes(source_file_copy, include_once, resource_manager); - - // Join vector of source lines into single string - std::ostringstream stream; - std::copy(source_file_copy.begin(), source_file_copy.end(), std::ostream_iterator(stream, "\n")); - - // Create shader template - render::shader_template* shader = new render::shader_template(stream.str()); - - // Build shader program - gl::shader_program* program = shader->build(render::shader_template::dictionary_type()); - - // Check if shader program was linked successfully - if (!program->was_linked()) - { - throw std::runtime_error("Shader program linking failed: " + program->get_info_log()); - } - - // Destroy shader template - delete shader; - - return program; -} - -template <> -render::shader_template* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) -{ - // Load shader template source file - text_file* source_file = resource_loader::load(resource_manager, file, path); - - // Make a copy of the shader template source file - text_file source_file_copy = *source_file; - - // Handle `#pragma include` directives - std::unordered_set include_once; - include_once.insert(source_file); - handle_includes(source_file_copy, include_once, resource_manager); - - // Join vector of source lines into single string - std::ostringstream stream; - std::copy(source_file_copy.begin(), source_file_copy.end(), std::ostream_iterator(stream, "\n")); - - // Create shader template - render::shader_template* shader = new render::shader_template(stream.str()); - - return shader; -} diff --git a/src/resources/string-map-loader.cpp b/src/resources/string-map-loader.cpp deleted file mode 100644 index 6dd3ced..0000000 --- a/src/resources/string-map-loader.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "resources/resource-loader.hpp" -#include "resources/serializer.hpp" -#include "resources/deserializer.hpp" -#include "i18n/string-map.hpp" - -template <> -i18n::string_map* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) -{ - i18n::string_map* map = new i18n::string_map(); - - deserialize_context ctx(file); - deserializer().deserialize(*map, ctx); - - return map; -} - -template <> -void resource_loader::save(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path, const i18n::string_map* map) -{ - serialize_context ctx(file); - serializer().serialize(*map, ctx); -} diff --git a/src/resources/string-table-loader.cpp b/src/resources/string-table-loader.cpp deleted file mode 100644 index 564487f..0000000 --- a/src/resources/string-table-loader.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "resources/resource-loader.hpp" -#include "i18n/string-table.hpp" -#include "resources/deserialize-error.hpp" -#include - -template <> -i18n::string_table* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) -{ - i18n::string_table* table = new i18n::string_table(); - - i18n::string_table_row row; - std::string entry; - - for (;;) - { - char c; - const PHYSFS_sint64 status = PHYSFS_readBytes(file, &c, 1); - - if (status == 1) - { - if (c == '\t') - { - row.push_back(entry); - entry.clear(); - } - else if (c == '\n') - { - row.push_back(entry); - entry.clear(); - table->push_back(row); - row.clear(); - } - else if (c != '\r') - { - entry.push_back(c); - } - } - else - { - if (PHYSFS_eof(file)) - { - if (!entry.empty()) - { - row.push_back(entry); - } - if (!row.empty()) - { - table->push_back(row); - } - break; - } - else - { - throw deserialize_error(PHYSFS_getLastError()); - } - } - } - - return table; -} diff --git a/src/resources/text-file-loader.cpp b/src/resources/text-file-loader.cpp deleted file mode 100644 index 2ad852b..0000000 --- a/src/resources/text-file-loader.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "resources/resource-loader.hpp" -#include "resources/text-file.hpp" -#include - -template <> -text_file* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) -{ - text_file* text = new text_file(); - std::string line; - - while (!PHYSFS_eof(file)) - { - physfs_getline(file, line); - text->push_back(line); - } - - return text; -} diff --git a/src/resources/texture-loader.cpp b/src/resources/texture-loader.cpp deleted file mode 100644 index 97cc6be..0000000 --- a/src/resources/texture-loader.cpp +++ /dev/null @@ -1,259 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "resources/resource-loader.hpp" -#include "resources/resource-manager.hpp" -#include "resources/image.hpp" -#include "gl/pixel-type.hpp" -#include "gl/pixel-format.hpp" -#include "gl/color-space.hpp" -#include "gl/texture-1d.hpp" -#include "gl/texture-2d.hpp" -#include "gl/texture-wrapping.hpp" -#include "gl/texture-filter.hpp" -#include -#include -#include -#include - -template <> -gl::texture_1d* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) -{ - // Read file into buffer - std::size_t size = static_cast(PHYSFS_fileLength(file)); - std::string buffer; - buffer.resize(size); - PHYSFS_readBytes(file, &buffer[0], size); - - // Parse json from file buffer - nlohmann::json json = nlohmann::json::parse(buffer); - - // Read image filename - std::string image_filename; - if (auto element = json.find("image"); element != json.end()) - image_filename = element.value().get(); - - // Read color space - gl::color_space color_space = gl::color_space::linear; - if (auto element = json.find("color_space"); element != json.end()) - { - std::string value = element.value().get(); - if (value == "linear") - color_space = gl::color_space::linear; - else if (value == "srgb") - color_space = gl::color_space::srgb; - } - - // Read extension mode - gl::texture_wrapping wrapping = gl::texture_wrapping::repeat; - if (auto element = json.find("extension"); element != json.end()) - { - std::string value = element.value().get(); - if (value == "clip") - wrapping = gl::texture_wrapping::clip; - else if (value == "extend") - wrapping = gl::texture_wrapping::extend; - else if (value == "repeat") - wrapping = gl::texture_wrapping::repeat; - else if (value == "mirrored_repeat") - wrapping = gl::texture_wrapping::mirrored_repeat; - } - - // Read interpolation mode - gl::texture_min_filter min_filter = gl::texture_min_filter::linear_mipmap_linear; - gl::texture_mag_filter mag_filter = gl::texture_mag_filter::linear; - if (auto element = json.find("interpolation"); element != json.end()) - { - std::string value = element.value().get(); - if (value == "linear") - { - min_filter = gl::texture_min_filter::linear_mipmap_linear; - mag_filter = gl::texture_mag_filter::linear; - } - else if (value == "closest") - { - min_filter = gl::texture_min_filter::nearest_mipmap_nearest; - mag_filter = gl::texture_mag_filter::nearest; - } - } - - // Read max anisotropy - float max_anisotropy = 0.0f; - if (auto element = json.find("max_anisotropy"); element != json.end()) - max_anisotropy = element.value().get(); - - // Load image - ::image* image = resource_manager->load<::image>(image_filename); - - // Determine pixel type - gl::pixel_type type = (image->get_component_size() == sizeof(float)) ? gl::pixel_type::float_32 : gl::pixel_type::uint_8; - - // Determine pixel format - gl::pixel_format format; - if (image->get_channel_count() == 1) - { - format = gl::pixel_format::r; - } - else if (image->get_channel_count() == 2) - { - format = gl::pixel_format::rg; - } - else if (image->get_channel_count() == 3) - { - format = gl::pixel_format::rgb; - } - else if (image->get_channel_count() == 4) - { - format = gl::pixel_format::rgba; - } - else - { - std::stringstream stream; - stream << std::string("Texture cannot be created from an image with an unsupported number of channels (") << image->get_channel_count() << std::string(")."); - delete image; - throw std::runtime_error(stream.str().c_str()); - } - - // Create texture - gl::texture_1d* texture = new gl::texture_1d(image->get_width(), type, format, color_space, image->data()); - - // Set wrapping and filtering - texture->set_wrapping(wrapping); - texture->set_filters(min_filter, mag_filter); - texture->set_max_anisotropy(max_anisotropy); - - // Free loaded image - resource_manager->unload(image_filename); - - return texture; -} - -template <> -gl::texture_2d* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) -{ - // Read file into buffer - std::size_t size = static_cast(PHYSFS_fileLength(file)); - std::string buffer; - buffer.resize(size); - PHYSFS_readBytes(file, &buffer[0], size); - - // Parse json from file buffer - nlohmann::json json = nlohmann::json::parse(buffer); - - // Read image filename - std::string image_filename; - if (auto element = json.find("image"); element != json.end()) - image_filename = element.value().get(); - - // Read color space - gl::color_space color_space = gl::color_space::linear; - if (auto element = json.find("color_space"); element != json.end()) - { - std::string value = element.value().get(); - if (value == "linear") - color_space = gl::color_space::linear; - else if (value == "srgb") - color_space = gl::color_space::srgb; - } - - // Read extension mode - gl::texture_wrapping wrapping = gl::texture_wrapping::repeat; - if (auto element = json.find("extension"); element != json.end()) - { - std::string value = element.value().get(); - if (value == "clip") - wrapping = gl::texture_wrapping::clip; - else if (value == "extend") - wrapping = gl::texture_wrapping::extend; - else if (value == "repeat") - wrapping = gl::texture_wrapping::repeat; - else if (value == "mirrored_repeat") - wrapping = gl::texture_wrapping::mirrored_repeat; - } - - // Read interpolation mode - gl::texture_min_filter min_filter = gl::texture_min_filter::linear_mipmap_linear; - gl::texture_mag_filter mag_filter = gl::texture_mag_filter::linear; - if (auto element = json.find("interpolation"); element != json.end()) - { - std::string value = element.value().get(); - if (value == "linear") - { - min_filter = gl::texture_min_filter::linear_mipmap_linear; - mag_filter = gl::texture_mag_filter::linear; - } - else if (value == "closest") - { - min_filter = gl::texture_min_filter::nearest_mipmap_nearest; - mag_filter = gl::texture_mag_filter::nearest; - } - } - - // Read max anisotropy - float max_anisotropy = 0.0f; - if (auto element = json.find("max_anisotropy"); element != json.end()) - max_anisotropy = element.value().get(); - - // Load image - ::image* image = resource_manager->load<::image>(image_filename); - - // Determine pixel type - gl::pixel_type type = (image->get_component_size() == sizeof(float)) ? gl::pixel_type::float_32 : gl::pixel_type::uint_8; - - // Determine pixel format - gl::pixel_format format; - if (image->get_channel_count() == 1) - { - format = gl::pixel_format::r; - } - else if (image->get_channel_count() == 2) - { - format = gl::pixel_format::rg; - } - else if (image->get_channel_count() == 3) - { - format = gl::pixel_format::rgb; - } - else if (image->get_channel_count() == 4) - { - format = gl::pixel_format::rgba; - } - else - { - std::stringstream stream; - stream << std::string("Texture cannot be created from an image with an unsupported number of channels (") << image->get_channel_count() << std::string(")."); - delete image; - throw std::runtime_error(stream.str().c_str()); - } - - // Create texture - gl::texture_2d* texture = new gl::texture_2d(image->get_width(), image->get_height(), type, format, color_space, image->data()); - - // Set wrapping and filtering - texture->set_wrapping(wrapping, wrapping); - texture->set_filters(min_filter, mag_filter); - texture->set_max_anisotropy(max_anisotropy); - - // Free loaded image - resource_manager->unload(image_filename); - - return texture; -} - - diff --git a/src/resources/typeface-loader.cpp b/src/resources/typeface-loader.cpp deleted file mode 100644 index 875da27..0000000 --- a/src/resources/typeface-loader.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "resource-loader.hpp" -#include "type/freetype/typeface.hpp" -#include -#include -#include -#include FT_FREETYPE_H - -template <> -type::typeface* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) -{ - FT_Error error = 0; - - // Init FreeType library object - FT_Library library; - error = FT_Init_FreeType(&library); - if (error) - { - throw std::runtime_error("Failed to init FreeType library (error code " + std::to_string(error) + ")"); - } - - // Read file into buffer - std::size_t size = static_cast(PHYSFS_fileLength(file)); - unsigned char* buffer = new unsigned char[size]; - PHYSFS_readBytes(file, buffer, size); - - // Load FreeType face from buffer - FT_Face face; - error = FT_New_Memory_Face(library, buffer, size, 0, &face); - if (error) - { - delete[] buffer; - FT_Done_FreeType(library); - throw std::runtime_error("Failed to load FreeType face (error code " + std::to_string(error) + ")"); - } - - return new type::freetype::typeface(library, face, buffer); -} diff --git a/src/scene/ambient-light.hpp b/src/scene/ambient-light.hpp deleted file mode 100644 index c2a7f5e..0000000 --- a/src/scene/ambient-light.hpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_SCENE_AMBIENT_LIGHT_HPP -#define ANTKEEPER_SCENE_AMBIENT_LIGHT_HPP - -#include "scene/light.hpp" - -namespace scene { - -class ambient_light: public light -{ -public: - /// Returns light_type::ambient - virtual light_type get_light_type() const; -}; - -inline light_type ambient_light::get_light_type() const -{ - return light_type::ambient; -} - -} // namespace scene - -#endif // ANTKEEPER_SCENE_AMBIENT_LIGHT_HPP - diff --git a/src/scene/billboard.cpp b/src/scene/billboard.cpp deleted file mode 100644 index 4b18ffb..0000000 --- a/src/scene/billboard.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "scene/billboard.hpp" -#include "config.hpp" - -namespace scene { - -const typename billboard::aabb_type billboard::local_bounds = {{-1, -1, -1}, {1, 1, 1}}; - -billboard::billboard(): - world_bounds(local_bounds), - material(nullptr), - type(billboard_type::flat), - alignment_axis(config::global_up) -{} - -billboard::billboard(const billboard& other) -{ - *this = other; -} - -billboard& billboard::operator=(const billboard& other) -{ - material = other.material; - type = other.type; - alignment_axis = other.alignment_axis; - set_transform(other.get_transform()); - set_active(other.is_active()); - set_culling_mask(other.get_culling_mask()); - return *this; -} - -void billboard::set_material(render::material* material) -{ - this->material = material; -} - -void billboard::set_billboard_type(billboard_type type) -{ - this->type = type; -} - -void billboard::set_alignment_axis(const float3& axis) -{ - this->alignment_axis = axis; -} - -void billboard::transformed() -{ - world_bounds = aabb_type::transform(local_bounds, get_transform()); -} - -void billboard::update_tweens() -{ - object_base::update_tweens(); - if (material) - { - material->update_tweens(); - } -} - -} // namespace scene diff --git a/src/scene/billboard.hpp b/src/scene/billboard.hpp deleted file mode 100644 index 82fed10..0000000 --- a/src/scene/billboard.hpp +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_SCENE_BILLBOARD_HPP -#define ANTKEEPER_SCENE_BILLBOARD_HPP - -#include "scene/object.hpp" -#include "geom/aabb.hpp" -#include "utility/fundamental-types.hpp" -#include "render/material.hpp" - -namespace scene { - -/// Enumerates billboard types. -enum class billboard_type -{ - // No alignment - flat, - - // Aligns to face camera - spherical, - - // Rotates about an alignment axis to face camera - cylindrical -}; - -/** - * A 2D unit quad with one material. - */ -class billboard: public object -{ -public: - typedef geom::aabb aabb_type; - - billboard(); - billboard(const billboard& other); - billboard& operator=(const billboard& other); - - void set_material(render::material* material); - - /// Sets the billboard alignment mode. - void set_billboard_type(billboard_type type); - - /// Sets the axis around which the billboard will be rotated when the alignment is set to billboard_alignment::cylindrical. - void set_alignment_axis(const float3& axis); - - virtual const bounding_volume_type& get_local_bounds() const; - virtual const bounding_volume_type& get_world_bounds() const; - - render::material* get_material() const; - billboard_type get_billboard_type() const; - const float3& get_alignment_axis() const; - - virtual void update_tweens(); - -private: - static const aabb_type local_bounds; - - virtual void transformed(); - - - aabb_type world_bounds; - render::material* material; - billboard_type type; - float3 alignment_axis; -}; - -inline const typename object_base::bounding_volume_type& billboard::get_local_bounds() const -{ - return local_bounds; -} - -inline const typename object_base::bounding_volume_type& billboard::get_world_bounds() const -{ - return world_bounds; -} - -inline render::material* billboard::get_material() const -{ - return material; -} - -inline billboard_type billboard::get_billboard_type() const -{ - return type; -} - -inline const float3& billboard::get_alignment_axis() const -{ - return alignment_axis; -} - -} // namespace scene - -#endif // ANTKEEPER_SCENE_BILLBOARD_HPP - diff --git a/src/scene/camera.cpp b/src/scene/camera.cpp deleted file mode 100644 index f51b717..0000000 --- a/src/scene/camera.cpp +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "scene/camera.hpp" -#include "config.hpp" -#include "math/numbers.hpp" -#include "math/interpolation.hpp" -#include "math/quaternion.hpp" -#include "math/projection.hpp" - -namespace scene { - -static float4x4 interpolate_view(const camera* camera, const float4x4& x, const float4x4& y, float a) -{ - math::transform transform = camera->get_transform_tween().interpolate(a); - float3 forward = transform.rotation * config::global_forward; - float3 up = transform.rotation * config::global_up; - return math::look_at(transform.translation, transform.translation + forward, up); -} - -static float4x4 interpolate_projection(const camera* camera, const float4x4& x, const float4x4& y, float a) -{ - if (camera->is_orthographic()) - { - return math::ortho( - camera->get_clip_left_tween().interpolate(a), - camera->get_clip_right_tween().interpolate(a), - camera->get_clip_bottom_tween().interpolate(a), - camera->get_clip_top_tween().interpolate(a), - camera->get_clip_far_tween().interpolate(a), - camera->get_clip_near_tween().interpolate(a)); - } - else - { - return math::perspective( - camera->get_fov_tween().interpolate(a), - camera->get_aspect_ratio_tween().interpolate(a), - camera->get_clip_far_tween().interpolate(a), - camera->get_clip_near_tween().interpolate(a)); - } -} - -static float4x4 interpolate_view_projection(const camera* camera, const float4x4& x, const float4x4& y, float a) -{ - return camera->get_projection_tween().interpolate(a) * camera->get_view_tween().interpolate(a); -} - -camera::camera(): - compositor(nullptr), - composite_index(0), - orthographic(true), - clip_left(-1.0f, math::lerp), - clip_right(1.0f, math::lerp), - clip_bottom(-1.0f, math::lerp), - clip_top(1.0f, math::lerp), - clip_near(-1.0f, math::lerp), - clip_far(1.0f, math::lerp), - fov(math::half_pi, math::lerp), - aspect_ratio(1.0f, math::lerp), - view(math::matrix4::identity(), std::bind(&interpolate_view, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)), - projection(math::matrix4::identity(), std::bind(&interpolate_projection, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)), - view_projection(math::matrix4::identity(), std::bind(&interpolate_view_projection, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)), - exposure(0.0f, math::lerp) -{} - -geom::primitive::ray camera::pick(const float2& ndc) const -{ - const float4x4 inverse_view_projection = math::inverse(view_projection[1]); - - const float4 near = inverse_view_projection * float4{ndc[0], ndc[1], 1.0f, 1.0f}; - const float4 far = inverse_view_projection * float4{ndc[0], ndc[1], 0.0f, 1.0f}; - - const float3 origin = float3{near[0], near[1], near[2]} / near[3]; - const float3 direction = math::normalize(float3{far[0], far[1], far[2]} / far[3] - origin); - - return {origin, direction}; -} - -float3 camera::project(const float3& object, const float4& viewport) const -{ - float4 result = view_projection[1] * float4{object[0], object[1], object[2], 1.0f}; - result[0] = (result[0] / result[3]) * 0.5f + 0.5f; - result[1] = (result[1] / result[3]) * 0.5f + 0.5f; - result[2] = (result[2] / result[3]) * 0.5f + 0.5f; - - result[0] = result[0] * viewport[2] + viewport[0]; - result[1] = result[1] * viewport[3] + viewport[1]; - - return math::vector(result); -} - -float3 camera::unproject(const float3& window, const float4& viewport) const -{ - float4 result; - result[0] = ((window[0] - viewport[0]) / viewport[2]) * 2.0f - 1.0f; - result[1] = ((window[1] - viewport[1]) / viewport[3]) * 2.0f - 1.0f; - //result[2] = window[2] * 2.0f - 1.0f; z: [-1, 1] - //result[2] = window[2]; // z: [0, 1] - result[2] = 1.0f - window[2]; // z: [1, 0] - result[3] = 1.0f; - - result = math::inverse(view_projection[1]) * result; - - return math::vector(result) * (1.0f / result[3]); -} - -void camera::set_perspective(float fov, float aspect_ratio, float clip_near, float clip_far) -{ - orthographic = false; - - this->fov[1] = fov; - this->aspect_ratio[1] = aspect_ratio; - this->clip_near[1] = clip_near; - this->clip_far[1] = clip_far; - - projection[1] = math::perspective_half_z(fov, aspect_ratio, clip_far, clip_near); - - // Recalculate view-projection matrix - view_projection[1] = projection[1] * view[1]; - - // Recalculate view frustum - /// @TODO: this is a hack to fix the half z projection matrix view frustum - view_frustum.set_matrix(math::perspective(this->fov[1], this->aspect_ratio[1], this->clip_near[1], this->clip_far[1]) * view[1]); -} - -void camera::set_orthographic(float clip_left, float clip_right, float clip_bottom, float clip_top, float clip_near, float clip_far) -{ - orthographic = true; - - this->clip_left[1] = clip_left; - this->clip_right[1] = clip_right; - this->clip_bottom[1] = clip_bottom; - this->clip_top[1] = clip_top; - this->clip_near[1] = clip_near; - this->clip_far[1] = clip_far; - - projection[1] = math::ortho_half_z(clip_left, clip_right, clip_bottom, clip_top, clip_far, clip_near); - - // Recalculate view-projection matrix - view_projection[1] = projection[1] * view[1]; - - // Recalculate view frustum - view_frustum.set_matrix(view_projection[1]); -} - -void camera::set_exposure(float ev100) -{ - exposure[1] = ev100; -} - -void camera::set_compositor(render::compositor* compositor) -{ - this->compositor = compositor; -} - -void camera::set_composite_index(int index) -{ - composite_index = index; -} - -void camera::update_tweens() -{ - object_base::update_tweens(); - clip_left.update(); - clip_right.update(); - clip_bottom.update(); - clip_top.update(); - clip_near.update(); - clip_far.update(); - fov.update(); - aspect_ratio.update(); - view.update(); - projection.update(); - view_projection.update(); - exposure.update(); -} - -void camera::transformed() -{ - // Recalculate view and view-projection matrices - float3 forward = get_rotation() * config::global_forward; - float3 up = get_rotation() * config::global_up; - view[1] = math::look_at(get_translation(), get_translation() + forward, up); - view_projection[1] = projection[1] * view[1]; - - // Recalculate view frustum - /// @TODO: this is a hack to fix the half z projection matrix view frustum - if (orthographic) - view_frustum.set_matrix(view_projection[1]); - else - view_frustum.set_matrix(math::perspective(fov[1], aspect_ratio[1], clip_near[1], clip_far[1]) * view[1]); -} - -} // namespace scene diff --git a/src/scene/camera.hpp b/src/scene/camera.hpp deleted file mode 100644 index 53b083b..0000000 --- a/src/scene/camera.hpp +++ /dev/null @@ -1,328 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_SCENE_CAMERA_HPP -#define ANTKEEPER_SCENE_CAMERA_HPP - -#include "scene/object.hpp" -#include "geom/view-frustum.hpp" -#include "geom/primitive/ray.hpp" -#include "utility/fundamental-types.hpp" -#include "render/compositor.hpp" - -namespace scene { - -/** - * - */ -class camera: public object -{ -public: - typedef geom::view_frustum view_frustum_type; - - camera(); - - /** - * Constructs a picking ray from normalized device coordinates (NDC). - * - * @param ndc NDC coordinates. - * - * @return Picking ray. - */ - geom::primitive::ray pick(const float2& ndc) const; - - /** - * Maps object coordinates to window coordinates. - * - * @param object Object coordinates. - * @param viewport Vector containing the `x`, `y`, `w`, and `h` of the viewport. - * @return Projected window coordinates. - */ - float3 project(const float3& object, const float4& viewport) const; - - /** - * Maps window coordinates to object coordinates. - * - * @param window Window coordinates. - * @param viewport Vector containing the `x`, `y`, `w`, and `h` of the viewport. - * @return Unprojected object coordinates. - */ - float3 unproject(const float3& window, const float4& viewport) const; - - /** - * Sets the camera's projection matrix using perspective projection. - * - * @param fov Vertical field of view. - * @param aspect_ratio Aspect ratio. - * @param clip_near Distance to near clipping plane. - * @param clip_far Distance to far clipping plane. - */ - void set_perspective(float fov, float aspect_ratio, float clip_near, float clip_far); - - /** - * Sets the camera's projection matrix using orthographic projection. - * - * @param clip_left Signed distance to left clipping plane. - * @param clip_right Signed distance to right clipping plane. - * @param clip_bottom Signed distance to bottom clipping plane. - * @param clip_top Signed distance to top clipping plane. - * @param clip_near Signed distance to near clipping plane. - * @param clip_far Signed distance to far clipping plane. - */ - void set_orthographic(float clip_left, float clip_right, float clip_bottom, float clip_top, float clip_near, float clip_far); - - /** - * Sets the camera's ISO 100 exposure value directly - * - * @param ev100 ISO 100 exposure value. - */ - void set_exposure(float ev100); - - void set_compositor(render::compositor* compositor); - void set_composite_index(int index); - - - virtual const bounding_volume_type& get_local_bounds() const; - virtual const bounding_volume_type& get_world_bounds() const; - - float is_orthographic() const; - float get_clip_left() const; - float get_clip_right() const; - float get_clip_bottom() const; - float get_clip_top() const; - float get_clip_near() const; - float get_clip_far() const; - float get_fov() const; - float get_aspect_ratio() const; - - /// Returns the camera's view matrix. - const float4x4& get_view() const; - - /// Returns the camera's projection matrix. - const float4x4& get_projection() const; - - /// Returns the camera's view-projection matrix. - const float4x4& get_view_projection() const; - - /// Returns the camera's view frustum. - const view_frustum_type& get_view_frustum() const; - - /// Returns the camera's ISO 100 exposure value. - float get_exposure() const; - - const render::compositor* get_compositor() const; - render::compositor* get_compositor(); - int get_composite_index() const; - - const tween& get_clip_left_tween() const; - const tween& get_clip_right_tween() const; - const tween& get_clip_bottom_tween() const; - const tween& get_clip_top_tween() const; - const tween& get_clip_near_tween() const; - const tween& get_clip_far_tween() const; - const tween& get_fov_tween() const; - const tween& get_aspect_ratio_tween() const; - const tween& get_view_tween() const; - const tween& get_projection_tween() const; - const tween& get_view_projection_tween() const; - const tween& get_exposure_tween() const; - - /// @copydoc object_base::update_tweens(); - virtual void update_tweens(); - -private: - virtual void transformed(); - - render::compositor* compositor; - int composite_index; - bool orthographic; - tween clip_left; - tween clip_right; - tween clip_bottom; - tween clip_top; - tween clip_near; - tween clip_far; - tween fov; - tween aspect_ratio; - tween view; - tween projection; - tween view_projection; - tween exposure; - view_frustum_type view_frustum; -}; - -inline const typename object_base::bounding_volume_type& camera::get_local_bounds() const -{ - /// @TODO: return local bounds, not world bounds - return view_frustum.get_bounds(); -} - -inline const typename object_base::bounding_volume_type& camera::get_world_bounds() const -{ - return view_frustum.get_bounds(); -} - -inline float camera::is_orthographic() const -{ - return orthographic; -} - -inline float camera::get_clip_left() const -{ - return clip_left[1]; -} - -inline float camera::get_clip_right() const -{ - return clip_right[1]; -} - -inline float camera::get_clip_bottom() const -{ - return clip_bottom[1]; -} - -inline float camera::get_clip_top() const -{ - return clip_top[1]; -} - -inline float camera::get_clip_near() const -{ - return clip_near[1]; -} - -inline float camera::get_clip_far() const -{ - return clip_far[1]; -} - -inline float camera::get_fov() const -{ - return fov[1]; -} - -inline float camera::get_aspect_ratio() const -{ - return aspect_ratio[1]; -} - -inline const float4x4& camera::get_view() const -{ - return view[1]; -} - -inline const float4x4& camera::get_projection() const -{ - return projection[1]; -} - -inline const float4x4& camera::get_view_projection() const -{ - return view_projection[1]; -} - -inline const typename camera::view_frustum_type& camera::get_view_frustum() const -{ - return view_frustum; -} - -inline float camera::get_exposure() const -{ - return exposure[1]; -} - -inline const render::compositor* camera::get_compositor() const -{ - return compositor; -} - -inline render::compositor* camera::get_compositor() -{ - return compositor; -} - -inline int camera::get_composite_index() const -{ - return composite_index; -} - -inline const tween& camera::get_clip_left_tween() const -{ - return clip_left; -} - -inline const tween& camera::get_clip_right_tween() const -{ - return clip_right; -} - -inline const tween& camera::get_clip_bottom_tween() const -{ - return clip_bottom; -} - -inline const tween& camera::get_clip_top_tween() const -{ - return clip_top; -} - -inline const tween& camera::get_clip_near_tween() const -{ - return clip_near; -} - -inline const tween& camera::get_clip_far_tween() const -{ - return clip_far; -} - -inline const tween& camera::get_fov_tween() const -{ - return fov; -} - -inline const tween& camera::get_aspect_ratio_tween() const -{ - return aspect_ratio; -} - -inline const tween& camera::get_view_tween() const -{ - return view; -} - -inline const tween& camera::get_projection_tween() const -{ - return projection; -} - -inline const tween& camera::get_view_projection_tween() const -{ - return view_projection; -} - -inline const tween& camera::get_exposure_tween() const -{ - return exposure; -} - -} // namespace scene - -#endif // ANTKEEPER_SCENE_CAMERA_HPP diff --git a/src/scene/collection.cpp b/src/scene/collection.cpp deleted file mode 100644 index b42e225..0000000 --- a/src/scene/collection.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "scene/collection.hpp" -#include "scene/object.hpp" - -namespace scene { - -void collection::add_object(object_base* object) -{ - objects.push_back(object); - object_map[object->get_object_type_id()].push_back(object); -} - -void collection::remove_object(object_base* object) -{ - objects.remove(object); - object_map[object->get_object_type_id()].remove(object); -} - -void collection::remove_objects() -{ - objects.clear(); - object_map.clear(); -} - -void collection::update_tweens() -{ - for (object_base* object: objects) - { - object->update_tweens(); - } -} - -} // namespace scene diff --git a/src/scene/directional-light.cpp b/src/scene/directional-light.cpp deleted file mode 100644 index 1d8aa57..0000000 --- a/src/scene/directional-light.cpp +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "directional-light.hpp" -#include "config.hpp" -#include "math/quaternion.hpp" -#include "math/interpolation.hpp" - -namespace scene { - -static float3 interpolate_direction(const float3& x, const float3& y, float a) -{ - math::quaternion q0 = math::rotation(config::global_forward, x); - math::quaternion q1 = math::rotation(config::global_forward, y); - return math::normalize(math::slerp(q0, q1, a) * config::global_forward); -} - -directional_light::directional_light(): - direction(config::global_forward, interpolate_direction), - shadow_caster(false), - shadow_framebuffer(nullptr), - shadow_bias(0.005f), - shadow_cascade_count(4), - shadow_cascade_coverage(1.0f), - shadow_cascade_distribution(0.8f), - light_texture(nullptr), - light_texture_opacity(1.0f, math::lerp), - light_texture_scale({1.0f, 1.0f}, math::lerp) -{ - shadow_cascade_distances.resize(shadow_cascade_count); - shadow_cascade_matrices.resize(shadow_cascade_count); -} - -void directional_light::set_shadow_caster(bool caster) noexcept -{ - shadow_caster = caster; -} - -void directional_light::set_shadow_framebuffer(const gl::framebuffer* framebuffer) noexcept -{ - shadow_framebuffer = framebuffer; -} - -void directional_light::set_shadow_bias(float bias) noexcept -{ - shadow_bias = bias; -} - -void directional_light::set_shadow_cascade_count(unsigned int count) noexcept -{ - shadow_cascade_count = count; - shadow_cascade_distances.resize(shadow_cascade_count); - shadow_cascade_matrices.resize(shadow_cascade_count); -} - -void directional_light::set_shadow_cascade_coverage(float factor) noexcept -{ - shadow_cascade_coverage = factor; -} - -void directional_light::set_shadow_cascade_distribution(float weight) noexcept -{ - shadow_cascade_distribution = weight; -} - -void directional_light::set_light_texture(const gl::texture_2d* texture) -{ - light_texture = texture; -} - -void directional_light::set_light_texture_opacity(float opacity) -{ - light_texture_opacity[1] = opacity; -} - -void directional_light::set_light_texture_scale(const float2& scale) -{ - light_texture_scale[1] = scale; -} - -void directional_light::update_tweens() -{ - light::update_tweens(); - direction.update(); - - if (light_texture) - { - light_texture_opacity.update(); - light_texture_scale.update(); - } -} - -void directional_light::transformed() -{ - direction[1] = math::normalize(get_transform().rotation * config::global_forward); -} - -} // namespace scene diff --git a/src/scene/directional-light.hpp b/src/scene/directional-light.hpp deleted file mode 100644 index 5c02240..0000000 --- a/src/scene/directional-light.hpp +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_SCENE_DIRECTIONAL_LIGHT_HPP -#define ANTKEEPER_SCENE_DIRECTIONAL_LIGHT_HPP - -#include "scene/light.hpp" -#include "gl/texture-2d.hpp" -#include "utility/fundamental-types.hpp" -#include - -namespace scene { - -/** - * Light source with parallel rays and constant intensity. - */ -class directional_light: public light -{ -public: - /// Creates a directional light. - directional_light(); - - /// Returns light_type::directional. - virtual light_type get_light_type() const; - - /// Returns the normalized direction vector of the light. - const float3& get_direction() const; - - /// @copydoc object_base::update_tweens(); - virtual void update_tweens(); - - /// @name Shadow - /// @{ - - /** - * Enables or disables shadow casting. - * - * @param caster `true` if the light should cast shadows, `false` otherwise. - */ - void set_shadow_caster(bool caster) noexcept; - - /** - * Sets the shadow map framebuffer. - * - * @param framebuffer Pointer to a shadow map framebuffer. - */ - void set_shadow_framebuffer(const gl::framebuffer* framebuffer) noexcept; - - /** - * Sets the shadow bias factor for reducing self-shadowing. - * - * @param bias Shadow bias factor. - */ - void set_shadow_bias(float bias) noexcept; - - /** - * Sets the number of shadow cascades. - * - * @param count Number of shadow cascades. - */ - void set_shadow_cascade_count(unsigned int count) noexcept; - - /** - * Sets the shadow cascade coverage factor. - * - * @param factor Percentage of the view frustum clipping range covered by shadow cascades. A value of `1.0` results in full coverage of the view frustum clipping range, `0.5` results in coverage of half of the clipping range, etc. - */ - void set_shadow_cascade_coverage(float factor) noexcept; - - /** - * Sets the shadow cascade distribution. - * - * @param weight Linear interpolation weight between uniform and logarithmic cascade distributions. A weight of `0.0` results in a uniform cascade distribution, while `1.0` results in a logarithmic distribution. - */ - void set_shadow_cascade_distribution(float weight) noexcept; - - /// Returns `true` if the light casts shadows, `false` otherwise. - bool is_shadow_caster() const noexcept; - - /// Returns the shadow map framebuffer, of `nullptr` if no shadow map framebuffer is set. - const gl::framebuffer* get_shadow_framebuffer() const noexcept; - - /// Returns the shadow bias factor. - float get_shadow_bias() const noexcept; - - /// Returns the number of shadow cascades. - unsigned int get_shadow_cascade_count() const noexcept; - - /// Returns the shadow cascade coverage factor. - float get_shadow_cascade_coverage() const noexcept; - - /// Returns the shadow cascade distribution weight. - float get_shadow_cascade_distribution() const noexcept; - - /// Returns the array of shadow cascade far clipping plane distances. - float* get_shadow_cascade_distances() const noexcept; - - /// Returns the array of world-space to cascade texture-space transformation matrices. - float4x4* get_shadow_cascade_matrices() const noexcept; - - /// @} - - /// @name Light texture - /// @{ - - /** - * Sets the light texture, also known as a gobo, cucoloris, or cookie. - * - * @param texture Light texture. - */ - void set_light_texture(const gl::texture_2d* texture); - - /** - * Sets the opacity of the light texture. - * - * @param opacity Light texture opacity, on `[0, 1]`. - */ - void set_light_texture_opacity(float opacity); - - /** - * Sets the scale of the light texture. - * - * @param scale Scale of the light texture. - */ - void set_light_texture_scale(const float2& scale); - - /// Returns the light texture for this light, or `nullptr` if no light texture is set. - const gl::texture_2d* get_light_texture() const; - - /// Returns the light texture opacity. - float get_light_texture_opacity() const; - - /// Returns the light texture scale. - const float2& get_light_texture_scale() const; - - /// Returns the light direction tween. - const tween& get_direction_tween() const; - - /// Returns the light texture opacity tween. - const tween& get_light_texture_opacity_tween() const; - - /// Returns the light texture scale tween. - const tween& get_light_texture_scale_tween() const; - - /// @} - -private: - virtual void transformed(); - - tween direction; - - bool shadow_caster; - const gl::framebuffer* shadow_framebuffer; - float shadow_bias; - unsigned int shadow_cascade_count; - float shadow_cascade_coverage; - float shadow_cascade_distribution; - mutable std::vector shadow_cascade_distances; - mutable std::vector shadow_cascade_matrices; - - const gl::texture_2d* light_texture; - tween light_texture_opacity; - tween light_texture_scale; - -}; - -inline light_type directional_light::get_light_type() const -{ - return light_type::directional; -} - -inline const float3& directional_light::get_direction() const -{ - return direction[1]; -} - -inline bool directional_light::is_shadow_caster() const noexcept -{ - return shadow_caster; -} - -inline const gl::framebuffer* directional_light::get_shadow_framebuffer() const noexcept -{ - return shadow_framebuffer; -} - -inline float directional_light::get_shadow_bias() const noexcept -{ - return shadow_bias; -} - -inline unsigned int directional_light::get_shadow_cascade_count() const noexcept -{ - return shadow_cascade_count; -} - -inline float directional_light::get_shadow_cascade_coverage() const noexcept -{ - return shadow_cascade_coverage; -} - -inline float directional_light::get_shadow_cascade_distribution() const noexcept -{ - return shadow_cascade_distribution; -} - -inline float* directional_light::get_shadow_cascade_distances() const noexcept -{ - return shadow_cascade_distances.data(); -} - -inline float4x4* directional_light::get_shadow_cascade_matrices() const noexcept -{ - return shadow_cascade_matrices.data(); -} - -inline const gl::texture_2d* directional_light::get_light_texture() const -{ - return light_texture; -} - -inline float directional_light::get_light_texture_opacity() const -{ - return light_texture_opacity[1]; -} - -inline const float2& directional_light::get_light_texture_scale() const -{ - return light_texture_scale[1]; -} - -inline const tween& directional_light::get_direction_tween() const -{ - return direction; -} - -inline const tween& directional_light::get_light_texture_opacity_tween() const -{ - return light_texture_opacity; -} - -inline const tween& directional_light::get_light_texture_scale_tween() const -{ - return light_texture_scale; -} - -} // namespace scene - -#endif // ANTKEEPER_SCENE_DIRECTIONAL_LIGHT_HPP diff --git a/src/scene/light.cpp b/src/scene/light.cpp deleted file mode 100644 index caf77b0..0000000 --- a/src/scene/light.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "scene/light.hpp" -#include "math/interpolation.hpp" - -namespace scene { - -light::light(): - local_bounds{{0.0f, 0.0f, 0.0f}, 0.0f}, - world_bounds{{0.0f, 0.0f, 0.0f}, 0.0f}, - color(float3{1.0f, 1.0f, 1.0f}, math::lerp), - intensity(1.0f, math::lerp), - scaled_color(float3{1.0f, 1.0f, 1.0f}, math::lerp) -{} - -void light::set_color(const float3& color) -{ - this->color[1] = color; - scaled_color[1] = color * intensity[1]; -} - -void light::set_intensity(float intensity) -{ - this->intensity[1] = intensity; - scaled_color[1] = color[1] * intensity; -} - -void light::update_tweens() -{ - object_base::update_tweens(); - color.update(); - intensity.update(); - scaled_color.update(); -} - -void light::transformed() -{ - world_bounds = {get_translation(), 0.0f}; -} - -} // namespace scene diff --git a/src/scene/light.hpp b/src/scene/light.hpp deleted file mode 100644 index 31dccbe..0000000 --- a/src/scene/light.hpp +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_SCENE_LIGHT_HPP -#define ANTKEEPER_SCENE_LIGHT_HPP - -#include "scene/object.hpp" -#include "geom/sphere.hpp" -#include "utility/fundamental-types.hpp" - -namespace scene { - -/// Light object type enumerations. -enum class light_type -{ - /// Denotes an ambient light. - ambient, - - /// Denotes a directional light. - directional, - - /// Denotes a point light. - point, - - /// Denotes a spot light. - spot -}; - -/** - * Abstract base class for light objects. - */ -class light: public object -{ -public: - typedef geom::sphere sphere_type; - - /// Creates a light. - light(); - - /// Returns an enumeration denoting the light object type. - virtual light_type get_light_type() const = 0; - - /** - * Sets the color of the light. - * - * @param color Scene-linear light color. - */ - void set_color(const float3& color); - - /** - * Sets the intensity of the light. - * - * @param intensity Light intensity. - */ - void set_intensity(float intensity); - - /// Returns the local-space bounding volume of the light. - virtual const bounding_volume_type& get_local_bounds() const; - - /// Returns the world-space bounding volume of the light. - virtual const bounding_volume_type& get_world_bounds() const; - - /// Returns the light color. - const float3& get_color() const; - - /// Returns the light intensity. - float get_intensity() const; - - /// Returns the intensity-scaled light color. - const float3& get_scaled_color() const; - - const tween& get_color_tween() const; - const tween& get_intensity_tween() const; - const tween& get_scaled_color_tween() const; - - /// @copydoc object_base::update_tweens(); - virtual void update_tweens(); - -private: - virtual void transformed(); - - tween color; - tween intensity; - tween scaled_color; - sphere_type local_bounds; - sphere_type world_bounds; -}; - -inline const typename object_base::bounding_volume_type& light::get_local_bounds() const -{ - return local_bounds; -} - -inline const typename object_base::bounding_volume_type& light::get_world_bounds() const -{ - return world_bounds; -} - -inline const float3& light::get_color() const -{ - return color[1]; -} - -inline float light::get_intensity() const -{ - return intensity[1]; -} - -inline const float3& light::get_scaled_color() const -{ - return scaled_color[1]; -} - -inline const tween& light::get_color_tween() const -{ - return color; -} - -inline const tween& light::get_intensity_tween() const -{ - return intensity; -} - -inline const tween& light::get_scaled_color_tween() const -{ - return scaled_color; -} - -} // namespace scene - -#endif // ANTKEEPER_SCENE_LIGHT_HPP diff --git a/src/scene/lod-group.cpp b/src/scene/lod-group.cpp deleted file mode 100644 index b64c7fb..0000000 --- a/src/scene/lod-group.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "scene/lod-group.hpp" -#include "scene/camera.hpp" - -namespace scene { - -lod_group::lod_group(std::size_t level_count): - local_bounds{{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}}, - world_bounds{{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}} -{ - resize(level_count); -} - -lod_group::lod_group(): - lod_group(1) -{} - -void lod_group::resize(std::size_t level_count) -{ - levels.resize(level_count); -} - -std::size_t lod_group::select_lod(const camera& camera) const -{ - float distance = camera.get_view_frustum().get_near().signed_distance(get_translation()); - - if (distance < 300.0f) - return 0; - else if (distance < 500.0f) - return 1; - else if (distance < 600.0f) - return 2; - - return 3; -} - -void lod_group::add_object(std::size_t level, object_base* object) -{ - levels[level].push_back(object); -} - -void lod_group::remove_object(std::size_t level, object_base* object) -{ - levels[level].remove(object); -} - -void lod_group::remove_objects(std::size_t level) -{ - levels[level].clear(); -} - -void lod_group::update_bounds() -{ - world_bounds = {get_translation(), get_translation()}; -} - -void lod_group::transformed() -{ - update_bounds(); -} - -} // namespace scene diff --git a/src/scene/lod-group.hpp b/src/scene/lod-group.hpp deleted file mode 100644 index 7f29cfe..0000000 --- a/src/scene/lod-group.hpp +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_SCENE_LOD_GROUP_HPP -#define ANTKEEPER_SCENE_LOD_GROUP_HPP - -#include "scene/object.hpp" -#include "geom/aabb.hpp" -#include -#include - -namespace scene { - -class camera; - -class lod_group: public object -{ -public: - typedef geom::aabb aabb_type; - - /** - * Creates a LOD group. - * - * @param level_count Number of detail levels in the group. - */ - lod_group(std::size_t level_count); - - /// Creates a LOD group with one level of detail. - lod_group(); - - /** - * Resizes the LOD group to accommodate the specified number of detail levels. - * - * @param level_count Number of desired detail levels in the group. - */ - void resize(std::size_t level_count); - - /** - * Selects the appropriate level of detail for a camera. - * - * @param camera Camera for which the LOD should be selected. - * @return Selected level of detail. - */ - std::size_t select_lod(const camera& camera) const; - - /** - * Adds an object to the LOD group. - * - * @param level Level of detail of the object to add. - * @param object Object to add. - */ - void add_object(std::size_t level, object_base* object); - - /** - * Removes an object from the LOD group. - * - * @param level Level of detail of the object to remove. - * @param object Object to remove. - */ - void remove_object(std::size_t level, object_base* object); - - /** - * Removes all objects with the specified level of detail. - * - * @param level Level of detail of the objects to remove. - */ - void remove_objects(std::size_t level); - - virtual const bounding_volume_type& get_local_bounds() const; - virtual const bounding_volume_type& get_world_bounds() const; - - /// Returns the number of detail levels in the group. - std::size_t get_level_count() const; - - /** - * Returns a list containing all objects in the LOD group with the specified detail level. - * - * @param level Level of detail. - * @return List of all objects in the group with the specified detail level. - */ - const std::list& get_objects(std::size_t level) const; - -private: - void update_bounds(); - virtual void transformed(); - - aabb_type local_bounds; - aabb_type world_bounds; - std::vector> levels; -}; - -inline const typename object_base::bounding_volume_type& lod_group::get_local_bounds() const -{ - return local_bounds; -} - -inline const typename object_base::bounding_volume_type& lod_group::get_world_bounds() const -{ - return world_bounds; -} - -inline std::size_t lod_group::get_level_count() const -{ - return levels.size(); -} - -inline const std::list& lod_group::get_objects(std::size_t level) const -{ - return levels[level]; -} - -} // namespace scene - -#endif // ANTKEEPER_SCENE_LOD_GROUP_HPP - diff --git a/src/scene/model-instance.cpp b/src/scene/model-instance.cpp deleted file mode 100644 index 5ba9fc0..0000000 --- a/src/scene/model-instance.cpp +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "scene/model-instance.hpp" -#include "render/model.hpp" -#include "render/material.hpp" - -namespace scene { - -model_instance::model_instance(render::model* model): - model(nullptr), - local_bounds{{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}}, - world_bounds{{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}}, - instanced(false), - instance_count(0) -{ - set_model(model); - update_bounds(); -} - -model_instance::model_instance(): - model_instance(nullptr) -{} - -model_instance::model_instance(const model_instance& other) -{ - *this = other; -} - -model_instance& model_instance::operator=(const model_instance& other) -{ - local_bounds = other.local_bounds; - world_bounds = other.world_bounds; - model = other.model; - pose = other.pose; - materials = other.materials; - instanced = other.instanced; - instance_count = other.instance_count; - return *this; -} - -void model_instance::set_model(render::model* model) -{ - this->model = model; - - if (model) - { - materials.resize(model->get_groups()->size()); - reset_materials(); - - pose = model->get_skeleton().bind_pose; - ::concatenate(pose, pose); - } - else - { - pose.clear(); - } - - update_bounds(); -} - -void model_instance::set_material(std::size_t group_index, render::material* material) -{ - materials[group_index] = material; -} - -void model_instance::set_instanced(bool instanced, std::size_t instance_count) -{ - this->instanced = instanced; - this->instance_count = (instanced) ? instance_count : 0; -} - -void model_instance::reset_materials() -{ - std::fill(materials.begin(), materials.end(), nullptr); -} - -void model_instance::update_bounds() -{ - if (model) - { - local_bounds = aabb_type::transform(model->get_bounds(), get_transform()); - transformed(); - } - else - { - local_bounds = {{0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}}; - world_bounds = {get_translation(), get_translation()}; - } -} - -void model_instance::transformed() -{ - world_bounds = aabb_type::transform(local_bounds, get_transform()); -} - -void model_instance::update_tweens() -{ - object_base::update_tweens(); - - // Update model material tweens - if (model) - { - for (render::model_group* group: *model->get_groups()) - { - render::material* material = group->get_material(); - if (material) - { - material->update_tweens(); - } - } - } - - // Update material override tweens - for (render::material* material: materials) - { - if (material) - { - material->update_tweens(); - } - } -} - -} // namespace scene diff --git a/src/scene/model-instance.hpp b/src/scene/model-instance.hpp deleted file mode 100644 index 30a2c0d..0000000 --- a/src/scene/model-instance.hpp +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_SCENE_MODEL_INSTANCE_HPP -#define ANTKEEPER_SCENE_MODEL_INSTANCE_HPP - -#include "scene/object.hpp" -#include "animation/pose.hpp" -#include "geom/aabb.hpp" -#include "render/model.hpp" -#include - -namespace scene { - -class model_instance: public object -{ -public: - typedef geom::aabb aabb_type; - - explicit model_instance(render::model* model); - model_instance(); - model_instance(const model_instance& other); - model_instance& operator=(const model_instance& other); - - /** - * Sets the model with which this model instance is associated. This will reset the pose and all overwritten materials. - */ - void set_model(render::model* model); - - /** - * Overwrites the material of a model group for this model instance. - * - * @param group_index Index of a model group. - * @param material Pointer to the material which should overwrite the model group's material. A value of `nullptr` indicates the material will not be overwritten. - */ - void set_material(std::size_t group_index, render::material* material); - - void set_instanced(bool instanced, std::size_t instance_count = 1); - - /** - * Resets all overwritten materials. - */ - void reset_materials(); - - virtual const bounding_volume_type& get_local_bounds() const; - virtual const bounding_volume_type& get_world_bounds() const; - - const render::model* get_model() const; - render::model* get_model(); - - /// Returns the skeletal animation pose of this model. - const pose& get_pose() const; - - /// @copydoc model_instance::get_pose() const - pose& get_pose(); - - const std::vector* get_materials() const; - - bool is_instanced() const; - std::size_t get_instance_count() const; - - virtual void update_tweens(); - - void update_bounds(); - -private: - virtual void transformed(); - - render::model* model; - ::pose pose; - std::vector materials; - aabb_type local_bounds; - aabb_type world_bounds; - bool instanced; - std::size_t instance_count; -}; - -inline const typename object_base::bounding_volume_type& model_instance::get_local_bounds() const -{ - return local_bounds; -} - -inline const typename object_base::bounding_volume_type& model_instance::get_world_bounds() const -{ - return world_bounds; -} - -inline const render::model* model_instance::get_model() const -{ - return model; -} - -inline render::model* model_instance::get_model() -{ - return model; -} - -inline const pose& model_instance::get_pose() const -{ - return pose; -} - -inline pose&model_instance::get_pose() -{ - return pose; -} - -inline const std::vector* model_instance::get_materials() const -{ - return &materials; -} - -inline bool model_instance::is_instanced() const -{ - return instanced; -} - -inline std::size_t model_instance::get_instance_count() const -{ - return instance_count; -} - -} // namespace scene - -#endif // ANTKEEPER_SCENE_MODEL_INSTANCE_HPP - diff --git a/src/scene/object.cpp b/src/scene/object.cpp deleted file mode 100644 index 79dc25d..0000000 --- a/src/scene/object.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "scene/object.hpp" -#include "math/interpolation.hpp" - -namespace scene { - -typename object_base::transform_type object_base::interpolate_transforms(const transform_type& x, const transform_type& y, float a) -{ - return - { - math::lerp(x.translation, y.translation, a), - math::nlerp(x.rotation, y.rotation, a), - math::lerp(x.scale, y.scale, a), - }; -} - -object_base::object_base(): - active(true), - transform(math::transform::identity, interpolate_transforms), - culling_mask(nullptr) -{} - -void object_base::set_culling_mask(const bounding_volume_type* culling_mask) -{ - this->culling_mask = culling_mask; -} - -std::size_t object_base::next_object_type_id() -{ - static std::atomic id{0}; - return id++; -} - -void object_base::render(const render::context& ctx, render::queue& queue) const -{} - -void object_base::update_tweens() -{ - transform.update(); -} - -void object_base::look_at(const vector_type& position, const vector_type& target, const vector_type& up) -{ - transform[1].translation = position; - transform[1].rotation = math::look_rotation(math::normalize(math::sub(target, position)), up); - transformed(); -} - -void object_base::transformed() -{} - -} // namespace scene diff --git a/src/scene/object.hpp b/src/scene/object.hpp deleted file mode 100644 index 84dac1c..0000000 --- a/src/scene/object.hpp +++ /dev/null @@ -1,268 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_SCENE_OBJECT_HPP -#define ANTKEEPER_SCENE_OBJECT_HPP - -#include "animation/tween.hpp" -#include "geom/bounding-volume.hpp" -#include "math/vector.hpp" -#include "math/quaternion.hpp" -#include "math/transform-type.hpp" -#include "render/context.hpp" -#include "render/queue.hpp" -#include -#include - -namespace scene { - -/** - * Internal base class for scene objects. - */ -class object_base -{ -public: - typedef math::vector vector_type; - typedef math::quaternion quaternion_type; - typedef math::transform transform_type; - typedef geom::bounding_volume bounding_volume_type; - - /// Returns the type ID for this scene object type. - virtual const std::size_t get_object_type_id() const = 0; - - /** - * Creates a scene object base. - */ - object_base(); - - /** - * Destroys a scene object base. - */ - virtual ~object_base() = default; - - /** - * Adds a render operation describing this object to a render queue. - * - * @param ctx Render context. - * @param queue Render queue. - * - * @see render::context - * @see render::operation - */ - virtual void render(const render::context& ctx, render::queue& queue) const; - - /** - * Updates all tweens in the scene object. - */ - virtual void update_tweens(); - - /** - * Activates or deactivates the scene object. - */ - void set_active(bool active); - - /** - * - */ - void look_at(const vector_type& position, const vector_type& target, const vector_type& up); - - /** - * Sets the scene object's transform. - */ - void set_transform(const transform_type& transform); - - /** - * Sets the scene object's translation. - */ - void set_translation(const vector_type& translation); - - /** - * Sets the scene object's rotation. - */ - void set_rotation(const quaternion_type& rotation); - - /** - * Sets the scene object's scale. - */ - void set_scale(const vector_type& scale); - - /** - * Sets a culling mask for the object, which will be used for view-frustum culling instead of the object's bounds. - */ - void set_culling_mask(const bounding_volume_type* culling_mask); - - /// Returns whether the scene object is active. - bool is_active() const; - - /** - * Returns the transform. - */ - const transform_type& get_transform() const; - - /** - * Returns the transform's translation vector. - */ - const vector_type& get_translation() const; - - /** - * Returns the transform's rotation quaternion. - */ - const quaternion_type& get_rotation() const; - - /** - * Returns the transform's scale vector. - */ - const vector_type& get_scale() const; - - /** - * Returns the transform tween. - */ - const tween& get_transform_tween() const; - tween& get_transform_tween(); - - /** - * Returns the local-space (untransformed) bounds of the object. - */ - virtual const bounding_volume_type& get_local_bounds() const = 0; - - /** - * Returns the world-space (transformed) bounds of the object. - */ - virtual const bounding_volume_type& get_world_bounds() const = 0; - - /** - * Returns the culling mask of the object. - */ - const bounding_volume_type* get_culling_mask() const; - -protected: - static std::size_t next_object_type_id(); - -private: - /// Interpolates between two transforms. - static transform_type interpolate_transforms(const transform_type& x, const transform_type& y, float a); - - /** - * Called every time the scene object's tranform is changed. - */ - virtual void transformed(); - - bool active; - tween transform; - const bounding_volume_type* culling_mask; -}; - -inline void object_base::set_active(bool active) -{ - this->active = active; -} - -inline void object_base::set_transform(const transform_type& transform) -{ - this->transform[1] = transform; - transformed(); -} - -inline void object_base::set_translation(const vector_type& translation) -{ - transform[1].translation = translation; - transformed(); -} - -inline void object_base::set_rotation(const quaternion_type& rotation) -{ - transform[1].rotation = rotation; - transformed(); -} - -inline void object_base::set_scale(const vector_type& scale) -{ - transform[1].scale = scale; - transformed(); -} - -inline bool object_base::is_active() const -{ - return active; -} - -inline const typename object_base::transform_type& object_base::get_transform() const -{ - return transform[1]; -} - -inline const typename object_base::vector_type& object_base::get_translation() const -{ - return get_transform().translation; -} - -inline const typename object_base::quaternion_type& object_base::get_rotation() const -{ - return get_transform().rotation; -} - -inline const typename object_base::vector_type& object_base::get_scale() const -{ - return get_transform().scale; -} - -inline const tween& object_base::get_transform_tween() const -{ - return transform; -} - -inline tween& object_base::get_transform_tween() -{ - return transform; -} - -inline const typename object_base::bounding_volume_type* object_base::get_culling_mask() const -{ - return culling_mask; -} - -/** - * Abstract base class for lights, cameras, model instances, and other scene objects. - * - * @tparam T This should be the same class that's inheriting from the scene object, in order to give it a valid type-specific ID. - */ -template -class object: public object_base -{ -public: - /// Unique type ID for this scene object type. - static const std::atomic object_type_id; - - /// @copydoc object_base::get_object_type_id() const - virtual const std::size_t get_object_type_id() const final; -}; - -template -const std::atomic object::object_type_id{object_base::next_object_type_id()}; - -template -inline const std::size_t object::get_object_type_id() const -{ - return object_type_id; -} - -} // namespace scene - -#endif // ANTKEEPER_SCENE_OBJECT_HPP - diff --git a/src/scene/point-light.cpp b/src/scene/point-light.cpp deleted file mode 100644 index 3d7744d..0000000 --- a/src/scene/point-light.cpp +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "point-light.hpp" -#include "math/interpolation.hpp" - -namespace scene { - -point_light::point_light(): - attenuation(float3{1, 0, 0}, math::lerp) -{} - -void point_light::set_attenuation(const float3& attenuation) -{ - this->attenuation[1] = attenuation; -} - -void point_light::update_tweens() -{ - light::update_tweens(); - attenuation.update(); -} - -} // namespace scene diff --git a/src/scene/point-light.hpp b/src/scene/point-light.hpp deleted file mode 100644 index b9edc8b..0000000 --- a/src/scene/point-light.hpp +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_SCENE_POINT_LIGHT_HPP -#define ANTKEEPER_SCENE_POINT_LIGHT_HPP - -#include "scene/light.hpp" -#include "utility/fundamental-types.hpp" - -namespace scene { - -/** - * Light source that radiates outward from a point. - */ -class point_light: public light -{ -public: - point_light(); - - /// Returns light_type::point - virtual light_type get_light_type() const; - - /** - * Sets the attenuation factors of the light. - * - * @param attenuation Vector containing the constant, linear, and quadratic attenuation factors, as x, y, and z, respectively. - */ - void set_attenuation(const float3& attenuation); - - /// Returns the attenuation factors of the light. - const float3& get_attenuation() const; - - /// Returns the attenuation tween. - const tween& get_attenuation_tween() const; - - /// @copydoc object_base::update_tweens(); - virtual void update_tweens(); - -private: - tween attenuation; -}; - -inline light_type point_light::get_light_type() const -{ - return light_type::point; -} - -inline const float3& point_light::get_attenuation() const -{ - return attenuation[1]; -} - -inline const tween& point_light::get_attenuation_tween() const -{ - return attenuation; -} - -} // namespace scene - -#endif // ANTKEEPER_SCENE_POINT_LIGHT_HPP - diff --git a/src/scene/scene.hpp b/src/scene/scene.hpp deleted file mode 100644 index be5114d..0000000 --- a/src/scene/scene.hpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_SCENE_HPP -#define ANTKEEPER_SCENE_HPP - -/// 3D scene -namespace scene {} - -#include "ambient-light.hpp" -#include "billboard.hpp" -#include "camera.hpp" -#include "collection.hpp" -#include "directional-light.hpp" -#include "light.hpp" -#include "lod-group.hpp" -#include "model-instance.hpp" -#include "object.hpp" -#include "point-light.hpp" -#include "spot-light.hpp" -#include "text.hpp" - -#endif // ANTKEEPER_SCENE_HPP diff --git a/src/scene/spot-light.cpp b/src/scene/spot-light.cpp deleted file mode 100644 index 3a51d84..0000000 --- a/src/scene/spot-light.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "spot-light.hpp" -#include "config.hpp" -#include "math/quaternion.hpp" -#include "math/interpolation.hpp" -#include - -namespace scene { - -static float3 interpolate_direction(const float3& x, const float3& y, float a) -{ - math::quaternion q0 = math::rotation(config::global_forward, x); - math::quaternion q1 = math::rotation(config::global_forward, y); - return math::normalize(math::slerp(q0, q1, a) * config::global_forward); -} - -spot_light::spot_light(): - direction(config::global_forward, interpolate_direction), - attenuation(float3{1, 0, 0}, math::lerp), - cutoff(float2{math::pi, math::pi}, math::lerp), - cosine_cutoff(float2{std::cos(math::pi), std::cos(math::pi)}, math::lerp) -{} - -void spot_light::set_attenuation(const float3& attenuation) -{ - this->attenuation[1] = attenuation; -} - -void spot_light::set_cutoff(const float2& cutoff) -{ - this->cutoff[1] = cutoff; - this->cosine_cutoff[1] = {std::cos(cutoff.x()), std::cos(cutoff.y())}; -} - -void spot_light::update_tweens() -{ - light::update_tweens(); - direction.update(); - attenuation.update(); - cutoff.update(); - cosine_cutoff.update(); -} - -void spot_light::transformed() -{ - direction[1] = math::normalize(get_transform().rotation * config::global_forward); -} - -} // namespace scene diff --git a/src/scene/spot-light.hpp b/src/scene/spot-light.hpp deleted file mode 100644 index bed3dd1..0000000 --- a/src/scene/spot-light.hpp +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_SCENE_SPOT_LIGHT_HPP -#define ANTKEEPER_SCENE_SPOT_LIGHT_HPP - -#include "scene/light.hpp" -#include "utility/fundamental-types.hpp" - -namespace scene { - -/** - * Directional cone light source. - */ -class spot_light: public light -{ -public: - /// Creates a spot light. - spot_light(); - - /// Returns light_type::spot - virtual light_type get_light_type() const; - - /** - * Sets the attenuation factors of the light. - * - * @param attenuation Vector containing the constant, linear, and quadratic attenuation factors, as x, y, and z, respectively. - */ - void set_attenuation(const float3& attenuation); - - /** - * Sets the spot light cutoff angles. - * - * @param cutoff Vector containing the inner and outer cutoff angles, as x and y, respectively. - */ - void set_cutoff(const float2& cutoff); - - /// Returns the direction vector. - const float3& get_direction() const; - - /// Returns the attenuation factors of the light. - const float3& get_attenuation() const; - - /// Returns the spot light cutoff angles. - const float2& get_cutoff() const; - - /// Returns the cosine of the spot light cutoff angles. - const float2& get_cosine_cutoff() const; - - /// Returns the direction tween. - const tween& get_direction_tween() const; - - /// Returns the attenuation tween. - const tween& get_attenuation_tween() const; - - /// Returns the cutoff tween. - const tween& get_cutoff_tween() const; - - /// Returns the cosine cutoff tween. - const tween& get_cosine_cutoff_tween() const; - - /// @copydoc object_base::update_tweens(); - virtual void update_tweens(); - -private: - virtual void transformed(); - - tween direction; - tween attenuation; - tween cutoff; - tween cosine_cutoff; -}; - -inline light_type spot_light::get_light_type() const -{ - return light_type::spot; -} - -inline const float3& spot_light::get_direction() const -{ - return direction[1]; -} - -inline const float3& spot_light::get_attenuation() const -{ - return attenuation[1]; -} - -inline const float2& spot_light::get_cutoff() const -{ - return cutoff[1]; -} - -inline const float2& spot_light::get_cosine_cutoff() const -{ - return cosine_cutoff[1]; -} - -inline const tween& spot_light::get_direction_tween() const -{ - return direction; -} - -inline const tween& spot_light::get_attenuation_tween() const -{ - return attenuation; -} - -inline const tween& spot_light::get_cutoff_tween() const -{ - return cutoff; -} - -inline const tween& spot_light::get_cosine_cutoff_tween() const -{ - return cosine_cutoff; -} - -} // namespace scene - -#endif // ANTKEEPER_SCENE_SPOT_LIGHT_HPP - diff --git a/src/scene/text.cpp b/src/scene/text.cpp deleted file mode 100644 index 1250116..0000000 --- a/src/scene/text.cpp +++ /dev/null @@ -1,341 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "scene/text.hpp" -#include "render/vertex-attribute.hpp" -#include "type/unicode/convert.hpp" -#include - -namespace scene { - -text::text(): - local_bounds{{0, 0, 0}, {0, 0, 0}}, - world_bounds{{0, 0, 0}, {0, 0, 0}}, - material(nullptr), - font(nullptr), - direction(type::text_direction::ltr), - content_u8(std::string()), - content_u32(std::u32string()), - color({0.0f, 0.0f, 0.0f, 1.0f}), - vertex_stride(0), - vertex_count(0), - vao(nullptr), - vbo(nullptr) -{ - // Allocate VBO and VAO - vbo = new gl::vertex_buffer(0, nullptr, gl::buffer_usage::static_draw); - vao = new gl::vertex_array(); - - // Calculate vertex stride - vertex_stride = (3 + 2 + 4) * sizeof(float); - - // Init vertex attribute offset - std::size_t attribute_offset = 0; - - // Define vertex position attribute - gl::vertex_attribute position_attribute; - position_attribute.buffer = vbo; - position_attribute.offset = attribute_offset; - position_attribute.stride = vertex_stride; - position_attribute.type = gl::vertex_attribute_type::float_32; - position_attribute.components = 3; - attribute_offset += position_attribute.components * sizeof(float); - - // Define vertex UV attribute - gl::vertex_attribute uv_attribute; - uv_attribute.buffer = vbo; - uv_attribute.offset = attribute_offset; - uv_attribute.stride = vertex_stride; - uv_attribute.type = gl::vertex_attribute_type::float_32; - uv_attribute.components = 2; - attribute_offset += uv_attribute.components * sizeof(float); - - // Define vertex color attribute - gl::vertex_attribute color_attribute; - color_attribute.buffer = vbo; - color_attribute.offset = attribute_offset; - color_attribute.stride = vertex_stride; - color_attribute.type = gl::vertex_attribute_type::float_32; - color_attribute.components = 4; - attribute_offset += color_attribute.components * sizeof(float); - - // Bind vertex attributes to VAO - vao->bind(render::vertex_attribute::position, position_attribute); - vao->bind(render::vertex_attribute::uv, uv_attribute); - vao->bind(render::vertex_attribute::color, color_attribute); - - // Init render operation - render_op.material = nullptr; - render_op.bone_count = 0; - render_op.skinning_palette = nullptr; - render_op.vertex_array = vao; - render_op.drawing_mode = gl::drawing_mode::triangles; - render_op.start_index = 0; - render_op.index_count = 0; - render_op.instance_count = 0; -} - -text::~text() -{ - // Free VAO and VBO - delete vao; - delete vbo; -} - -void text::render(const render::context& ctx, render::queue& queue) const -{ - if (!vertex_count) - return; - - render_op.transform = math::matrix_cast(get_transform_tween().interpolate(ctx.alpha)); - render_op.depth = ctx.clip_near.signed_distance(math::vector(render_op.transform[3])); - queue.push_back(render_op); -} - -void text::refresh() -{ - update_content(); -} - -void text::set_material(render::material* material) -{ - this->material = material; - render_op.material = material; -} - -void text::set_font(const type::bitmap_font* font) -{ - if (this->font != font) - { - this->font = font; - - // Update text in VBO - update_content(); - } -} - -void text::set_direction(type::text_direction direction) -{ - if (this->direction != direction) - { - this->direction = direction; - - // Update text in VBO - update_content(); - } -} - -void text::set_content(const std::string& content) -{ - // If content has changed - if (content_u8 != content) - { - // Update UTF-8 content - content_u8 = content; - - // Convert UTF-8 content to UTF-32 - content_u32 = type::unicode::u32(content_u8); - - // Update text in VBO - update_content(); - } -} - -void text::set_color(const float4& color) -{ - this->color = color; - - // Update color in VBO - update_color(); -} - -void text::transformed() -{ - world_bounds = aabb_type::transform(local_bounds, get_transform()); -} - -void text::update_tweens() -{ - object_base::update_tweens(); - if (material) - { - material->update_tweens(); - } -} - -void text::update_content() -{ - // If no valid font or no text, clear vertex count - if (!font || content_u32.empty()) - { - vertex_count = 0; - render_op.index_count = vertex_count; - local_bounds = {{0, 0, 0}, {0, 0, 0}}; - transformed(); - return; - } - - // Calculate new vertex count and minimum vertex buffer size - std::size_t vertex_count = content_u32.length() * 6; - std::size_t min_vertex_buffer_size = vertex_count * vertex_stride; - - // Expand vertex data buffer to accommodate vertices - if (vertex_data.size() < min_vertex_buffer_size) - vertex_data.resize(min_vertex_buffer_size); - - // Get font metrics and bitmap - const type::font_metrics& font_metrics = font->get_font_metrics(); - const image& font_bitmap = font->get_bitmap(); - - // Init pen position - float2 pen_position = {0.0f, 0.0f}; - - // Reset local-space bounds - local_bounds = {{0, 0, 0}, {0, 0, 0}}; - - // Generate vertex data - char32_t previous_code = 0; - float* v = reinterpret_cast(vertex_data.data()); - for (char32_t code: content_u32) - { - // Apply kerning - if (previous_code) - { - pen_position.x() += font->get_kerning(previous_code, code).x(); - } - - if (font->contains(code)) - { - // Get glyph - const type::bitmap_glyph& glyph = font->get_glyph(code); - - // Calculate vertex positions - float2 positions[6]; - positions[0] = pen_position + glyph.metrics.horizontal_bearing; - positions[1] = {positions[0].x(), positions[0].y() - glyph.metrics.height}; - positions[2] = {positions[0].x() + glyph.metrics.width, positions[1].y()}; - positions[3] = {positions[2].x(), positions[0].y()}; - positions[4] = positions[0]; - positions[5] = positions[2]; - - // Calculate vertex UVs - float2 uvs[6]; - uvs[0] = {static_cast(glyph.position.x()), static_cast(glyph.position.y())}; - uvs[1] = {uvs[0].x(), uvs[0].y() + glyph.metrics.height}; - uvs[2] = {uvs[0].x() + glyph.metrics.width, uvs[1].y()}; - uvs[3] = {uvs[2].x(), uvs[0].y()}; - uvs[4] = uvs[0]; - uvs[5] = uvs[2]; - - for (int i = 0; i < 6; ++i) - { - // Round positions - positions[i].x() = std::round(positions[i].x()); - positions[i].y() = std::round(positions[i].y()); - - // Normalize UVs - uvs[i].x() = uvs[i].x() / static_cast(font_bitmap.get_width()); - uvs[i].y() = uvs[i].y() / static_cast(font_bitmap.get_height()); - } - - // Add vertex to vertex data buffer - for (int i = 0; i < 6; ++i) - { - *(v++) = positions[i].x(); - *(v++) = positions[i].y(); - *(v++) = 0.0f; - *(v++) = uvs[i].x(); - *(v++) = uvs[i].y(); - *(v++) = color[0]; - *(v++) = color[1]; - *(v++) = color[2]; - *(v++) = color[3]; - } - - // Advance pen position - pen_position.x() += glyph.metrics.horizontal_advance; - - // Update local-space bounds - for (int i = 0; i < 4; ++i) - { - const float2& position = positions[i]; - for (int j = 0; j < 2; ++j) - { - local_bounds.min_point[j] = std::min(local_bounds.min_point[j], position[j]); - local_bounds.max_point[j] = std::max(local_bounds.max_point[j], position[j]); - } - } - } - else - { - // Glyph not in font, zero vertex data - for (std::size_t i = 0; i < (6 * 9); ++i) - *(v++) = 0.0f; - } - - // Handle newlines - if (code == static_cast('\n')) - { - pen_position.x() = 0.0f; - pen_position.y() -= font_metrics.linegap; - } - - // Update previous UTF-32 character code - previous_code = code; - } - - // Resize VBO, if necessary, and upload vertex data - if (vertex_count > this->vertex_count) - { - this->vertex_count = vertex_count; - vbo->resize(min_vertex_buffer_size, vertex_data.data()); - } - else - { - vbo->write(0, min_vertex_buffer_size, vertex_data.data()); - } - - // Update vertex count - this->vertex_count = vertex_count; - render_op.index_count = vertex_count; - - // Update world-space bounds - transformed(); -} - -void text::update_color() -{ - float* v = reinterpret_cast(vertex_data.data()); - for (std::size_t i = 0; i < vertex_count; ++i) - { - // Skip vertex position (vec3) and vertex UV (vec2) - v += (3 + 2); - - // Update vertex color - *(v++) = color[0]; - *(v++) = color[1]; - *(v++) = color[2]; - *(v++) = color[3]; - } - - // Update VBO - vbo->write(0, vertex_count * vertex_stride, vertex_data.data()); -} - -} // namespace scene diff --git a/src/scene/text.hpp b/src/scene/text.hpp deleted file mode 100644 index 4f0594b..0000000 --- a/src/scene/text.hpp +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_SCENE_TEXT_HPP -#define ANTKEEPER_SCENE_TEXT_HPP - -#include "scene/object.hpp" -#include "geom/aabb.hpp" -#include "utility/fundamental-types.hpp" -#include "gl/vertex-array.hpp" -#include "gl/vertex-buffer.hpp" -#include "render/material.hpp" -#include "type/bitmap-font.hpp" -#include "type/text-direction.hpp" - -namespace scene { - -/** - * Text scene object. - */ -class text: public object -{ -public: - typedef geom::aabb aabb_type; - - /// Constructs a text object. - text(); - - /// Destructs a text object. - ~text(); - - /// @copydoc scene::object_base::render(const render::context&, render::queue&) const - virtual void render(const render::context& ctx, render::queue& queue) const; - - /** - * Manually updates the text object if its font has been updated or altered in any way. - */ - void refresh(); - - /** - * Sets the text material. - * - * @param material Text material. - */ - void set_material(render::material* material); - - /** - * Sets the text font. - * - * @param font Pointer to a font. - */ - void set_font(const type::bitmap_font* font); - - /** - * Sets the direction of the text. - * - * @param direction Text direction. - */ - void set_direction(type::text_direction direction); - - /** - * Sets the text content. - * - * @param content UTF-8 string of text. - */ - void set_content(const std::string& content); - - /** - * Sets the text color. - * - * Text color is passed to the text's material shader as a vertex color. - * - * @param color RGBA color. - */ - void set_color(const float4& color); - - /// Returns the text material. - render::material* get_material() const; - - /// Returns the text font. - const type::bitmap_font* get_font() const; - - /// Returns the text direction. - const type::text_direction& get_direction() const; - - /// Returns the text content. - const std::string& get_content() const; - - /// Returns the text color. - const float4& get_color() const; - - /// @copydoc scene::object::get_local_bounds() const - virtual const bounding_volume_type& get_local_bounds() const; - - /// @copydoc scene::object::get_world_bounds() const - virtual const bounding_volume_type& get_world_bounds() const; - - /// @copydoc scene::object::update_tweens() - virtual void update_tweens(); - -private: - void update_content(); - void update_color(); - - virtual void transformed(); - - mutable render::operation render_op; - aabb_type local_bounds; - aabb_type world_bounds; - render::material* material; - const type::bitmap_font* font; - type::text_direction direction; - std::string content_u8; - std::u32string content_u32; - float4 color; - std::size_t vertex_stride; - std::size_t vertex_count; - std::vector vertex_data; - gl::vertex_array* vao; - gl::vertex_buffer* vbo; -}; - -inline render::material* text::get_material() const -{ - return material; -} - -inline const type::bitmap_font* text::get_font() const -{ - return font; -} - -inline const type::text_direction& text::get_direction() const -{ - return direction; -} - -inline const std::string& text::get_content() const -{ - return content_u8; -} - -inline const float4& text::get_color() const -{ - return color; -} - -inline const typename object_base::bounding_volume_type& text::get_local_bounds() const -{ - return local_bounds; -} - -inline const typename object_base::bounding_volume_type& text::get_world_bounds() const -{ - return world_bounds; -} - -} // namespace scene - -#endif // ANTKEEPER_SCENE_TEXT_HPP diff --git a/src/type/bitmap-font.cpp b/src/type/bitmap-font.cpp deleted file mode 100644 index 4e3a31b..0000000 --- a/src/type/bitmap-font.cpp +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "type/bitmap-font.hpp" -#include "geom/rect-pack.hpp" -#include - -namespace type { - -bitmap_font::bitmap_font(const font_metrics& metrics): - font(metrics) -{} - -bitmap_font::bitmap_font() -{} - -bool bitmap_font::contains(char32_t code) const -{ - return glyphs.count(code) != 0; -} - -void bitmap_font::insert(char32_t code, const bitmap_glyph& glyph) -{ - glyphs[code] = glyph; -} - -void bitmap_font::remove(char32_t code) -{ - if (auto it = glyphs.find(code); it != glyphs.end()) - glyphs.erase(it); -} - -void bitmap_font::clear() -{ - glyphs.clear(); -} - -bool bitmap_font::pack(bool resize) -{ - // Returns the smallest power of two that is not smaller than @p x. - auto ceil2 = [](unsigned int x) -> unsigned int - { - if (x <= 1) - return 1; - unsigned int y = 2; - --x; - while (x >>= 1) - y <<= 1; - return y; - }; - - // Calculate initial size of the font bitmap - unsigned int bitmap_w; - unsigned int bitmap_h; - if (resize) - { - // Find the maximum glyph dimensions - unsigned int max_glyph_w = 0; - unsigned int max_glyph_h = 0; - for (auto it = glyphs.begin(); it != glyphs.end(); ++it) - { - max_glyph_w = std::max(max_glyph_w, it->second.bitmap.get_width()); - max_glyph_h = std::max(max_glyph_h, it->second.bitmap.get_height()); - } - - // Find minimum power of two dimensions that can accommodate maximum glyph dimensions - bitmap_w = ceil2(max_glyph_w); - bitmap_h = ceil2(max_glyph_h); - } - else - { - bitmap_w = bitmap.get_width(); - bitmap_h = bitmap.get_height(); - } - - bool packed = false; - geom::rect_pack glyph_pack(bitmap_w, bitmap_h); - std::unordered_map::node_type*> glyph_map; - - while (!packed) - { - // For each glyph - for (auto it = glyphs.begin(); it != glyphs.end(); ++it) - { - // Attempt to pack glyph bitmap - const auto* node = glyph_pack.pack(it->second.bitmap.get_width(), it->second.bitmap.get_height()); - - // Abort if packing failed - if (!node) - break; - - // Map pack node to glyph character code - glyph_map[it->first] = node; - } - - // Check if not all glyphs were packed - if (glyph_map.size() != glyphs.size()) - { - if (!resize) - { - // No resize, packing failed - packed = false; - break; - } - - // Clear glyph map - glyph_map.clear(); - - // Clear glyph pack - glyph_pack.clear(); - - // Resize glyph pack - if (bitmap_w > bitmap_h) - bitmap_h = ceil2(++bitmap_h); - else - bitmap_w = ceil2(++bitmap_w); - glyph_pack.resize(bitmap_w, bitmap_h); - } - else - { - packed = true; - } - } - - // Copy glyph bitmaps into font bitmap - if (packed) - { - // Resize font bitmap - bitmap.resize(bitmap_w, bitmap_h); - - // For each glyph - for (auto it = glyphs.begin(); it != glyphs.end(); ++it) - { - // Find rect pack node corresponding to the glyph - const auto* node = glyph_map[it->first]; - - // Copy glyph bitmap data into font bitmap - image& glyph_bitmap = it->second.bitmap; - bitmap.copy(glyph_bitmap, glyph_bitmap.get_width(), glyph_bitmap.get_height(), 0, 0, node->bounds.min.x(), node->bounds.min.y()); - - // Record coordinates of glyph bitmap within font bitmap - it->second.position = {node->bounds.min.x(), node->bounds.min.y()}; - - // Clear glyph bitmap data - glyph_bitmap.resize(0, 0); - - } - } - - return packed; -} - -void bitmap_font::unpack(bool resize) -{ - for (auto it = glyphs.begin(); it != glyphs.end(); ++it) - { - bitmap_glyph& glyph = it->second; - - // Get glyph dimensions - unsigned int glyph_width = static_cast(glyph.metrics.width + 0.5f); - unsigned int glyph_height = static_cast(glyph.metrics.height + 0.5f); - - // Reformat glyph bitmap if necessary - if (!glyph.bitmap.compatible(bitmap)) - glyph.bitmap.format(bitmap.get_component_size(), bitmap.get_channel_count()); - - // Resize glyph bitmap if necessary - if (glyph.bitmap.get_width() != glyph_width || glyph.bitmap.get_height() != glyph_height) - glyph.bitmap.resize(glyph_width, glyph_height); - - // Copy pixel data from font bitmap to glyph bitmap - glyph.bitmap.copy(bitmap, glyph_width, glyph_height, glyph.position.x(), glyph.position.y()); - } - - // Free font bitmap pixel data - if (resize) - { - bitmap.resize(0, 0); - } -} - -const glyph_metrics& bitmap_font::get_glyph_metrics(char32_t code) const -{ - if (auto it = glyphs.find(code); it != glyphs.end()) - return it->second.metrics; - throw std::invalid_argument("Cannot fetch metrics of unknown bitmap glyph"); -} - -const bitmap_glyph& bitmap_font::get_glyph(char32_t code) const -{ - if (auto it = glyphs.find(code); it != glyphs.end()) - return it->second; - throw std::invalid_argument("Cannot get unknown bitmap glyph"); -} - -bitmap_glyph& bitmap_font::get_glyph(char32_t code) -{ - if (auto it = glyphs.find(code); it != glyphs.end()) - return it->second; - throw std::invalid_argument("Cannot get unknown bitmap glyph"); -} - -} // namespace type diff --git a/src/type/bitmap-font.hpp b/src/type/bitmap-font.hpp deleted file mode 100644 index 73086e4..0000000 --- a/src/type/bitmap-font.hpp +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_TYPE_BITMAP_FONT_HPP -#define ANTKEEPER_TYPE_BITMAP_FONT_HPP - -#include "type/font.hpp" -#include "type/bitmap-glyph.hpp" -#include "resources/image.hpp" -#include - -namespace type { - -/** - * Raster font in which glyphs are stored as arrays of pixels. - * - * @see type::font - * @see type::font_metrics - * @see type::bitmap_glyph - * @see image - */ -class bitmap_font: public font -{ -public: - /** - * Creates a bitmap font and sets its metrics. - * - * @param metrics Metrics describing the font. - */ - bitmap_font(const font_metrics& metrics); - - /// Creates an empty bitmap font. - bitmap_font(); - - /// Destroys a bitmap font. - virtual ~bitmap_font() = default; - - /// @copydoc font::contains(char32_t) const - virtual bool contains(char32_t code) const; - - /** - * Inserts a glyph into the font. - * - * @param code UTF-32 character code of the glyph to insert. - * @param glyph Bitmap glyph data. - */ - void insert(char32_t code, const bitmap_glyph& glyph); - - /** - * Removes a glyph from the font. - * - * @param code UTF-32 character code of the glyph to remove. - */ - void remove(char32_t code); - - /** - * Removes all glyphs from the font. - */ - void clear(); - - /** - * Packs all glyph bitmaps into the font bitmap. - * - * @param resize Automatically resize the font bitmap to contain all glyphs. Bitmap size will start at the closest power of two to the largest glyph, then its dimensions will increase to the next power of two until its large enough that all glyphs can be contained. - * @return `true` if all glyphs were successfully packed, `false` otherwise. - * - * @except std::runtime_error Glyph bitmap format doesn't match font bitmap format. - * @except std::runtime_error Not enough space in font bitmap to pack glyph. - */ - bool pack(bool resize = true); - - /** - * Unpacks all glyph bitmaps from the font bitmap. - * - * @param resize Automatically resizes the font bitmap to zero. - */ - void unpack(bool resize = true); - - /// Returns a reference to the bitmap containing glyph pixel data. - const image& get_bitmap() const; - - /// @copydoc bitmap_font::get_bitmap() const - image& get_bitmap(); - - /** - * @copydoc font::get_glyph_metrics(char32_t) const - * - * @except std::invalid_argument Cannot fetch metrics of unknown bitmap glyph - */ - virtual const glyph_metrics& get_glyph_metrics(char32_t code) const; - - /** - * Returns a reference to the glyph corresponding to a UTF-32 character code. - * - * @param code UTF-32 character code of a glyph. - * @return Reference to the corresponding glyph. - * - * @except std::invalid_argument Cannot get unknown bitmap glyph - */ - const bitmap_glyph& get_glyph(char32_t code) const; - - /// @copydoc bitmap_font::get_glyph(char32_t) const - bitmap_glyph& get_glyph(char32_t code); - - /** - * Returns a reference to the glyph corresponding to a UTF-32 character code, performing an insertion if such glyph does not already exist. - * - * @param code UTF-32 character code of a glyph. - * @return Reference to the corresponding glyph. - */ - bitmap_glyph& operator[](char32_t code); - -private: - std::unordered_map glyphs; - image bitmap; -}; - -inline const image& bitmap_font::get_bitmap() const -{ - return bitmap; -} - -inline image& bitmap_font::get_bitmap() -{ - return bitmap; -} - -inline bitmap_glyph& bitmap_font::operator[](char32_t code) -{ - return glyphs[code]; -} - -} // namespace type - -#endif // ANTKEEPER_TYPE_BITMAP_FONT_HPP diff --git a/src/type/bitmap-glyph.hpp b/src/type/bitmap-glyph.hpp deleted file mode 100644 index 2964fe1..0000000 --- a/src/type/bitmap-glyph.hpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_TYPE_BITMAP_GLYPH_HPP -#define ANTKEEPER_TYPE_BITMAP_GLYPH_HPP - -#include "resources/image.hpp" -#include "type/glyph-metrics.hpp" -#include "utility/fundamental-types.hpp" - -namespace type { - -/** - * Single glyph in a bitmap font. - * - * @see type::bitmap_font - */ -struct bitmap_glyph -{ - /// Metrics describing the glyph. - glyph_metrics metrics; - - /// Bitmap representing the glyph. - image bitmap; - - /// Position of the packed glyph bitmap within the font bitmap. - uint2 position; -}; - -} // namespace type - -#endif // ANTKEEPER_TYPE_BITMAP_GLYPH_HPP diff --git a/src/type/font.cpp b/src/type/font.cpp deleted file mode 100644 index 5488b39..0000000 --- a/src/type/font.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "type/font.hpp" - -namespace type { - -font::font(const font_metrics& metrics): - metrics(metrics) -{} - -font::font() -{} - -font::~font() -{} - -void font::kern(char32_t first, char32_t second, const float2& offset) -{ - kerning_table[first][second] = offset; -} - -void font::set_font_metrics(const font_metrics& metrics) -{ - this->metrics = metrics; -} - -const float2& font::get_kerning(char32_t first, char32_t second) const -{ - if (auto it_first = kerning_table.find(first); it_first != kerning_table.end()) - if (auto it_second = it_first->second.find(second); it_second != it_first->second.end()) - return it_second->second; - - static const float2 no_kerning = {0.0f, 0.0f}; - return no_kerning; -} - -} // namespace type diff --git a/src/type/font.hpp b/src/type/font.hpp deleted file mode 100644 index 25bd73a..0000000 --- a/src/type/font.hpp +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_TYPE_FONT_HPP -#define ANTKEEPER_TYPE_FONT_HPP - -#include "type/kerning-table.hpp" -#include "type/font-metrics.hpp" -#include "type/glyph-metrics.hpp" - -namespace type { - -/** - * Abstract base class for fonts. - * - * @see type::font_metrics - * @see type::glyph_metrics - * @see type::bitmap_font - */ -class font -{ -public: - /** - * Creates a font and sets its metrics. - * - * @param metrics Metrics describing the font. - */ - font(const font_metrics& metrics); - - /// Creates an empty font. - font(); - - /// Destroys a font. - virtual ~font(); - - /** - * Returns `true` if the font contains a glyph with the given character code. - * - * @param code UTF-32 character code of a glyph. - * @return `true` if the font contains the glyph, `false` otherwise. - */ - virtual bool contains(char32_t code) const = 0; - - /** - * Sets the kerning offset for a pair of glyphs. - * - * @param first UTF-32 character code of the first glyph. - * @param second UTF-32 character code of the second glyph. - * @param offset Kerning offset. - */ - void kern(char32_t first, char32_t second, const float2& offset); - - /** - * Sets the font metrics - * - * @param metrics Font metrics. - */ - void set_font_metrics(const font_metrics& metrics); - - /** - * Returns metrics describing a glyph. - * - * @param code UTF-32 character code of a glyph. - * @return Metrics describing the glyph. - */ - virtual const glyph_metrics& get_glyph_metrics(char32_t code) const = 0; - - /** - * Returns the kerning offset for a pair of glyphs. - * - * @param first UTF-32 character code of the first glyph. - * @param second UTF-32 character code of the second glyph. - * @return Kerning offset. - */ - const float2& get_kerning(char32_t first, char32_t second) const; - - /// Returns the font's kerning table. - const kerning_table& get_kerning_table() const; - - /// Returns metrics describing the font. - const font_metrics& get_font_metrics() const; - -protected: - font_metrics metrics; - kerning_table kerning_table; -}; - -inline const kerning_table& font::get_kerning_table() const -{ - return kerning_table; -} - -inline const font_metrics& font::get_font_metrics() const -{ - return metrics; -} - -} // namespace type - -#endif // ANTKEEPER_TYPE_FONT_HPP diff --git a/src/type/freetype/typeface.cpp b/src/type/freetype/typeface.cpp deleted file mode 100644 index 15740f7..0000000 --- a/src/type/freetype/typeface.cpp +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "type/freetype/typeface.hpp" -#include -#include - -namespace type { -namespace freetype { - -typeface::typeface(FT_Library library, FT_Face face, unsigned char* buffer): - library(library), - face(face), - buffer(buffer), - height(-1.0f) -{ - /// Build charset - FT_UInt index; - FT_ULong c = FT_Get_First_Char(face, &index); - while (index) - { - this->charset.insert(static_cast(c)); - c = FT_Get_Next_Char(face, c, &index); - } -} - -typeface::~typeface() -{ - FT_Done_Face(face); - delete[] buffer; - FT_Done_FreeType(library); -} - -bool typeface::has_kerning() const -{ - return FT_HAS_KERNING(face); -} - -bool typeface::get_metrics(float height, font_metrics& metrics) const -{ - // Set font size - set_face_pixel_size(height); - - // Get font metrics - metrics.size = height; - metrics.ascent = face->size->metrics.ascender / 64.0f; - metrics.descent = face->size->metrics.descender / 64.0f; - metrics.linespace = face->size->metrics.height / 64.0f; - metrics.linegap = metrics.linespace - (metrics.ascent - metrics.descent); - metrics.underline_position = FT_MulFix(face->underline_position, face->size->metrics.y_scale) / 64.0f; - metrics.underline_thickness = FT_MulFix(face->underline_thickness, face->size->metrics.y_scale) / 64.0f; - metrics.max_horizontal_advance = face->size->metrics.max_advance / 64.0f; - metrics.max_vertical_advance = FT_MulFix(face->max_advance_height, face->size->metrics.y_scale) / 64.0f; - - return true; -} - -bool typeface::get_metrics(float height, char32_t code, glyph_metrics& metrics) const -{ - // Set font size - set_face_pixel_size(height); - - // Get index of glyph - FT_UInt glyph_index = FT_Get_Char_Index(face, static_cast(code)); - - // Load glyph and render bitmap - FT_Error error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT); - if (error) - { - throw std::runtime_error("FreeType failed to load glyph (error code " + std::to_string(error) + ")"); - } - - // Get glyph metrics - metrics.width = face->glyph->metrics.width / 64.0f; - metrics.height = face->glyph->metrics.height / 64.0f; - metrics.horizontal_bearing.x() = face->glyph->metrics.horiBearingX / 64.0f; - metrics.horizontal_bearing.y() = face->glyph->metrics.horiBearingY / 64.0f; - metrics.vertical_bearing.x() = face->glyph->metrics.vertBearingX / 64.0f; - metrics.vertical_bearing.y() = face->glyph->metrics.vertBearingY / 64.0f; - metrics.horizontal_advance = face->glyph->metrics.horiAdvance / 64.0f; - metrics.vertical_advance = face->glyph->metrics.vertAdvance / 64.0f; - - return true; -} - -bool typeface::get_bitmap(float height, char32_t code, image& bitmap) const -{ - // Set font size - set_face_pixel_size(height); - - // Get index of glyph - FT_UInt glyph_index = FT_Get_Char_Index(face, static_cast(code)); - - // Load glyph and render bitmap - FT_Error error = FT_Load_Glyph(face, glyph_index, FT_LOAD_RENDER | FT_LOAD_TARGET_(FT_RENDER_MODE_NORMAL)); - if (error) - { - throw std::runtime_error("FreeType failed to load glyph (error code " + std::to_string(error) + ")"); - } - - // Format and resize bitmap - bitmap.resize(0, 0); - bitmap.format(sizeof(unsigned char), 1); - bitmap.resize(face->glyph->bitmap.width, face->glyph->bitmap.rows); - - // Copy glyph bitmap data in bitmap - std::memcpy(bitmap.data(), face->glyph->bitmap.buffer, bitmap.get_size()); - - return true; -} - -bool typeface::get_kerning(float height, char32_t first, char32_t second, float2& offset) const -{ - // Check if typeface has kerning information - if (!has_kerning()) - return false; - - // Set font size - set_face_pixel_size(height); - - // Get indices of the two glyphs - FT_UInt first_index = FT_Get_Char_Index(face, static_cast(first)); - FT_UInt second_index = FT_Get_Char_Index(face, static_cast(second)); - - // Get kerning vector - FT_Vector kerning; - FT_Error error = FT_Get_Kerning(face, first_index, second_index, FT_KERNING_DEFAULT, &kerning); - if (error) - { - throw std::runtime_error("FreeType failed to get kerning vector (error code " + std::to_string(error) + ")"); - } - - offset = float2{static_cast(kerning.x), static_cast(kerning.y)} / 64.0f; - - return true; -} - -void typeface::set_face_pixel_size(float height) const -{ - if (this->height == height) - return; - - FT_Error error = FT_Set_Pixel_Sizes(face, 0, static_cast(height)); - if (error) - { - throw std::runtime_error("FreeType failed to set face size (error code " + std::to_string(error) + ")"); - } - - this->height = height; -} - -} // namespace freetype -} // namespace type diff --git a/src/type/freetype/typeface.hpp b/src/type/freetype/typeface.hpp deleted file mode 100644 index a705cb5..0000000 --- a/src/type/freetype/typeface.hpp +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_TYPE_FREETYPE_TYPEFACE_HPP -#define ANTKEEPER_TYPE_FREETYPE_TYPEFACE_HPP - -#include "type/typeface.hpp" -#include -#include FT_FREETYPE_H - -namespace type { -namespace freetype { - -/** - * Typeface implementation using the FreeType library. - * - * @see type::typeface - */ -class typeface: public type::typeface -{ -public: - /** - * Creates a FreeType typeface. - * - * @param library Pointer to a FreeType library instance. - * @param face Pointer to the FreeType object instance. - * @param buffer Pointer to file buffer containing FreeType face data. - */ - typeface(FT_Library library, FT_Face face, unsigned char* buffer); - - /// Destroys a FreeType typeface. - virtual ~typeface(); - - /// @copydoc type::typeface::has_kerning() const - virtual bool has_kerning() const; - - /// @copydoc type::typeface::get_metrics(float, font_metrics&) const - virtual bool get_metrics(float height, font_metrics& metrics) const; - - /// @copydoc type::typeface::get_metrics(float, char32_t, glyph_metrics&) const - virtual bool get_metrics(float height, char32_t code, glyph_metrics& metrics) const; - - /// @copydoc type::typeface::get_bitmap(float, char32_t, image&) const - virtual bool get_bitmap(float height, char32_t code, image& bitmap) const; - - /// @copydoc type::typeface::get_kerning(float, char32_t, char32_t, float2&) const - virtual bool get_kerning(float height, char32_t first, char32_t second, float2& offset) const; - -private: - void set_face_pixel_size(float height) const; - - FT_Library library; - FT_Face face; - unsigned char* buffer; - mutable float height; -}; - -} // namespace freetype -} // namespace type - -#endif // ANTKEEPER_TYPE_FREETYPE_TYPEFACE_HPP diff --git a/src/type/glyph-metrics.hpp b/src/type/glyph-metrics.hpp deleted file mode 100644 index 53e131b..0000000 --- a/src/type/glyph-metrics.hpp +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_TYPE_GLYPH_METRICS_HPP -#define ANTKEEPER_TYPE_GLYPH_METRICS_HPP - -#include "utility/fundamental-types.hpp" - -namespace type { - -/** - * Metrics describing properties of a glyph. - */ -struct glyph_metrics -{ - /// Horizontal extent of the glyph. - float width; - - /// Vertical extent of the glyph. - float height; - - /// Offset from the pen position to the glyph's top-left edge, in horizontal layouts. - float2 horizontal_bearing; - - /// Offset from the pen position to the glph's top-left edge, in vertical layouts. - float2 vertical_bearing; - - /// Distance to move the pen position after the glyph has been rendered, in horizontal layouts. - float horizontal_advance; - - /// Distance to move the pen position after the glyph has been rendered, in vertical layouts. - float vertical_advance; -}; - -} // namespace type - -#endif // ANTKEEPER_TYPE_GLYPH_METRICS_HPP diff --git a/src/type/kerning-table.hpp b/src/type/kerning-table.hpp deleted file mode 100644 index 5cf8c48..0000000 --- a/src/type/kerning-table.hpp +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_TYPE_KERNING_TABLE_HPP -#define ANTKEEPER_TYPE_KERNING_TABLE_HPP - -#include "utility/fundamental-types.hpp" -#include - -namespace type { - -/// Table containing kerning offsets for pairs of glyphs. -typedef std::unordered_map> kerning_table; - -} // namespace type - -#endif // ANTKEEPER_TYPE_KERNING_TABLE_HPP diff --git a/src/type/type.hpp b/src/type/type.hpp deleted file mode 100644 index c37d9fe..0000000 --- a/src/type/type.hpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_TYPE_HPP -#define ANTKEEPER_TYPE_HPP - -/// Text and typography. -namespace type {} - -#include "type/bitmap-font.hpp" -#include "type/bitmap-glyph.hpp" -#include "type/font.hpp" -#include "type/font-metrics.hpp" -#include "type/glyph-metrics.hpp" -#include "type/kerning-table.hpp" -#include "type/text-direction.hpp" -#include "type/unicode/unicode.hpp" - -#endif // ANTKEEPER_TYPE_HPP diff --git a/src/type/typeface.cpp b/src/type/typeface.cpp deleted file mode 100644 index 38b95d9..0000000 --- a/src/type/typeface.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "type/typeface.hpp" - -namespace type { - -typeface::typeface(typeface_style style, int weight): - style(style), - weight(weight) -{} - -typeface::typeface(): - style(typeface_style::normal), - weight(400) -{} - -void typeface::set_style(typeface_style style) -{ - this->style = style; -} - -void typeface::set_weight(int weight) -{ - this->weight = weight; -} - -} // namespace type diff --git a/src/type/typeface.hpp b/src/type/typeface.hpp deleted file mode 100644 index 212be2b..0000000 --- a/src/type/typeface.hpp +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_TYPE_TYPEFACE_HPP -#define ANTKEEPER_TYPE_TYPEFACE_HPP - -#include "type/font-metrics.hpp" -#include "type/glyph-metrics.hpp" -#include "resources/image.hpp" -#include "utility/fundamental-types.hpp" -#include - -namespace type { - -/// Emumerates typeface styles. -enum class typeface_style -{ - /// Normal typeface style. - normal, - - /// Italic typeface style. - italic, - - /// Oblique typeface style. - oblique -}; - -/** - * Abstract base class for a typeface, which corresponds to a single digital font file. - * - * @see type::font - */ -class typeface -{ -public: - /** - * Creates a typeface, setting its style and weight. - * - * @param style Typeface style. - * @param weight Typeface weight. - */ - typeface(typeface_style style, int weight); - - /// Creates an empty typeface. - typeface(); - - /// Destroys a typeface. - virtual ~typeface() = default; - - /** - * Sets the style of the typeface. - * - * @param style Typeface style. - */ - void set_style(typeface_style style); - - /** - * Sets the weight of the typeface. - * - * @param weight Typeface weight. - */ - void set_weight(int weight); - - /// Returns the style of the typeface. - [[nodiscard]] inline typeface_style get_style() const noexcept - { - return style; - } - - /// Returns the weight of the typeface. - [[nodiscard]] inline int get_weight() const noexcept - { - return weight; - } - - /// Returns `true` if the typeface contains kerning information, `false` otherwise. - virtual bool has_kerning() const = 0; - - /** - * Gets metrics for a font of the specified size. - * - * @param[in] height Height of the font, in pixels. - * @param[out] metrics Font metrics. - * @return `true` if font metrics were returned, `false` otherwise. - */ - virtual bool get_metrics(float height, font_metrics& metrics) const = 0; - - /** - * Gets metrics for a glyph in a font of the specified size. - * - * @param[in] height Height of the font, in pixels. - * @param[in] code UTF-32 character code of a glyph. - * @param[out] metrics Glyph metrics. - * @return `true` if glyph metrics were returned, `false` otherwise. - */ - virtual bool get_metrics(float height, char32_t code, glyph_metrics& metrics) const = 0; - - /** - * Gets a bitmap of a glyph in a font of the specified size. - * - * @param[in] height Height of the font, in pixels. - * @param[in] code UTF-32 character code of a glyph. - * @param[out] bitmap Glyph bitmap data. - * @return `true` if glyph bitmap data was returned, `false` otherwise. - */ - virtual bool get_bitmap(float height, char32_t code, image& bitmap) const = 0; - - /** - * Gets the kerning offset for a pair of glyphs. - * - * @param[in] height Height of the font, in pixels. - * @param[in] first UTF-32 character code of the first glyph. - * @param[in] second UTF-32 character code of the second glyph. - * @param[out] offset Kerning offset. - * @return `true` if a kerning offset was returned, `false` otherwise. - */ - virtual bool get_kerning(float height, char32_t first, char32_t second, float2& offset) const = 0; - - /// Returns the set of characters supported by the typeface. - [[nodiscard]] inline const std::unordered_set& get_charset() const noexcept - { - return charset; - } - -protected: - std::unordered_set charset; - -private: - typeface_style style; - int weight; -}; - -} // namespace type - -#endif // ANTKEEPER_TYPE_TYPEFACE_HPP diff --git a/src/type/unicode/block.cpp b/src/type/unicode/block.cpp deleted file mode 100644 index 41e4477..0000000 --- a/src/type/unicode/block.cpp +++ /dev/null @@ -1,347 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "type/unicode/block.hpp" - -namespace type { -namespace unicode { - -const block block::basic_latin{0x0, 0x7F}; -const block block::latin_1_supplement{0x80, 0xFF}; -const block block::latin_extended_a{0x0100, 0x017F}; -const block block::latin_extended_b{0x0180, 0x024F}; -const block block::ipa_extensions{0x0250, 0x02AF}; -const block block::spacing_modifier_letters{0x02B0, 0x02FF}; -const block block::combining_diacritical_marks{0x0300, 0x036F}; -const block block::greek_and_coptic{0x0370, 0x03FF}; -const block block::cyrillic{0x0400, 0x04FF}; -const block block::cyrillic_supplement{0x0500, 0x052F}; -const block block::armenian{0x0530, 0x058F}; -const block block::hebrew{0x0590, 0x05FF}; -const block block::arabic{0x0600, 0x06FF}; -const block block::syriac{0x0700, 0x074F}; -const block block::arabic_supplement{0x0750, 0x077F}; -const block block::thaana{0x0780, 0x07BF}; -const block block::nko{0x07C0, 0x07FF}; -const block block::samaritan{0x0800, 0x083F}; -const block block::mandaic{0x0840, 0x085F}; -const block block::syriac_supplement{0x0860, 0x086F}; -const block block::arabic_extended_b{0x0870, 0x089F}; -const block block::arabic_extended_a{0x08A0, 0x08FF}; -const block block::devanagari{0x0900, 0x097F}; -const block block::bengali{0x0980, 0x09FF}; -const block block::gurmukhi{0x0A00, 0x0A7F}; -const block block::gujarati{0x0A80, 0x0AFF}; -const block block::oriya{0x0B00, 0x0B7F}; -const block block::tamil{0x0B80, 0x0BFF}; -const block block::telugu{0x0C00, 0x0C7F}; -const block block::kannada{0x0C80, 0x0CFF}; -const block block::malayalam{0x0D00, 0x0D7F}; -const block block::sinhala{0x0D80, 0x0DFF}; -const block block::thai{0x0E00, 0x0E7F}; -const block block::lao{0x0E80, 0x0EFF}; -const block block::tibetan{0x0F00, 0x0FFF}; -const block block::myanmar{0x1000, 0x109F}; -const block block::georgian{0x10A0, 0x10FF}; -const block block::hangul_jamo{0x1100, 0x11FF}; -const block block::ethiopic{0x1200, 0x137F}; -const block block::ethiopic_supplement{0x1380, 0x139F}; -const block block::cherokee{0x13A0, 0x13FF}; -const block block::unified_canadian_aboriginal_syllabics{0x1400, 0x167F}; -const block block::ogham{0x1680, 0x169F}; -const block block::runic{0x16A0, 0x16FF}; -const block block::tagalog{0x1700, 0x171F}; -const block block::hanunoo{0x1720, 0x173F}; -const block block::buhid{0x1740, 0x175F}; -const block block::tagbanwa{0x1760, 0x177F}; -const block block::khmer{0x1780, 0x17FF}; -const block block::mongolian{0x1800, 0x18AF}; -const block block::unified_canadian_aboriginal_syllabics_extended{0x18B0, 0x18FF}; -const block block::limbu{0x1900, 0x194F}; -const block block::tai_le{0x1950, 0x197F}; -const block block::new_tai_lue{0x1980, 0x19DF}; -const block block::khmer_symbols{0x19E0, 0x19FF}; -const block block::buginese{0x1A00, 0x1A1F}; -const block block::tai_tham{0x1A20, 0x1AAF}; -const block block::combining_diacritical_marks_extended{0x1AB0, 0x1AFF}; -const block block::balinese{0x1B00, 0x1B7F}; -const block block::sundanese{0x1B80, 0x1BBF}; -const block block::batak{0x1BC0, 0x1BFF}; -const block block::lepcha{0x1C00, 0x1C4F}; -const block block::ol_chiki{0x1C50, 0x1C7F}; -const block block::cyrillic_extended_c{0x1C80, 0x1C8F}; -const block block::georgian_extended{0x1C90, 0x1CBF}; -const block block::sundanese_supplement{0x1CC0, 0x1CCF}; -const block block::vedic_extensions{0x1CD0, 0x1CFF}; -const block block::phonetic_extensions{0x1D00, 0x1D7F}; -const block block::phonetic_extensions_supplement{0x1D80, 0x1DBF}; -const block block::combining_diacritical_marks_supplement{0x1DC0, 0x1DFF}; -const block block::latin_extended_additional{0x1E00, 0x1EFF}; -const block block::greek_extended{0x1F00, 0x1FFF}; -const block block::general_punctuation{0x2000, 0x206F}; -const block block::superscripts_and_subscripts{0x2070, 0x209F}; -const block block::currency_symbols{0x20A0, 0x20CF}; -const block block::combining_diacritical_marks_for_symbols{0x20D0, 0x20FF}; -const block block::letterlike_symbols{0x2100, 0x214F}; -const block block::number_forms{0x2150, 0x218F}; -const block block::arrows{0x2190, 0x21FF}; -const block block::mathematical_operators{0x2200, 0x22FF}; -const block block::miscellaneous_technical{0x2300, 0x23FF}; -const block block::control_pictures{0x2400, 0x243F}; -const block block::optical_character_recognition{0x2440, 0x245F}; -const block block::enclosed_alphanumerics{0x2460, 0x24FF}; -const block block::box_drawing{0x2500, 0x257F}; -const block block::block_elements{0x2580, 0x259F}; -const block block::geometric_shapes{0x25A0, 0x25FF}; -const block block::miscellaneous_symbols{0x2600, 0x26FF}; -const block block::dingbats{0x2700, 0x27BF}; -const block block::miscellaneous_mathematical_symbols_a{0x27C0, 0x27EF}; -const block block::supplemental_arrows_a{0x27F0, 0x27FF}; -const block block::braille_patterns{0x2800, 0x28FF}; -const block block::supplemental_arrows_b{0x2900, 0x297F}; -const block block::miscellaneous_mathematical_symbols_b{0x2980, 0x29FF}; -const block block::supplemental_mathematical_operators{0x2A00, 0x2AFF}; -const block block::miscellaneous_symbols_and_arrows{0x2B00, 0x2BFF}; -const block block::glagolitic{0x2C00, 0x2C5F}; -const block block::latin_extended_c{0x2C60, 0x2C7F}; -const block block::coptic{0x2C80, 0x2CFF}; -const block block::georgian_supplement{0x2D00, 0x2D2F}; -const block block::tifinagh{0x2D30, 0x2D7F}; -const block block::ethiopic_extended{0x2D80, 0x2DDF}; -const block block::cyrillic_extended_a{0x2DE0, 0x2DFF}; -const block block::supplemental_punctuation{0x2E00, 0x2E7F}; -const block block::cjk_radicals_supplement{0x2E80, 0x2EFF}; -const block block::kangxi_radicals{0x2F00, 0x2FDF}; -const block block::ideographic_description_characters{0x2FF0, 0x2FFF}; -const block block::cjk_symbols_and_punctuation{0x3000, 0x303F}; -const block block::hiragana{0x3040, 0x309F}; -const block block::katakana{0x30A0, 0x30FF}; -const block block::bopomofo{0x3100, 0x312F}; -const block block::hangul_compatibility_jamo{0x3130, 0x318F}; -const block block::kanbun{0x3190, 0x319F}; -const block block::bopomofo_extended{0x31A0, 0x31BF}; -const block block::cjk_strokes{0x31C0, 0x31EF}; -const block block::katakana_phonetic_extensions{0x31F0, 0x31FF}; -const block block::enclosed_cjk_letters_and_months{0x3200, 0x32FF}; -const block block::cjk_compatibility{0x3300, 0x33FF}; -const block block::cjk_unified_ideographs_extension_a{0x3400, 0x4DBF}; -const block block::yijing_hexagram_symbols{0x4DC0, 0x4DFF}; -const block block::cjk_unified_ideographs{0x4E00, 0x9FFF}; -const block block::yi_syllables{0xA000, 0xA48F}; -const block block::yi_radicals{0xA490, 0xA4CF}; -const block block::lisu{0xA4D0, 0xA4FF}; -const block block::vai{0xA500, 0xA63F}; -const block block::cyrillic_extended_b{0xA640, 0xA69F}; -const block block::bamum{0xA6A0, 0xA6FF}; -const block block::modifier_tone_letters{0xA700, 0xA71F}; -const block block::latin_extended_d{0xA720, 0xA7FF}; -const block block::syloti_nagri{0xA800, 0xA82F}; -const block block::common_indic_number_forms{0xA830, 0xA83F}; -const block block::phags_pa{0xA840, 0xA87F}; -const block block::saurashtra{0xA880, 0xA8DF}; -const block block::devanagari_extended{0xA8E0, 0xA8FF}; -const block block::kayah_li{0xA900, 0xA92F}; -const block block::rejang{0xA930, 0xA95F}; -const block block::hangul_jamo_extended_a{0xA960, 0xA97F}; -const block block::javanese{0xA980, 0xA9DF}; -const block block::myanmar_extended_b{0xA9E0, 0xA9FF}; -const block block::cham{0xAA00, 0xAA5F}; -const block block::myanmar_extended_a{0xAA60, 0xAA7F}; -const block block::tai_viet{0xAA80, 0xAADF}; -const block block::meetei_mayek_extensions{0xAAE0, 0xAAFF}; -const block block::ethiopic_extended_a{0xAB00, 0xAB2F}; -const block block::latin_extended_e{0xAB30, 0xAB6F}; -const block block::cherokee_supplement{0xAB70, 0xABBF}; -const block block::meetei_mayek{0xABC0, 0xABFF}; -const block block::hangul_syllables{0xAC00, 0xD7AF}; -const block block::hangul_jamo_extended_b{0xD7B0, 0xD7FF}; -const block block::high_surrogates{0xD800, 0xDB7F}; -const block block::high_private_use_surrogates{0xDB80, 0xDBFF}; -const block block::low_surrogates{0xDC00, 0xDFFF}; -const block block::private_use_area{0xE000, 0xF8FF}; -const block block::cjk_compatibility_ideographs{0xF900, 0xFAFF}; -const block block::alphabetic_presentation_forms{0xFB00, 0xFB4F}; -const block block::arabic_presentation_forms_a{0xFB50, 0xFDFF}; -const block block::variation_selectors{0xFE00, 0xFE0F}; -const block block::vertical_forms{0xFE10, 0xFE1F}; -const block block::combining_half_marks{0xFE20, 0xFE2F}; -const block block::cjk_compatibility_forms{0xFE30, 0xFE4F}; -const block block::small_form_variants{0xFE50, 0xFE6F}; -const block block::arabic_presentation_forms_b{0xFE70, 0xFEFF}; -const block block::halfwidth_and_fullwidth_forms{0xFF00, 0xFFEF}; -const block block::specials{0xFFF0, 0xFFFF}; -const block block::linear_b_syllabary{0x10000, 0x1007F}; -const block block::linear_b_ideograms{0x10080, 0x100FF}; -const block block::aegean_numbers{0x10100, 0x1013F}; -const block block::ancient_greek_numbers{0x10140, 0x1018F}; -const block block::ancient_symbols{0x10190, 0x101CF}; -const block block::phaistos_disc{0x101D0, 0x101FF}; -const block block::lycian{0x10280, 0x1029F}; -const block block::carian{0x102A0, 0x102DF}; -const block block::coptic_epact_numbers{0x102E0, 0x102FF}; -const block block::old_italic{0x10300, 0x1032F}; -const block block::gothic{0x10330, 0x1034F}; -const block block::old_permic{0x10350, 0x1037F}; -const block block::ugaritic{0x10380, 0x1039F}; -const block block::old_persian{0x103A0, 0x103DF}; -const block block::deseret{0x10400, 0x1044F}; -const block block::shavian{0x10450, 0x1047F}; -const block block::osmanya{0x10480, 0x104AF}; -const block block::osage{0x104B0, 0x104FF}; -const block block::elbasan{0x10500, 0x1052F}; -const block block::caucasian_albanian{0x10530, 0x1056F}; -const block block::vithkuqi{0x10570, 0x105BF}; -const block block::linear_a{0x10600, 0x1077F}; -const block block::latin_extended_f{0x10780, 0x107BF}; -const block block::cypriot_syllabary{0x10800, 0x1083F}; -const block block::imperial_aramaic{0x10840, 0x1085F}; -const block block::palmyrene{0x10860, 0x1087F}; -const block block::nabataean{0x10880, 0x108AF}; -const block block::hatran{0x108E0, 0x108FF}; -const block block::phoenician{0x10900, 0x1091F}; -const block block::lydian{0x10920, 0x1093F}; -const block block::meroitic_hieroglyphs{0x10980, 0x1099F}; -const block block::meroitic_cursive{0x109A0, 0x109FF}; -const block block::kharoshthi{0x10A00, 0x10A5F}; -const block block::old_south_arabian{0x10A60, 0x10A7F}; -const block block::old_north_arabian{0x10A80, 0x10A9F}; -const block block::manichaean{0x10AC0, 0x10AFF}; -const block block::avestan{0x10B00, 0x10B3F}; -const block block::inscriptional_parthian{0x10B40, 0x10B5F}; -const block block::inscriptional_pahlavi{0x10B60, 0x10B7F}; -const block block::psalter_pahlavi{0x10B80, 0x10BAF}; -const block block::old_turkic{0x10C00, 0x10C4F}; -const block block::old_hungarian{0x10C80, 0x10CFF}; -const block block::hanifi_rohingya{0x10D00, 0x10D3F}; -const block block::rumi_numeral_symbols{0x10E60, 0x10E7F}; -const block block::yezidi{0x10E80, 0x10EBF}; -const block block::old_sogdian{0x10F00, 0x10F2F}; -const block block::sogdian{0x10F30, 0x10F6F}; -const block block::old_uyghur{0x10F70, 0x10FAF}; -const block block::chorasmian{0x10FB0, 0x10FDF}; -const block block::elymaic{0x10FE0, 0x10FFF}; -const block block::brahmi{0x11000, 0x1107F}; -const block block::kaithi{0x11080, 0x110CF}; -const block block::sora_sompeng{0x110D0, 0x110FF}; -const block block::chakma{0x11100, 0x1114F}; -const block block::mahajani{0x11150, 0x1117F}; -const block block::sharada{0x11180, 0x111DF}; -const block block::sinhala_archaic_numbers{0x111E0, 0x111FF}; -const block block::khojki{0x11200, 0x1124F}; -const block block::multani{0x11280, 0x112AF}; -const block block::khudawadi{0x112B0, 0x112FF}; -const block block::grantha{0x11300, 0x1137F}; -const block block::newa{0x11400, 0x1147F}; -const block block::tirhuta{0x11480, 0x114DF}; -const block block::siddham{0x11580, 0x115FF}; -const block block::modi{0x11600, 0x1165F}; -const block block::mongolian_supplement{0x11660, 0x1167F}; -const block block::takri{0x11680, 0x116CF}; -const block block::ahom{0x11700, 0x1174F}; -const block block::dogra{0x11800, 0x1184F}; -const block block::warang_citi{0x118A0, 0x118FF}; -const block block::dives_akuru{0x11900, 0x1195F}; -const block block::nandinagari{0x119A0, 0x119FF}; -const block block::zanabazar_square{0x11A00, 0x11A4F}; -const block block::soyombo{0x11A50, 0x11AAF}; -const block block::unified_canadian_aboriginal_syllabics_extended_a{0x11AB0, 0x11ABF}; -const block block::pau_cin_hau{0x11AC0, 0x11AFF}; -const block block::bhaiksuki{0x11C00, 0x11C6F}; -const block block::marchen{0x11C70, 0x11CBF}; -const block block::masaram_gondi{0x11D00, 0x11D5F}; -const block block::gunjala_gondi{0x11D60, 0x11DAF}; -const block block::makasar{0x11EE0, 0x11EFF}; -const block block::lisu_supplement{0x11FB0, 0x11FBF}; -const block block::tamil_supplement{0x11FC0, 0x11FFF}; -const block block::cuneiform{0x12000, 0x123FF}; -const block block::cuneiform_numbers_and_punctuation{0x12400, 0x1247F}; -const block block::early_dynastic_cuneiform{0x12480, 0x1254F}; -const block block::cypro_minoan{0x12F90, 0x12FFF}; -const block block::egyptian_hieroglyphs{0x13000, 0x1342F}; -const block block::egyptian_hieroglyph_format_controls{0x13430, 0x1343F}; -const block block::anatolian_hieroglyphs{0x14400, 0x1467F}; -const block block::bamum_supplement{0x16800, 0x16A3F}; -const block block::mro{0x16A40, 0x16A6F}; -const block block::tangsa{0x16A70, 0x16ACF}; -const block block::bassa_vah{0x16AD0, 0x16AFF}; -const block block::pahawh_hmong{0x16B00, 0x16B8F}; -const block block::medefaidrin{0x16E40, 0x16E9F}; -const block block::miao{0x16F00, 0x16F9F}; -const block block::ideographic_symbols_and_punctuation{0x16FE0, 0x16FFF}; -const block block::tangut{0x17000, 0x187FF}; -const block block::tangut_components{0x18800, 0x18AFF}; -const block block::khitan_small_script{0x18B00, 0x18CFF}; -const block block::tangut_supplement{0x18D00, 0x18D7F}; -const block block::kana_extended_b{0x1AFF0, 0x1AFFF}; -const block block::kana_supplement{0x1B000, 0x1B0FF}; -const block block::kana_extended_a{0x1B100, 0x1B12F}; -const block block::small_kana_extension{0x1B130, 0x1B16F}; -const block block::nushu{0x1B170, 0x1B2FF}; -const block block::duployan{0x1BC00, 0x1BC9F}; -const block block::shorthand_format_controls{0x1BCA0, 0x1BCAF}; -const block block::znamenny_musical_notation{0x1CF00, 0x1CFCF}; -const block block::byzantine_musical_symbols{0x1D000, 0x1D0FF}; -const block block::musical_symbols{0x1D100, 0x1D1FF}; -const block block::ancient_greek_musical_notation{0x1D200, 0x1D24F}; -const block block::mayan_numerals{0x1D2E0, 0x1D2FF}; -const block block::tai_xuan_jing_symbols{0x1D300, 0x1D35F}; -const block block::counting_rod_numerals{0x1D360, 0x1D37F}; -const block block::mathematical_alphanumeric_symbols{0x1D400, 0x1D7FF}; -const block block::sutton_signwriting{0x1D800, 0x1DAAF}; -const block block::latin_extended_g{0x1DF00, 0x1DFFF}; -const block block::glagolitic_supplement{0x1E000, 0x1E02F}; -const block block::nyiakeng_puachue_hmong{0x1E100, 0x1E14F}; -const block block::toto{0x1E290, 0x1E2BF}; -const block block::wancho{0x1E2C0, 0x1E2FF}; -const block block::ethiopic_extended_b{0x1E7E0, 0x1E7FF}; -const block block::mende_kikakui{0x1E800, 0x1E8DF}; -const block block::adlam{0x1E900, 0x1E95F}; -const block block::indic_siyaq_numbers{0x1EC70, 0x1ECBF}; -const block block::ottoman_siyaq_numbers{0x1ED00, 0x1ED4F}; -const block block::arabic_mathematical_alphabetic_symbols{0x1EE00, 0x1EEFF}; -const block block::mahjong_tiles{0x1F000, 0x1F02F}; -const block block::domino_tiles{0x1F030, 0x1F09F}; -const block block::playing_cards{0x1F0A0, 0x1F0FF}; -const block block::enclosed_alphanumeric_supplement{0x1F100, 0x1F1FF}; -const block block::enclosed_ideographic_supplement{0x1F200, 0x1F2FF}; -const block block::miscellaneous_symbols_and_pictographs{0x1F300, 0x1F5FF}; -const block block::emoticons{0x1F600, 0x1F64F}; -const block block::ornamental_dingbats{0x1F650, 0x1F67F}; -const block block::transport_and_map_symbols{0x1F680, 0x1F6FF}; -const block block::alchemical_symbols{0x1F700, 0x1F77F}; -const block block::geometric_shapes_extended{0x1F780, 0x1F7FF}; -const block block::supplemental_arrows_c{0x1F800, 0x1F8FF}; -const block block::supplemental_symbols_and_pictographs{0x1F900, 0x1F9FF}; -const block block::chess_symbols{0x1FA00, 0x1FA6F}; -const block block::symbols_and_pictographs_extended_a{0x1FA70, 0x1FAFF}; -const block block::symbols_for_legacy_computing{0x1FB00, 0x1FBFF}; -const block block::cjk_unified_ideographs_extension_b{0x20000, 0x2A6DF}; -const block block::cjk_unified_ideographs_extension_c{0x2A700, 0x2B73F}; -const block block::cjk_unified_ideographs_extension_d{0x2B740, 0x2B81F}; -const block block::cjk_unified_ideographs_extension_e{0x2B820, 0x2CEAF}; -const block block::cjk_unified_ideographs_extension_f{0x2CEB0, 0x2EBEF}; -const block block::cjk_compatibility_ideographs_supplement{0x2F800, 0x2FA1F}; -const block block::cjk_unified_ideographs_extension_g{0x30000, 0x3134F}; -const block block::tags{0xE0000, 0xE007F}; -const block block::variation_selectors_supplement{0xE0100, 0xE01EF}; -const block block::supplementary_private_use_area_a{0xF0000, 0xFFFFF}; -const block block::supplementary_private_use_area_b{0x100000, 0x10FFFF}; - -} // namespace unicode -} // namespace type diff --git a/src/type/unicode/convert.cpp b/src/type/unicode/convert.cpp deleted file mode 100644 index 8e7dedf..0000000 --- a/src/type/unicode/convert.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "type/unicode/convert.hpp" -#include - -namespace type { -namespace unicode { - -std::u32string u32(const std::string& u8) -{ - std::wstring_convert, char32_t> convert; - return convert.from_bytes(u8); -} - -std::string u8(const std::u32string& u32) -{ - std::wstring_convert, char32_t> convert; - return convert.to_bytes(u32); -} - -} // namespace unicode -} // namespace type diff --git a/src/type/unicode/unicode.hpp b/src/type/unicode/unicode.hpp deleted file mode 100644 index ebbacf8..0000000 --- a/src/type/unicode/unicode.hpp +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_TYPE_UNICODE_HPP -#define ANTKEEPER_TYPE_UNICODE_HPP - -namespace type { - -/// Unicode-related functions and data. -namespace unicode {} - -} // namespace type - -#include "type/unicode/block.hpp" -#include "type/unicode/convert.hpp" - -#endif // ANTKEEPER_TYPE_UNICODE_HPP diff --git a/src/utility/dict.cpp b/src/utility/dict.cpp deleted file mode 100644 index 2dfda06..0000000 --- a/src/utility/dict.cpp +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "utility/dict.hpp" -#include "resources/serializer.hpp" -#include "resources/serialize-error.hpp" -#include "resources/deserializer.hpp" -#include "resources/deserialize-error.hpp" -#include "utility/hash/fnv1a.hpp" -#include -#include -#include -#include -#include - -using namespace hash::literals; - -template -static void serialize_any(const std::any& any, serialize_context& ctx) -{ - serializer().serialize(std::any_cast(any), ctx); -} - -template -static void deserialize_any(std::any& any, deserialize_context& ctx) -{ - T value; - deserializer().deserialize(value, ctx); - any = std::move(value); -} - -/** - * Serializes a dict with an unsigned 32-bit key. - * - * @param[in] dict Dict to serialize. - * @param[in,out] ctx Serialize context. - * - * @throw serialize_error Write error. - * @throw serialize_error Unsupported dict value type. - */ -template <> -void serializer>::serialize(const dict& dict, serialize_context& ctx) -{ - // Map type indices to tuples containing a type hash and serialize function pointer - static const std::unordered_map - < - std::type_index, - std::tuple - < - std::uint32_t, - void (*)(const std::any&, serialize_context&) - > - > type_map - { - {std::type_index(typeid(bool)), {"bool"_fnv1a32, &serialize_any}}, - {std::type_index(typeid(std::uint8_t)), {"uint8"_fnv1a32, &serialize_any}}, - {std::type_index(typeid(std::uint16_t)), {"uint16"_fnv1a32, &serialize_any}}, - {std::type_index(typeid(std::uint32_t)), {"uint32"_fnv1a32, &serialize_any}}, - {std::type_index(typeid(std::uint64_t)), {"uint64"_fnv1a32, &serialize_any}}, - {std::type_index(typeid(std::int8_t)), {"int8"_fnv1a32, &serialize_any}}, - {std::type_index(typeid(std::int16_t)), {"int16"_fnv1a32, &serialize_any}}, - {std::type_index(typeid(std::int32_t)), {"int32"_fnv1a32, &serialize_any}}, - {std::type_index(typeid(std::int64_t)), {"int64"_fnv1a32, &serialize_any}}, - {std::type_index(typeid(float)), {"float"_fnv1a32, &serialize_any}}, - {std::type_index(typeid(double)), {"double"_fnv1a32, &serialize_any}}, - {std::type_index(typeid(std::string)), {"string"_fnv1a32, &serialize_any}}, - {std::type_index(typeid(std::u8string)), {"u8string"_fnv1a32, &serialize_any}}, - {std::type_index(typeid(std::u16string)), {"u16string"_fnv1a32, &serialize_any}}, - {std::type_index(typeid(std::u32string)), {"u32string"_fnv1a32, &serialize_any}} - }; - - // Write dict size - std::uint64_t size = static_cast(dict.size()); - ctx.write64(reinterpret_cast(&size), 1); - - // Write dict entries - for (const auto& [key, value]: dict) - { - if (auto i = type_map.find(value.type()); i != type_map.end()) - { - const auto& [type_hash, type_serializer] = i->second; - - // Write entry type hash and key - ctx.write32(reinterpret_cast(&type_hash), 1); - ctx.write32(reinterpret_cast(&key), 1); - - // Serialize entry value - type_serializer(value, ctx); - } - else - { - throw serialize_error("Unsupported dict value type"); - } - } -} - -/** - * Deserializes a dict with an unsigned 32-bit key. - * - * @param[out] dict Dict to serialize. - * @param[in,out] ctx Deserialize context. - * - * @throw deserialize_error Read error. - * @throw deserialize_error Unsupported dict value type. - */ -template <> -void deserializer>::deserialize(dict& dict, deserialize_context& ctx) -{ - // Map type hashes to deserialize function pointers - static const std::unordered_map - < - std::uint32_t, - void (*)(std::any&, deserialize_context&) - > type_map - { - {"bool"_fnv1a32, &deserialize_any}, - {"uint8"_fnv1a32, &deserialize_any}, - {"uint16"_fnv1a32, &deserialize_any}, - {"uint32"_fnv1a32, &deserialize_any}, - {"uint64"_fnv1a32, &deserialize_any}, - {"int8"_fnv1a32, &deserialize_any}, - {"int16"_fnv1a32, &deserialize_any}, - {"int32"_fnv1a32, &deserialize_any}, - {"int64"_fnv1a32, &deserialize_any}, - {"float"_fnv1a32, &deserialize_any}, - {"double"_fnv1a32, &deserialize_any}, - {"string"_fnv1a32, &deserialize_any}, - {"u8string"_fnv1a32, &deserialize_any}, - {"u16string"_fnv1a32, &deserialize_any}, - {"u32string"_fnv1a32, &deserialize_any} - }; - - dict.clear(); - - // Read dict size - std::uint64_t size = 0; - ctx.read64(reinterpret_cast(&size), 1); - - // Read dict entries - for (std::size_t i = 0; i < size; ++i) - { - // Read entry type hash - std::uint32_t type_hash = 0; - ctx.read32(reinterpret_cast(&type_hash), 1); - - if (auto i = type_map.find(type_hash); i != type_map.end()) - { - // Read entry key - std::uint32_t key = 0; - ctx.read32(reinterpret_cast(&key), 1); - - // Deserialize entry value - i->second(dict[key], ctx); - } - else - { - throw deserialize_error("Unsupported dict value type"); - } - } -} diff --git a/src/utility/fundamental-types.hpp b/src/utility/fundamental-types.hpp deleted file mode 100644 index bb35902..0000000 --- a/src/utility/fundamental-types.hpp +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_FUNDAMENTAL_TYPES_HPP -#define ANTKEEPER_FUNDAMENTAL_TYPES_HPP - -#include "math/vector.hpp" -#include "math/matrix.hpp" - -/// 2D vector of bools -using bool2 = math::vector; - -/// 3D vector of bools -using bool3 = math::vector; - -/// 4D vector of bools -using bool4 = math::vector; - -/// 2D vector of chars -using char2 = math::vector; - -/// 3D vector of chars -using char3 = math::vector; - -/// 4D vector of chars -using char4 = math::vector; - -/// 2D vector of unsigned chars -using uchar2 = math::vector; - -/// 3D vector of unsigned chars -using uchar3 = math::vector; - -/// 4D vector of unsigned chars -using uchar4 = math::vector; - -/// 2D vector of shorts -using short2 = math::vector; - -/// 3D vector of shorts -using short3 = math::vector; - -/// 4D vector of shorts -using short4 = math::vector; - -/// 2D vector of unsigned shorts -using ushort2 = math::vector; - -/// 3D vector of unsigned shorts -using ushort3 = math::vector; - -/// 4D vector of unsigned shorts -using ushort4 = math::vector; - -/// 2D vector of ints -using int2 = math::vector; - -/// 3D vector of ints -using int3 = math::vector; - -/// 4D vector of ints -using int4 = math::vector; - -/// 2D vector of unsigned ints -using uint2 = math::vector; - -/// 3D vector of unsigned ints -using uint3 = math::vector; - -/// 4D vector of unsigned ints -using uint4 = math::vector; - -/// 2D vector of longs -using long2 = math::vector; - -/// 3D vector of longs -using long3 = math::vector; - -/// 4D vector of longs -using long4 = math::vector; - -/// 2D vector of unsigned longs -using ulong2 = math::vector; - -/// 3D vector of unsigned longs -using ulong3 = math::vector; - -/// 4D vector of unsigned longs -using ulong4 = math::vector; - -/// 2D vector of floats -using float2 = math::vector; - -/// 3D vector of floats -using float3 = math::vector; - -/// 4D vector of floats -using float4 = math::vector; - -/// 2D vector of doubles -using double2 = math::vector; - -/// 3D vector of doubles -using double3 = math::vector; - -/// 4D vector of doubles -using double4 = math::vector; - -/// 2x2 matrix of bools -using bool2x2 = math::matrix; - -/// 3x3 matrix of bools -using bool3x3 = math::matrix; - -/// 4x4 matrix of bools -using bool4x4 = math::matrix; - -/// 2x2 matrix of chars -using char2x2 = math::matrix; - -/// 3x3 matrix of chars -using char3x3 = math::matrix; - -/// 4x4 matrix of chars -using char4x4 = math::matrix; - -/// 2x2 matrix of unsigned chars -using uchar2x2 = math::matrix; - -/// 3x3 matrix of unsigned chars -using uchar3x3 = math::matrix; - -/// 4x4 matrix of unsigned chars -using uchar4x4 = math::matrix; - -/// 2x2 matrix of shorts -using short2x2 = math::matrix; - -/// 3x3 matrix of shorts -using short3x3 = math::matrix; - -/// 4x4 matrix of shorts -using short4x4 = math::matrix; - -/// 2x2 matrix of unsigned shorts -using ushort2x2 = math::matrix; - -/// 3x3 matrix of unsigned shorts -using ushort3x3 = math::matrix; - -/// 4x4 matrix of unsigned shorts -using ushort4x4 = math::matrix; - -/// 2x2 matrix of ints -using int2x2 = math::matrix; - -/// 3x3 matrix of ints -using int3x3 = math::matrix; - -/// 4x4 matrix of ints -using int4x4 = math::matrix; - -/// 2x2 matrix of unsigned ints -using uint2x2 = math::matrix; - -/// 3x3 matrix of unsigned ints -using uint3x3 = math::matrix; - -/// 4x4 matrix of unsigned ints -using uint4x4 = math::matrix; - -/// 2x2 matrix of longs -using long2x2 = math::matrix; - -/// 3x3 matrix of longs -using long3x3 = math::matrix; - -/// 4x4 matrix of longs -using long4x4 = math::matrix; - -/// 2x2 matrix of unsigned longs -using ulong2x2 = math::matrix; - -/// 3x3 matrix of unsigned longs -using ulong3x3 = math::matrix; - -/// 4x4 matrix of unsigned longs -using ulong4x4 = math::matrix; - -/// 2x2 matrix of floats -using float2x2 = math::matrix; - -/// 3x3 matrix of floats -using float3x3 = math::matrix; - -/// 4x4 matrix of floats -using float4x4 = math::matrix; - -/// 2x2 matrix of doubles -using double2x2 = math::matrix; - -/// 3x3 matrix of doubles -using double3x3 = math::matrix; - -/// 4x4 matrix of doubles -using double4x4 = math::matrix; - -#endif // ANTKEEPER_FUNDAMENTAL_TYPES_HPP diff --git a/src/utility/hash.hpp b/src/utility/hash.hpp deleted file mode 100644 index 9ffba42..0000000 --- a/src/utility/hash.hpp +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#ifndef ANTKEEPER_UTILITY_HASH_HPP -#define ANTKEEPER_UTILITY_HASH_HPP - -/** - * Hash functions. - */ -namespace hash {} - -#include "utility/hash/fnv1a.hpp" -#include "utility/hash/literals.hpp" - -#endif // ANTKEEPER_UTILITY_HASH_HPP diff --git a/src/utility/paths.cpp b/src/utility/paths.cpp deleted file mode 100644 index 0f67264..0000000 --- a/src/utility/paths.cpp +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "utility/paths.hpp" -#include -#include -#include - -#if defined(_WIN32) - #include - #include -#else - #include - #include - #include - #include -#endif - -std::filesystem::path get_executable_path() -{ - std::filesystem::path executable_path; - - #if defined(_WIN32) - // Get executable path on Windows - std::wstring path(MAX_PATH, L'\0'); - GetModuleFileNameW(GetModuleHandleW(nullptr), path.data(), MAX_PATH); - path.erase(std::find(path.begin(), path.end(), L'\0'), path.end()); - executable_path = path; - #else - // Get executable path on Linux - char path[PATH_MAX]; - ssize_t length = ::readlink("/proc/self/exe", path, sizeof(path) - 1); - if (length != -1) - { - path[length] = '\0'; - executable_path = path; - } - #endif - - return executable_path; -} - -std::filesystem::path get_executable_data_path() -{ - #if defined(_WIN32) - return get_executable_path().parent_path(); - #else - return get_executable_path().parent_path().parent_path() / "share"; - #endif -} - -std::filesystem::path get_local_config_path() -{ - std::filesystem::path local_config_path; - - #if defined(_WIN32) - - std::wstring path(MAX_PATH, L'\0'); - if (SHGetFolderPathW(nullptr, CSIDL_LOCAL_APPDATA, nullptr, SHGFP_TYPE_CURRENT, path.data()) == S_OK) - { - path.erase(std::find(path.begin(), path.end(), L'\0'), path.end()); - local_config_path = path; - } - - // Windows Vista+ - // wchar_t* path_buffer = nullptr; - // if (SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_DEFAULT, nullptr, &path_buffer) == S_OK) - // { - // local_config_path = std::filesystem::path(path_buffer); - // CoTaskMemFree(static_cast(path_buffer)); - // } - - #else - // Determine home path - std::filesystem::path home_path = getpwuid(getuid())->pw_dir; - - // Determine config path - char* xdg_config_home = std::getenv("XDG_CONFIG_HOME"); - if (!xdg_config_home) - { - // Default to $HOME/.config/ as per: - // https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html#variables - local_config_path = home_path / ".config/"; - } - else - { - local_config_path = xdg_config_home; - } - #endif - - return local_config_path; -} - -std::filesystem::path get_shared_config_path() -{ - #if defined(_WIN32) - std::filesystem::path shared_config_path; - - std::wstring path(MAX_PATH, L'\0'); - if (SHGetFolderPathW(nullptr, CSIDL_APPDATA, nullptr, SHGFP_TYPE_CURRENT, path.data()) == S_OK) - { - path.erase(std::find(path.begin(), path.end(), L'\0'), path.end()); - shared_config_path = path; - } - - // Windows Vista+ - // wchar_t* path_buffer = nullptr; - // if (SHGetKnownFolderPath(FOLDERID_RoamingAppData, KF_FLAG_DEFAULT, nullptr, &path_buffer) == S_OK) - // { - // shared_config_path = path_buffer; - // CoTaskMemFree(static_cast(path_buffer)); - // } - - return shared_config_path; - #else - return get_local_config_path(); - #endif -} diff --git a/src/utility/uuid.cpp b/src/utility/uuid.cpp deleted file mode 100644 index 18bf4fc..0000000 --- a/src/utility/uuid.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2023 Christopher J. Howard - * - * This file is part of Antkeeper source code. - * - * Antkeeper source code is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Antkeeper source code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Antkeeper source code. If not, see . - */ - -#include "utility/uuid.hpp" -#include - -std::string uuid::string() const -{ - static const char* hex = "0123456789abcdef"; - - std::string str(32, '0'); - - char* c = str.data(); - for (std::byte byte: data) - { - *(c++) = hex[static_cast(byte) >> 4]; - *(c++) = hex[static_cast(byte) & 15]; - } - - return str; -} - -std::ostream& operator<<(std::ostream& os, const uuid& id) -{ - os << id.string(); - return os; -}